I am having a crash issue with the VertexBuffer line. If I comment out the line, my program will iterate through this code 49 times. The code is from Riemer's tutorial for XNA 4.0 and does work in that solution. I have checked the 'using' statements and don't see any thing different between the two projects. I can't figure out why the VertexBuffer will crash the program while the IndexBuffer is just fine. Does anyone have any ideas?
public void CopyToTerrainBuffers()
{
for (int x = 0; x < 7; x++)
for (int y = 0; y < 7; y++)
{
if (!gpu_buffer_std[x, y].initialized)
{
VertexBuffer temp = new VertexBuffer( device,
typeof(VertexMultitextured),
tiles_std[x + 1, y + 1].terrainVertices.Length * VertexMultitextured.SizeInBytes,
BufferUsage.WriteOnly);
// size = 3,698,744
gpu_buffer_std[x, y].terrainVertexBuffer = new VertexBuffer(device,
typeof(VertexMultitextured),
tiles_std[x + 1, y + 1].terrainVertices.Length * VertexMultitextured.SizeInBytes,
BufferUsage.WriteOnly);
gpu_buffer_std[x, y].terrainIndexBuffer = new IndexBuffer( device,
typeof(int),
tiles_std[x + 1, y + 1].indices.Length,
BufferUsage.WriteOnly);
gpu_buffer_std[x, y].initialized = true;
}
if (!tiles_std[x + 1, y + 1].toGPU)
{
//gpu_buffer_std[x, y].terrainVertexBuffer.SetData(tiles_std[x + 1, y + 1].terrainVertices);
gpu_buffer_std[x, y].terrainIndexBuffer.SetData(tiles_std[x + 1, y + 1].indices);
gpu_buffer_std[x, y].terrainVertexDeclaration = VertexMultitextured.VertexDeclaration;
tiles_std[x + 1, y + 1].toGPU = true;
}
}
}
The 'temp' was to see if it was the pointer causing the problem. This is the whole function (brute force method). Here is the struct being used.
public struct VertexMultitextured : IVertexType
{
public Vector3 position;
public Vector3 normal;
public Vector4 textureCoordinate;
public Vector4 texWeights;
public static int SizeInBytes = sizeof(float) * (3 + 3 + 4 + 4);
public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration
(
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
new VertexElement(sizeof(float) * 3, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
new VertexElement(sizeof(float) * (3 + 3), VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 0),
new VertexElement(sizeof(float) * (3 + 3 + 4), VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 1)
);
public VertexMultitextured(Vector3 position, Vector3 normal, Vector4 textureCoordinate, Vector4 texWeights)
{
this.position = position;
this.normal = normal;
this.textureCoordinate = textureCoordinate;
this.texWeights = texWeights;
}
public Vector3 Position
{
get { return position; }
set { position = value; }
}
public Vector3 Normal
{
get { return normal; }
set { normal = value; }
}
public Vector4 TextureCoordinate
{
get { return textureCoordinate; }
set { textureCoordinate = value; }
}
public Vector4 TexWeights
{
get { return texWeights; }
set { texWeights = value; }
}
VertexDeclaration IVertexType.VertexDeclaration
{
get { return VertexDeclaration; }
}
}
Update: I have taken the code in question and placed it in Reimers code, and have produced the same results. The debugger shows the following:
System.OutOfMemoryException was unhandled
HResult=-2147024882
Message=Insufficient memory to continue the execution of the program.
Source=Microsoft.Xna.Framework
StackTrace:
at Microsoft.Xna.Framework.Helpers.GetExceptionFromResult(UInt32 result)
at Microsoft.Xna.Framework.Graphics.GraphicsHelpers.GetExceptionFromResult(UInt32 result)
at Microsoft.Xna.Framework.Graphics.VertexBuffer.CreateBuffer(VertexDeclaration vertexDeclaration, UInt32 dwVertexCount, UInt32 usage, _D3DPOOL pool)
at Microsoft.Xna.Framework.Graphics.VertexBuffer..ctor(GraphicsDevice graphicsDevice, Type vertexType, Int32 vertexCount, BufferUsage usage)
at Series3D4.Game1.CopyToTerrainBuffers(VertexMultitextured[] vertices, Int32[] indices) in D:\C#Programs\Series3D4\Series3D4\Series3D4\Game1.cs:line 494
at Series3D4.Game1.LoadVertices() in D:\C#Programs\Series3D4\Series3D4\Series3D4\Game1.cs:line 190
at Series3D4.Game1.LoadContent() in D:\C#Programs\Series3D4\Series3D4\Series3D4\Game1.cs:line 172
at Microsoft.Xna.Framework.Game.Initialize()
at Series3D4.Game1.Initialize() in D:\C#Programs\Series3D4\Series3D4\Series3D4\Game1.cs:line 154
at Microsoft.Xna.Framework.Game.RunGame(Boolean useBlockingRun)
at Microsoft.Xna.Framework.Game.Run()
at Series3D4.Program.Main(String[] args) in D:\C#Programs\Series3D4\Series3D4\Series3D4\Program.cs:line 13
InnerException:
Here is the class being used:
public class DisplayTileChunk
{
// Header info for tile control
public bool beenmoved; // x,z been updated after move
public bool isvisible; // render the tile on screen
public bool loaded; // tile loaded/processed in memory
public bool toGPU; // tile data transfered to GPU
// Terrain info
public int[] texture_index = new int[4];
public VertexMultitextured[] terrainVertices = new VertexMultitextured[4225]; // 65 x 65
public int[] indices = new int[24576]; // 64 x 64 x 6
public VertexBuffer terrainVertexBuffer;
public IndexBuffer terrainIndexBuffer;
public VertexDeclaration terrainVertexDeclaration;
public int noVertices; // = terrainVertexBuffer.VertexCount;
public int noTriangles; // = terrainIndexBuffer.IndexCount / 3;
// Water Info
public VertexBuffer waterVertexBuffer;
public VertexDeclaration waterVertexDeclaration;
// Vegetation Info
public VertexBuffer treeVertexBuffer;
public VertexDeclaration treeVertexDeclaration;
}
And the modified function from Reimers code:
private void CopyToTerrainBuffers(VertexMultitextured[] vertices, int[] indices)
{
terrainVertexBuffer = new VertexBuffer(device, typeof(VertexMultitextured), vertices.Length * VertexMultitextured.SizeInBytes, BufferUsage.WriteOnly);
terrainVertexBuffer.SetData(vertices);
terrainIndexBuffer = new IndexBuffer(device, typeof(int), indices.Length, BufferUsage.WriteOnly);
terrainIndexBuffer.SetData(indices);
for (int x = 0; x < 36; x++)
for (int y = 0; y < 36; y++)
{
gpu_buffer_std[x, y] = new DisplayTileChunk();
VertexBuffer test = new VertexBuffer(device, typeof(VertexMultitextured), 4225 * VertexMultitextured.SizeInBytes, BufferUsage.None);
//gpu_buffer_std[x, y].terrainVertexBuffer = new VertexBuffer(device, typeof(VertexMultitextured), gpu_buffer_std[x, y].terrainVertices.Length * VertexMultitextured.SizeInBytes, BufferUsage.WriteOnly);
//gpu_buffer_std[x, y].terrainIndexBuffer = new IndexBuffer(device, typeof(int), gpu_buffer_std[x, y].indices.Length, BufferUsage.WriteOnly);
}
}
And some interesting numbers from the debugger:
vertices.Length = 16384
terrainVertexBuffer.VertexCount = 917504
terrainVertexBuffer._vertexCount = 917504
terrainVertexBuffer._size = 51380224
test.VertexCount = 236600
test._vertexCount = 236600
test._size = 13249600
Found the problem, original line:
terrainVertexBuffer = new VertexBuffer(device,
typeof(VertexMultitextured),
vertices.Length * VertexMultitextured.SizeInBytes,
BufferUsage.WriteOnly);
Correct line:
terrainVertexBuffer = new VertexBuffer(device,
typeof(VertexMultitextured),
vertices.Length, // error was here
BufferUsage.WriteOnly);
I couldn't see the error for looking at it, took a few days off and 'boom', there it was.
Related
Codes below:
using System;
using UnityEngine;
using UnityEngine.Serialization;
public class Chessboard : MonoBehaviour
{
[Header("Art")]
[SerializeField] private Material tileMaterial;
//Logic
private const int TILE_COUNT_X = 8;
private const int TILE_COUNT_Y = 8;
private GameObject[,] tiles;
private Camera currentCamera;
private Vector2Int currentHover = -Vector2Int.one;
private void Awake()
{
GenerateAllTiles(1, TILE_COUNT_X, TILE_COUNT_Y);
}
private void Update()
{
if (!currentCamera)
{
currentCamera = Camera.current;
return;
}
RaycastHit info;
Ray ray = currentCamera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out info, 100, LayerMask.GetMask("Tile")))
{
// get the indexes of Tile i've hit
Vector2Int hitPosition = LookupTileIndex(info.transform.gameObject);
//If we are hovering a tile after not hovering any
if (currentHover == -Vector2Int.one)
{
tiles[hitPosition.x, hitPosition.y].layer = LayerMask.NameToLayer("Hover");
currentHover = hitPosition;
}
//If we were already hovering a tile, change the previous one
else if (currentHover != hitPosition)
{
tiles[currentHover.x, currentHover.y].layer = LayerMask.NameToLayer("Tile");
currentHover = hitPosition;
tiles[hitPosition.x, hitPosition.y].layer = LayerMask.NameToLayer("Hover");
}
}
else
{
if (currentHover != -Vector2Int.one)
{
tiles[currentHover.x, currentHover.y].layer = LayerMask.NameToLayer("Tile");
currentHover = -Vector2Int.one;
}
}
}
//Generate the board (useful)
private void GenerateAllTiles(float tileSize, int tileCountX, int tileCountY)
{
tiles = new GameObject[tileCountX, tileCountY];
for (int x = 0; x < tileCountX; x++)
for (int y = 0; y < tileCountY; y++)
tiles[x, y] = generate1tile(tileSize, x, y);
}
private GameObject generate1tile(float tileSize, int x, int y)
{
GameObject tileObject = new GameObject(string.Format("Tile X:{0}, Y:{1}", x, y));
tileObject.transform.parent = transform;
Mesh mesh = new Mesh();
tileObject.AddComponent<MeshFilter>().mesh = mesh;
tileObject.AddComponent<MeshRenderer>().material = tileMaterial;
Vector3[] vertices = new Vector3[4];
vertices[0] = new Vector3(x * tileSize, 0, y * tileSize);
vertices[1] = new Vector3(x * tileSize, 0, (y + 1) * tileSize);
vertices[2] = new Vector3((x + 1) * tileSize, 0, y * tileSize);
vertices[3] = new Vector3((x + 1) * tileSize, 0, (y + 1) * tileSize);
int[] tris = new int[]{0, 2, 1, 1, 2, 3};
mesh.vertices = vertices;
mesh.triangles = tris;
mesh.RecalculateNormals();
tileObject.layer = LayerMask.NameToLayer("Tile");
tileObject.AddComponent<BoxCollider>();
return tileObject;
}
private Vector2Int LookupTileIndex(GameObject hitInfo)
{
for(int x = 0; x < TILE_COUNT_X; x++)
for(int y = 0; y < TILE_COUNT_Y; y++)
if(tiles[x, y] == hitInfo)
return new Vector2Int(x, y);
return new Vector2Int(-1, -1); //Invalid
}
}
Error Message:
A game object can only be in one layer. The layer needs to be in the range [0...31]
UnityEngine.StackTraceUtility:ExtractStackTrace ()
Chessboard:generate1tile (single,int,int) (at Assets/scripts/Chessboard.cs:93)
Chessboard:GenerateAllTiles (single,int,int) (at Assets/scripts/Chessboard.cs:68)
Chessboard:Awake () (at Assets/scripts/Chessboard.cs:20)
I just get into Unity, so I might be missed some parts of it.
I tired using AI to config my problem, but it doesn't work. I am expecting i put my mouse on a tile, the tile change its color.
Here is the link to the tutorial I watched: https://www.youtube.com/watch?v=FtGy7J8XD90&list=PLmcbjnHce7SeAUFouc3X9zqXxiPbCz8Zp&index=2&ab_channel=Epitome
I want to generate a cube and subdivide each face of the cube 4 times, in this way a cube of 6 faces will have 24 faces. I'm am trying doing this in Unity3d but its giving me Out of Memory Exception when I try executing it. I think it may be due to some bad efficient code, but I think I'm missing something because I'm trying to see if I can make it more efficient and I don't really see nothing very bad.
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class sphereMesh : MonoBehaviour
{
// Start is called before the first frame update
Mesh mesh;
List<Vector3> vertices = new List<Vector3>();
List<int> triangulos = new List<int>();
public int numeroSubdivisiones = 1;
static Vector3[] faceCubes = {
new Vector3(1, 1, 1), //0
new Vector3(-1, 1, 1), //1
new Vector3(-1, -1, 1), //2
new Vector3(1, -1, 1), //3
new Vector3(-1, 1, -1), //4
new Vector3(1, 1, -1), //5
new Vector3(1, -1, -1), //6
new Vector3(-1, -1, -1) //7
};
static int[][] facetriangulos = {
// Cara trasera
new int[]{0,1,2,3},
// Cara derecha
new int[]{5,0,3,6},
// Cara frontal
new int[]{4,5,6,7},
// Cara izquierda
new int[]{1,4,7,2},
// Cara arriba
new int[]{5,4,1,0},
// Cara abajo
new int[]{3,2,7,6},
};
void MakeFace(int dir)
{
vertices.AddRange(faceVertices(dir));
int vCount = vertices.Count;
triangulos.Add(vCount - 4);
triangulos.Add(vCount - 4 + 1);
triangulos.Add(vCount - 4 + 2);
triangulos.Add(vCount - 4);
triangulos.Add(vCount - 4 + 2);
triangulos.Add(vCount - 4 + 3);
}
void MakeCube()
{
vertices = new List<Vector3>();
triangulos = new List<int>();
for (int i = 0; i <6; i++)
{
MakeFace(i);
}
}
// Funcion que crea una cara de vertices
public static Vector3[] faceVertices(int dir)
{
Vector3[] fv = new Vector3[4];
for (int i = 0; i < fv.Length; i++)
{
// La direccion y la posicion del array
fv[i] = faceCubes[facetriangulos[dir][i]];
}
return fv;
}
// Modifica la lista de vertices y la actualiza
// De forma que un cuadrado da lugar a otros cuatro cuadrados
// T --> Top, D --> Down, L --> Left, R --> Right
// v --> Vertice
void subdividirCuadrado(Vector3 vTL, Vector3 vTR, Vector3 vDL, Vector3 vDR)
{
Vector3 vT = vT = Vector3.Lerp(vTR, vTL, 0.5f);
Vector3 vD = vD = Vector3.Lerp(vDR, vDL, 0.5f);
Vector3 vR = vR = Vector3.Lerp(vTR,vDR, 0.5f);
Vector3 vL = vL = Vector3.Lerp(vTL,vDL, 0.5f);
Vector3 vCenter = Vector3.Lerp(vR, vL, 0.5f);
int size = vertices.Count;
vertices.AddRange(new List<Vector3> {vT, vD, vL, vR, vTL, vTR, vDL, vDR, vCenter});
int tT = size + 0;
int tD = size + 1;
int tL = size + 2;
int tR = size + 3;
int tTL = size + 4;
int tTR = size + 5;
int tDL = size + 6;
int tDR = size + 7;
int tCenter = size + 8;
triangulos.AddRange(new List<int> {tL, tTL, tT});
triangulos.AddRange(new List<int> {tL, tT, tCenter});
triangulos.AddRange(new List<int> {tCenter, tT, tTR});
triangulos.AddRange(new List<int> {tCenter, tTR, tR});
triangulos.AddRange(new List<int> {tDL, tL, tCenter});
triangulos.AddRange(new List<int> {tDL, tCenter, tD});
triangulos.AddRange(new List<int> {tD, tCenter, tR});
triangulos.AddRange(new List<int> {tD, tR, tDR});
}
void subCubo()
{
for(int j = 0; j < numeroSubdivisiones; j++)
{
Debug.Log("AAAAA" + vertices.Count);
for (int i = 0; i < vertices.Count; i += 4)
{
subdividirCuadrado(vertices[i], vertices[i+1], vertices[i+2], vertices[i+3]);
/*
mesh.vertices[i][0] = mesh.vertices[i][0]/i;
mesh.vertices[i][1] = mesh.vertices[i][1]/i;
mesh.vertices[i][2] = mesh.vertices[i][2]/i;
mesh.vertices[i+1][0] = mesh.vertices[i+1][0]/i+1;
mesh.vertices[i+1][1] = mesh.vertices[i+1][1]/i+1;
mesh.vertices[i+1][2] = mesh.vertices[i+1][2]/i+1;
mesh.vertices[i+2][0] = mesh.vertices[i+2][0]/i+2;
mesh.vertices[i+2][1] = mesh.vertices[i+2][1]/i+2;
mesh.vertices[i+2][2] = mesh.vertices[i+2][2]/i+2;
mesh.vertices[i+3][0] = mesh.vertices[i+3][0]/i+3;
mesh.vertices[i+3][1] = mesh.vertices[i+3][1]/i+3;
mesh.vertices[i+3][2] = mesh.vertices[i+3][2]/i+3;
*/
}
}
}
void Awake()
{
mesh = GetComponent<MeshFilter>().mesh;
}
void Start()
{
MakeCube();
subCubo();
UpdateMesh();
}
// Update is called once per frame
void UpdateMesh()
{
mesh.Clear();
mesh.vertices = vertices.ToArray();
mesh.triangles = triangulos.ToArray();
mesh.RecalculateNormals();
}
}
I think this is infinity loop:
for (int i = 0; i < vertices.Count; i += 4)
{
subdividirCuadrado(vertices[i], vertices[i+1], vertices[i+2], vertices[i+3]);
You keep going through the loop until i is bigger than count of items in vertices.
During each iteration i is increased by 4, but there are 9 new items in vertices:
vertices.AddRange(new List<Vector3> {vT, vD, vL, vR, vTL, vTR, vDL, vDR, vCenter});
Therefore i is never bigger than vertices.count and thus the loop is infinite.
vertices.AddRange(new List { vT, vD, vL, vR, vTL, vTR, vDL, vDR, vCenter });
This line inside subdividirCuadrado()
You add 9 NEW vertices to the vertices list but its inside
for (int i = 0; i < vertices.Count; i += 4).
Result is the count keep going up and up and up. To infinity :)
I am trying to create a high level API with sharpdx. It has to be able to draw, but I am stuck on how to make it work with multiple Draw calls at the same time.
This is how I call the class
DirectXFinalD d33d = new DirectXFinalD();
Here I create a new object of the type viereck(rectangle)
Viereck viereck = new Viereck(0, 0, 0.2, 0.1, myBrush, myBrush, 1, false);
Here I pass the object to the class
d33d.DrawDirectX(viereck);
And it already works, but the problem is, I want it to be able that you can pass more objects at any given time, and let them be drawn.
I already tried to always update the vertexbuffer and always += the vertices, but the problem is that different shapes need different topologies. Here is the class:
namespace DrawHost
{
public class DirectXFinalD : DrawHost.DirectXBaseD<D3D11>, IDrawable
{
;
public DirectXFinalD(IDrawable objectToDraw = null, DataStream stream = null)
{
this.objectToDraw = objectToDraw;
if (stream == null)
stream = new DataStream(32 * 612500, true, true);
else
this.stream = stream;
}
protected override void Attach()
{
#region Shader
if (Renderer == null)
return;
device = Renderer.Device;
context = device.ImmediateContext;
// Compile Vertex and Pixel shaders
vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniTri.fx", "VS", "vs_4_0", ShaderFlags.None, EffectFlags.None);
vertexShader = new VertexShader(device, vertexShaderByteCode);
pixelShaderByteCode = ShaderBytecode.CompileFromFile("MiniTri.fx", "PS", "ps_4_0", ShaderFlags.None, EffectFlags.None);
pixelShader = new PixelShader(device, pixelShaderByteCode);
// Layout from VertexShader input signature
layout = new InputLayout(device, ShaderSignature.GetInputSignature(vertexShaderByteCode), new[] {
new InputElement("POSITION",0,Format.R32G32B32A32_Float,0,0),
new InputElement("COLOR",0,Format.R32G32B32A32_Float,16,0)
});
#endregion
if (objectToDraw == null) { }
else
{
float r = 0;
float g = 0;
float b = 0;
switch (objectToDraw.ToString())
{
#region Dreieck
case "Dreieck":
Dreieck dreieck = (Dreieck)objectToDraw;
topology = PrimitiveTopology.TriangleStrip;
ConvertColor(ref r, ref g, ref b, ((System.Windows.Media.SolidColorBrush)dreieck.Color).Color);
streamList.Add(new Vector4((Convert.ToSingle(dreieck.X)), (Convert.ToSingle(dreieck.Y) / 10), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));
streamList.Add(new Vector4(((Convert.ToSingle(dreieck.X) + Convert.ToSingle(dreieck.Width))), -(Convert.ToSingle(dreieck.Y)), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));
streamList.Add(new Vector4(-(Convert.ToSingle(dreieck.X)), -((Convert.ToSingle(dreieck.Y) + Convert.ToSingle(dreieck.Height) )), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));
break;
#endregion
#region Viereck
case "Viereck":
Viereck viereck = (Viereck)objectToDraw;
topology = PrimitiveTopology.TriangleStrip;
ConvertColor(ref r, ref g, ref b, ((System.Windows.Media.SolidColorBrush)viereck.Color).Color);
streamList.Add(new Vector4((Convert.ToSingle(viereck.X)), (Convert.ToSingle(viereck.Y)), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));// ok
streamList.Add(new Vector4(((Convert.ToSingle(viereck.X))), (Convert.ToSingle(viereck.Y) + Convert.ToSingle(viereck.Height)), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));// ok
streamList.Add(new Vector4((Convert.ToSingle(viereck.X) + Convert.ToSingle(viereck.Width)), (Convert.ToSingle(viereck.Y) ), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));// ok
streamList.Add(new Vector4((Convert.ToSingle(viereck.X) + Convert.ToSingle(viereck.Width)), ((Convert.ToSingle(viereck.Y) + Convert.ToSingle(viereck.Height))), 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));// ok
break;
#endregion
#region Kreis
case "Kreis":
topology = PrimitiveTopology.Undefined;
Kreis kreis = (Kreis)objectToDraw;
ConvertColor(ref r, ref g, ref b, ((System.Windows.Media.SolidColorBrush)kreis.Color).Color);
for (float j = 0; j <= 360; j++)
{
for (double i = 0; i <= 360; i++) //254
{
double rad = i * (Math.PI / 180);
float x = (float)Math.Cos(rad) * ((float)kreis.Width / 2);
float y = (float)Math.Sin(rad) * ((float)kreis.Height / 2);
streamList.Add(new Vector4(x , y, 0f, 1.0f)); streamList.Add(new Vector4(r, g, b, 1.0f));
}
}
break;
#endregion
};
foreach (Vector4 a in streamList)
{
stream.WriteRange(new[] { a });
}
stream.Position = 0;
streamGV streamGV = new streamGV(stream);
//streamGV.GetList(streamList);
//streamList = null;
GC.Collect();
}
vertices = new Buffer(device, stream, new BufferDescription()
{
BindFlags = BindFlags.VertexBuffer,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None,
SizeInBytes = (int)stream.Length,
Usage = ResourceUsage.Default,
StructureByteStride = 0,
});
stream.Dispose();
// Prepare All the stages
context.InputAssembler.InputLayout = (layout);
context.InputAssembler.PrimitiveTopology = topology;
context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertices, 32, 0));
context.VertexShader.Set(vertexShader);
context.PixelShader.Set(pixelShader);
}
public override void RenderScene(DrawEventArgs args)
{
Renderer.Device.ImmediateContext.ClearRenderTargetView(Renderer.RenderTargetView, new Color4(0.6f, 0, 0, 0));
Renderer.Device.ImmediateContext.Draw((int)stream.Length, 0);
return;
}
public override void Case(DXElement dxviewer)
{
dxviewer11 = dxviewer;
}
public override void DrawDirectX(IDrawable objectToDraw)
{
this.objectToDraw = objectToDraw;
//dxviewer11.Renderer = new Scene_11();
//Renderer = new D3D11();
stream = new DataStream(32 * 612500, true, true);
streamGV strean = new streamGV();
dxviewer11.Renderer = new DirectXFinalD(objectToDraw, stream) { Renderer = new D3D11() };
}
private void ConvertColor(ref float r, ref float g, ref float b, System.Windows.Media.Color color)
{
r = (float)(color.R * 255);
g = (float)(color.G * 255);
b = (float)(color.B * 255);
}
}
How can I make it possible to draw all of them at the same time? I am using sharpdx as my renderform. One problem is that I always have to change the topology, for example triangle needs trianglelistbut for the circle I use Linestrip. Any help would be appreciated
As I can see you're calling the Attach ones. In this attach method you're only creating 1 vertex buffer, with a mesh depending on objectToDraw.
You should decouple you shader compilation code and your vertexbuffer setup.
You could create a class that will manage the vertexbuffer and 'knows' how to draw the mesh.
For example: (PSEUDO)
[StructLayout(LayoutKind.Sequential)]
public struct Vertex
{
public const int Stride = 16 + 16;
public Vector4 Pos;
public Color4 Color;
}
public class Mesh
{
private Vertex[] _vertices;
private int[] _indices;
private SharpDX.Direct3D11.Buffer _indexBuffer;
private SharpDX.Direct3D11.Buffer _vertexBuffer;
private VertexBufferBinding _vertexBufferBinding;
public Mesh(Vertex[] vertices, int[] indices)
{
// save the vertices in a field
_vertices = value;
var vbd = new BufferDescription(
SharpDX.Utilities.SizeOf<Vertex>() * _vertices.Length,
ResourceUsage.Immutable,
BindFlags.VertexBuffer,
CpuAccessFlags.None,
ResourceOptionFlags.None,
0);
// setup the vertex buffer
_vertexBuffer = SharpDX.Direct3D11.Buffer.Create<Vertex>(DX11.Device, _vertices, vbd);
// create the binding
_vertexBufferBinding = new VertexBufferBinding(_vertexBuffer, Vertex.Stride, 0);
_indices = value;
var ibd = new BufferDescription(
sizeof(int) * _indices.Length,
ResourceUsage.Immutable,
BindFlags.IndexBuffer,
CpuAccessFlags.None,
ResourceOptionFlags.None,
0);
// setup the index buffer
_indexBuffer = SharpDX.Direct3D11.Buffer.Create<int>(DX11.Device, _indices, ibd);
}
// the SelectBuffers will select the right vertex buffer.
// this could be combined with the Draw method, but I rather not
// You should call this ones even when you draw multiple the same mesh.
public void SelectBuffers()
{
DX11.Device.ImmediateContext.InputAssembler.SetVertexBuffers(0, _vertexBufferBinding);
DX11.Device.ImmediateContext.InputAssembler.SetIndexBuffer(_indexBuffer, SharpDX.DXGI.Format.R32_UInt, 0);
}
public void Draw()
{
DX11.Device.ImmediateContext.DrawIndexed(_indices.Length, 0, 0);
}
}
List<Mesh> _meshes = new List<Mesh>();
public void SetupShaders()
{
vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniTri.fx", "VS", "vs_4_0", ShaderFlags.None, EffectFlags.None);
vertexShader = new VertexShader(device, vertexShaderByteCode);
pixelShaderByteCode = ShaderBytecode.CompileFromFile("MiniTri.fx", "PS", "ps_4_0", ShaderFlags.None, EffectFlags.None);
...... etc
}
public Mesh SetupMesh(object objectToDraw)
{
switch(.....)
{
// .. implement your beautiful switch ;-)
}
return new Mesh(vertices, indices);
}
public void Init()
{
SetupShaders();
_meshes.Add(SetupMesh(new Dreieck(.....)));
_meshes.Add(SetupMesh(new Viereck(.....)));
_meshes.Add(SetupMesh(new Kreis(.....)));
}
public override void RenderScene(DrawEventArgs args)
{
Renderer.Device.ImmediateContext.ClearRenderTargetView(Renderer.RenderTargetView, new Color4(0.6f, 0, 0, 0));
foreach(var mesh in _meshes)
{
mesh.SelectBuffers();
mesh.Draw();
}
return;
}
Something like that...
I am currently learning OpenGL and I am trying to write simple solar system application similar to the one in tutorial, yet for some reason camera behavior is really weird and I am not sure what is causing this. I want my camera to look at the sun, but all I am getting are some really weird angles of planets or nothing at all, it might not even be pure camera problem. Can somebody tell me what exactly I am doing wrong? I would appreciate some code. If someone is willing to help and prefer to check application instead of reading code here, link is added below.
The camera here will be as close to the tutorial as possible (FPS), but I got also dragging/scrolling system instead of this.
public class Camera
{
private static float eyeX, eyeY, eyeZ;
private static float centerX, centerY, centerZ;
private const float movingSpeed = 0.3f;
private const float rotationSpeed = 0.25f;
private static double i, j, k;
public static float Height { get; set; }
public static float Slope { get; set; }
public void InitCamera()
{
eyeX = 0f;
eyeY = 15f;
eyeZ = 25f;
centerX = 0;
centerY = 2;
centerZ = 0;
Look();
}
public void Look()
{
Gl.MatrixMode(OpenGL.GL_MODELVIEW);
Gl.LoadIdentity();
Gl.LookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, 0, 1, 0);
}
public void UpdateDirVector()
{
i = -Math.Sin(((double)Slope).ToRadians());
j = Math.Sin(((double)Height).ToRadians());
k = Math.Cos(((double)Slope).ToRadians());
centerX = eyeX - (float)i;
centerY = eyeY - (float)j;
centerZ = eyeZ - (float)k;
}
public static void CenterMouse()
{
if (GlCenter == null)
return;
var pos = (Point) GlCenter;
WinApi.SetCursorPos((int)Math.Round(pos.X), (int)Math.Round(pos.Y));
}
public void Update(int pressedButton)
{
if (GlCenter == null)
return;
var pos = (Point)GlCenter;
var halfHeight = GlHeight / 2;
var halfWidth = GlWidth / 2;
var position = new Pointer();
WinApi.GetCursorPos(ref position);
var diffX = (float)pos.X - position.x;
var diffY = (float)pos.Y - position.y;
if (position.y < halfHeight)
Height -= rotationSpeed * diffY;
else if (position.y > halfHeight)
Height += rotationSpeed * -diffY;
if (position.x < halfWidth)
Slope += rotationSpeed * -diffX;
else if (position.x > halfWidth)
Slope -= rotationSpeed * diffX;
UpdateDirVector();
CenterMouse();
if (pressedButton == 1) // LPM
{
eyeX -= (float)i * movingSpeed;
eyeY -= (float)j * movingSpeed;
eyeZ -= (float)k * movingSpeed;
}
else if (pressedButton == -1) // PPM
{
eyeX += (float)i * movingSpeed;
eyeY += (float)j * movingSpeed;
eyeZ += (float)k * movingSpeed;
}
Look();
}
}
Planet.cs:
public class Planet
{
private readonly PlanetTypes _planetType;
private readonly Position _position;
private float _orbitAngle;
private readonly float _sizeRadius;
private readonly float _velocity;
private readonly string _texturePath;
private uint _list;
private float _rotationAngle;
public Planet(float radius, PlanetTypes planetType, Position position, string texturePath, bool hasMoon)
{
_sizeRadius = radius;
_planetType = planetType;
_position = position;
_orbitAngle = Rng.Next(360);
_velocity = (float)Rng.NextDouble() * 0.3f;
_texturePath = texturePath;
}
public void Create()
{
var quadric = Gl.NewQuadric();
Gl.QuadricNormals(quadric, OpenGL.GLU_SMOOTH);
Gl.QuadricTexture(quadric, (int) OpenGL.GL_TRUE);
_list = Gl.GenLists(1);
Gl.NewList(_list, OpenGL.GL_COMPILE);
Gl.PushMatrix();
Gl.Rotate(270, 1, 0, 0);
Gl.Sphere(quadric, _sizeRadius, 32, 32);
Gl.PopMatrix();
Gl.EndList();
}
public void DrawOrbit()
{
Gl.Begin(OpenGL.GL_LINE_STRIP);
for (var i = 0; i <= 360; i++)
Gl.Vertex(_position.X * (float)Math.Sin(i * Math.PI / 180), 0, _position.X * (float)Math.Cos(i * Math.PI / 180));
Gl.End();
}
public void Draw()
{
DrawOrbit();
LoadTexture($"{_texturesPath}{_texturePath}");
Gl.PushMatrix();
_orbitAngle += _velocity;
_rotationAngle += 0.6f;
Gl.Rotate(_orbitAngle, 0, 1, 0);
Gl.Translate(-_position.X, -_position.Y, -_position.Z);
Gl.Rotate(_rotationAngle, 0, 1, 0);
Gl.CallList(_list);
Gl.PopMatrix();
}
}
Sun.cs
public class Sun
{
private uint _list;
private float _rotation;
private readonly string _texturePath;
public Sun(string texturePath)
{
_texturePath = texturePath;
}
public void Create()
{
var quadratic = Gl.NewQuadric();
Gl.QuadricNormals(quadratic, OpenGL.GLU_SMOOTH);
Gl.QuadricTexture(quadratic, (int)OpenGL.GL_TRUE);
_list = Gl.GenLists(1);
Gl.NewList(_list, OpenGL.GL_COMPILE);
Gl.PushMatrix();
Gl.Rotate(90, 1, 0, 0);
Gl.Sphere(quadratic, 3, 32, 32);
Gl.PopMatrix();
Gl.EndList();
}
public void Draw()
{
LoadTexture($"{_texturesPath}{_texturePath}");
Gl.PushMatrix();
_rotation += 0.05f;
Gl.Rotate(_rotation, 0, 1, 0);
Gl.CallList(_list);
Gl.PopMatrix();
}
}
Stars.cs
public class Stars
{
private readonly List<Position> starPositions = new List<Position>();
public void CreateStars(int amount)
{
var count = 0;
while (count <= amount)
{
var p = default(Position);
p.X = Rng.Next(110) * (float)Math.Pow(-1, Rng.Next());
p.Z = Rng.Next(110) * (float)Math.Pow(-1, Rng.Next());
p.Y = Rng.Next(110) * (float)Math.Pow(-1, Rng.Next());
if (!(Math.Pow(Math.Pow(p.X, 2) + Math.Pow(p.Y, 2) + Math.Pow(p.Z, 2), 1 / 3f) > 15))
continue;
starPositions.Add(p);
count++;
}
}
public void Draw()
{
Gl.Begin(OpenGL.GL_POINTS);
Gl.Color(1, 1, 1);
Gl.PointSize(3);
foreach (var starPos in starPositions)
Gl.Vertex(starPos.X, starPos.Y, starPos.Z);
Gl.End();
}
}
SolarSystem.cs (basically a collection of everything above, enums aren't necessary, I left them cause they might be useful in the future)
public class SolarSystem
{
public static Random Rng { get; } = new Random();
private readonly Stars _stars;
private readonly Sun _sun;
private readonly List<Planet> _planets;
public SolarSystem()
{
Camera = new Camera();
_stars = new Stars();
_sun = new Sun("sun.bmp");
_planets = new List<Planet>();
}
public void CreateScene()
{
_planets.Add(new Planet(0.5f, PlanetTypes.Mercury, new Position(5, 0, 0), "mercury.bmp", false)); // tylko tutaj pliki, wszedzie indziej przeksztaĆcone na .bmp
_planets.Add(new Planet(0.7f, PlanetTypes.Venus, new Position(11, 0, 0), "venus.bmp", false));
_planets.Add(new Planet(1, PlanetTypes.Earth, new Position(15, 0, 0), "earth.bmp", true));
_planets.Add(new Planet(1, PlanetTypes.Mars, new Position(22, 0, 0), "mars.bmp", false));
_planets.Add(new Planet(1.5f, PlanetTypes.Jupiter, new Position(28, 0, 0), "jupiter.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Saturn, new Position(35, 0, 0), "saturn.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Uranus, new Position(41, 0, 0), "uranus.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Neptune, new Position(51, 0, 0), "neptune.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Pluto, new Position(60, 0, 0), "pluto.bmp", false));
_stars.CreateStars(500);
_sun.Create();
foreach (var planet in _planets)
planet.Create();
}
public Camera Camera { get; }
public void DrawScene()
{
_stars.Draw();
_sun.Draw();
foreach (var planet in _planets)
planet.Draw();
}
public enum PlanetTypes
{
Mercury,
Venus,
Earth,
Mars,
Jupiter,
Saturn,
Neptune,
Uranus,
Pluto
}
}
API (including pointer position translation, because only this way I was able to center cursor, it will be removed with dragging camera system)):
public static class WinApi
{
[DllImport("GDI32.dll")]
public static extern void SwapBuffers(uint hdc);
[DllImport("user32.dll")]
public static extern void SetCursorPos(int x, int y);
[DllImport("user32.dll")]
public static extern void GetCursorPos(ref Pointer point);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int X;
public int Y;
}
[DllImport("User32", EntryPoint = "ClientToScreen", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ClientToScreen(
IntPtr hWnd,
ref POINT pt);
[EnvironmentPermission(SecurityAction.LinkDemand, Unrestricted = true)]
public static Point? TransformToScreen(
Point point,
Visual relativeTo)
{
var hwndSource = PresentationSource.FromVisual(relativeTo) as HwndSource;
if (hwndSource == null)
return null;
var root = hwndSource.RootVisual;
// Transform the point from the root to client coordinates.
var transformToRoot = relativeTo.TransformToAncestor(root);
var pointRoot = transformToRoot.Transform(point);
var m = Matrix.Identity;
var transform = VisualTreeHelper.GetTransform(root);
if (transform != null)
{
m = Matrix.Multiply(m, transform.Value);
}
var offset = VisualTreeHelper.GetOffset(root);
m.Translate(offset.X, offset.Y);
var pointClient = m.Transform(pointRoot);
pointClient = hwndSource.CompositionTarget.TransformToDevice.Transform(pointClient);
var pointClientPixels = new POINT();
pointClientPixels.X = (0 < pointClient.X)
? (int)(pointClient.X + 0.5)
: (int)(pointClient.X - 0.5);
pointClientPixels.Y = (0 < pointClient.Y)
? (int)(pointClient.Y + 0.5)
: (int)(pointClient.Y - 0.5);
var pointScreenPixels = pointClientPixels;
if (ClientToScreen(
hwndSource.Handle,
ref pointScreenPixels))
{
return new Point(
pointScreenPixels.X,
pointScreenPixels.Y);
}
return new Point();
}
}
Extensions.cs
public static class Extensions
{
public static double ToDegrees(this double radians)
{
return radians * (180.0 / Math.PI);
}
public static double ToRadians(this double degrees)
{
return Math.PI * degrees / 180.0;
}
}
MainWindow.cs
public partial class MainWindow
{
private DispatcherTimer _dispatcherTimer;
private uint _hdc;
public static string _texturesPath = #"Data\Textures\";
private SolarSystem _solarSystem;
private int _movement;
public static OpenGL Gl { get; private set; }
public static Point? GlCenter { get; private set; }
public static int GlHeight { get; private set; }
public static int GlWidth { get; private set; }
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
ContentRendered += MainWindow_ContentRendered;
}
private void MainWindow_ContentRendered(object sender, EventArgs e)
{
Gl = openGLControl.OpenGL;
var source = (HwndSource)PresentationSource.FromVisual(openGLControl);
var hWnd = source?.Handle;
if (hWnd != null) _hdc = (uint)hWnd;
_solarSystem = new SolarSystem();
_solarSystem.Camera.InitCamera();
float[] materialAmbient = { 0.5f, 0.5f, 0.5f, 1.0f };
float[] materialDiffuse = { 1f, 1f, 1f, 1.0f };
float[] materialShininess = { 10.0f };
float[] lightPosition = { 0f, 0f, 0f, 1.0f };
float[] lightAmbient = { 0.85f, 0.85f, 0.85f, 0.0f };
Gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, lightAmbient);
Gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPosition);
Gl.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_SHININESS, materialShininess);
Gl.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_DIFFUSE, materialDiffuse);
Gl.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_AMBIENT, materialAmbient);
Gl.Enable(OpenGL.GL_LIGHTING);
Gl.Enable(OpenGL.GL_LIGHT0);
Gl.Enable(OpenGL.GL_DEPTH_TEST);
_solarSystem.CreateScene();
Gl.ClearColor(0, 0, 0, 1);
GlCenter = WinApi.TransformToScreen(new Point(openGLControl.Width / 2, openGLControl.Height / 2), openGLControl);
GlHeight = (int)openGLControl.Height;
GlWidth = (int)openGLControl.Width;
Camera.CenterMouse();
_dispatcherTimer = new DispatcherTimer();
_dispatcherTimer.Tick += DispatcherTimer_Tick;
_dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1);
_dispatcherTimer.Start();
}
private void DispatcherTimer_Tick(object sender, EventArgs e)
{
Gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
_solarSystem.Camera.Update(_movement);
_solarSystem.DrawScene();
WinApi.SwapBuffers(_hdc);
Gl.Flush();
}
private void OpenGLControl_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
_movement = 1;
else
_movement = -1;
}
private void OpenGLControl_MouseUp(object sender, MouseButtonEventArgs e)
{
_movement = 0;
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.C)
{
GlCenter = WinApi.TransformToScreen(new Point(openGLControl.Width / 2, openGLControl.Height / 2), openGLControl); // new Point((int)(Left), (int)(Top));
}
}
public static uint LoadTexture(string filename)
{
if (string.IsNullOrEmpty(filename))
throw new ArgumentException(filename);
Gl.Enable(OpenGL.GL_TEXTURE_2D);
var texture = new uint[1];
var id = texture[0];
Gl.GenTextures(1, texture);
Gl.BindTexture(OpenGL.GL_TEXTURE_2D, id);
var bmp = new Bitmap(filename);
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
Gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, 3, bmpData.Width, bmpData.Height, 0, OpenGL.GL_BGR, OpenGL.GL_UNSIGNED_BYTE, bmpData.Scan0);
Gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_LINEAR);
Gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, OpenGL.GL_LINEAR);
bmp.UnlockBits(bmpData);
return id;
}
}
Link to whole Application:
https://www.dropbox.com/sh/uhfyeayxn8l7q9y/AAA8tFda5-ZLAjTUzJcwKUm6a?dl=0
UPDATE 1:
I have changed Look() function to:
public void Look()
{
Gl.MatrixMode(OpenGL.GL_PROJECTION);
Gl.LoadIdentity();
Gl.Viewport(0, 0, GlWidth, GlHeight);
Gl.Perspective(45.0f, GlWidth / (double) GlHeight, 1, 200.0);
Gl.LookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, 0, 1, 0);
Gl.MatrixMode(OpenGL.GL_MODELVIEW);
}
And now it works.
Now I can see that my application is loading incorrect textures for some reason. I guess thats because method LoadTextures() I wrote, is using Gl.GenTextures(1, texture). (There is no Gen(Single)Texture method in sharpgl or I am getting it all wrong).
UPDATE 2:
So basically most of my textures doesn't work because they are not power of two, but from what I have read, they don't have to be anymore. SO my current question is: How to force sharpGL to display NPOT textures?
UPDATE 3:
Turns out I can load them like this, but well, they are upside down :).
_texture = new Texture();
...
Gl.Enable(OpenGL.GL_TEXTURE_2D);
_texture.Create(Gl, $"{_texturesPath}{_texturePath}");
_texture.Bind(Gl);
UPDATE 4:
I can flip texture to display it properly, but question is why this is happening?
Gl.Enable(OpenGL.GL_TEXTURE_2D);
var bmp = new Bitmap($"{_texturesPath}{_texturePath}");
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
_texture.Create(Gl, bmp);
_texture.Bind(Gl);
UPDATE 5:
While question from Update 4 still stands, I got last question: How can I recalculate Camera so it is not limited to look up / down only to -90/90 degree?
You don't appear to be setting up your viewing frustrum correctly, and are using it in an uninitialized state. You have code for it, but it is commented out in MainWindow.xaml.cs:123.
You must setup the frustrum. At least once before drawing. It can be either perspective or orthographic.
gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.LoadIdentity();
gl.Perspective(60.0f, (double)Width / (double)Height, 0.01, 100.0);
I'm writing a small graph writing program as a personal project.
Each point is supposed to get drawn on a wpf canvas but I'm having trouble translating the points from the graph's coordinate system, example: x = -8 to 4 y=-4 to 4, to the canvas' coordinate system example: x = 600 to 0 y = 400 to 0.
I'm using the method outlined here to precompute the transformation equations.
However I'm having trouble with the result of the multiplication v=M^-1*u.
My expected result is:
[0.2 ]
[0.0 ]
[-8.0]
[4.0 ]
But the result I'm getting is:
[4.0 ]
[-4.0 ]
[0.01 ]
[-0.006]
I've verified that my transformation matrix is correct and when I do the calculation by hand I'm getting the expected result.
The method to calculate the transformation equations:
private void CalculateTransformationFunctions()
{
// Define the transformation matrix
var transformationMatrix = new Matrix4x4
{
M11 = _destArea.XMin,
M12 = _destArea.YMin,
M13 = 1,
M14 = 0,
M21 = -_destArea.YMin,
M22 = _destArea.XMin,
M23 = 0,
M24 = 1,
M31 = _destArea.XMax,
M32 = _destArea.YMax,
M33 = 1,
M34 = 0,
M41 = -_destArea.YMax,
M42 = _destArea.XMax,
M43 = 0,
M44 = 1
};
// Define the source vector
var srcVector = new Vector4
{
X = _srcArea.XMin,
Y = _srcArea.YMax,
Z = _srcArea.XMax,
W = _srcArea.YMin
};
// Invert the transformationmatrix before the multiplication
Matrix4x4 invertedTransformationMatrix;
if(!Matrix4x4.Invert(transformationMatrix,out invertedTransformationMatrix))
throw new Exception();
// Returns the wrong value
var transformResult = Vector4.Transform(srcVector, invertedTransformationMatrix);
float a = transformResult.X,
b = transformResult.Y,
c = transformResult.Z,
d = transformResult.W;
_xTransformationFunction = (x, y) => (a*x + b*y - b*d - a*c)/(a*a + b*b);
_yTransformationFunction = (x, y) => (b*x - a*y - b*c + a*d)/(a*a + b*b);
}
Which is called in the constructor of its parent class.
My question:
Am I misunderstanding what Vector4.Transform() does here? Or am I completely blind and missing something very obvious?
Full source of the class:
using System;
using System.Numerics;
using System.Windows;
using System.Windows.Media;
using Grapher.Control.Grapher;
namespace Grapher.GraphingMath
{
public class Translator
{
private GraphingArea _srcArea;
private GraphingArea _destArea;
public GraphingArea SourceArea
{
get
{
return _srcArea;
}
set
{
_srcArea = value;
CalculateTransformationFunctions();
}
}
public GraphingArea DestinationArea
{
get { return _destArea; }
set
{
_destArea = value;
CalculateTransformationFunctions();
}
}
private Func<double, double, double> _xTransformationFunction;
private Func<double, double, double> _yTransformationFunction;
public Translator(GraphingArea sourceArea, GraphingArea destArea)
{
_destArea = destArea;
_srcArea = sourceArea;
CalculateTransformationFunctions();
}
public Point TranslatePoint(Point point)
{
var x = point.X;
var y = point.Y;
return new Point
{
X = _xTransformationFunction(x, y),
Y = _yTransformationFunction(x, y)
};
}
/*
x1 y1 1 0
-y1 x1 0 1
M= x2 y2 1 0
-y2 x2 0 1
x1,y1 = dest_min
x2,y2 = dest_max
*/
private void CalculateTransformationFunctions()
{
// Define the transformation matrix
var transformationMatrix = new Matrix4x4
{
M11 = _destArea.XMin,
M12 = _destArea.YMin,
M13 = 1,
M14 = 0,
M21 = -_destArea.YMin,
M22 = _destArea.XMin,
M23 = 0,
M24 = 1,
M31 = _destArea.XMax,
M32 = _destArea.YMax,
M33 = 1,
M34 = 0,
M41 = -_destArea.YMax,
M42 = _destArea.XMax,
M43 = 0,
M44 = 1
};
// Define the source vector
var srcVector = new Vector4
{
X = _srcArea.XMin,
Y = _srcArea.YMax,
Z = _srcArea.XMax,
W = _srcArea.YMin
};
// Invert the transformationmatrix before the multiplication
Matrix4x4 invertedTransformationMatrix;
if(!Matrix4x4.Invert(transformationMatrix,out invertedTransformationMatrix))
throw new Exception();
// Returns the wrong value
var transformResult = Vector4.Transform(srcVector, invertedTransformationMatrix);
float a = transformResult.X,
b = transformResult.Y,
c = transformResult.Z,
d = transformResult.W;
_xTransformationFunction = (x, y) => (a*x + b*y - b*d - a*c)/(a*a + b*b);
_yTransformationFunction = (x, y) => (b*x - a*y - b*c + a*d)/(a*a + b*b);
}
}
}
And for the graphing area struct:
using System;
namespace Grapher.Control.Grapher
{
public struct GraphingArea
{
public float XMin { get; set; }
public float YMin { get; set; }
public float XMax { get; set; }
public float YMax { get; set; }
public float Width => Math.Abs(XMax - XMin);
public float Height => Math.Abs(YMax - YMin);
}
}
In my main method I call the Translator class like this:
Point testPoint = new Point {X = 0, Y = 0};
var srcArea = new GraphingArea
{
XMax = 4,
XMin = -8,
YMax = 4,
YMin = -4
};
var destArea = new GraphingArea
{
XMax = 600,
XMin = 0,
YMax = 400,
YMin = 0
};
var translator = new Translator(srcArea, destArea);
var translatedPoint = translator.TranslatePoint(testPoint);
Edit
Ended up just writing my own matrix multiplication method. I must be misunderstanding what Vector4.Transform() does...
Code here, for anyone interested:
using System.Numerics;
namespace Grapher.GraphingMath.MatrixAndVectorMath
{
public static class Matrix4x4Multiply
{
public static Vector4 Vector4Multiply(Matrix4x4 matrix, Vector4 vector)
{
var mat = new float[4, 4]
{
{matrix.M11, matrix.M12, matrix.M13, matrix.M14},
{matrix.M21, matrix.M22, matrix.M23, matrix.M24},
{matrix.M31, matrix.M32, matrix.M33, matrix.M34},
{matrix.M41, matrix.M42, matrix.M43, matrix.M44}
}; // We'll just wrap the matrix in a float so we can index it.
var vec = new float[4] {vector.X, vector.Y, vector.Z, vector.W}; // And the same with the vector
var result = new float[4] {0, 0, 0, 0};
for (var row = 0; row < mat.GetLength(0); row++)
{
for (var col = 0; col < mat.GetLength(1); col++)
{
result[row] += mat[row, col]*vec[col];
}
}
return new Vector4
{
X = result[0],
Y = result[1],
Z = result[2],
W = result[3]
};
}
}
}
I don't know if this will help but I do something along those lines in one of my projects. I don't use matrices though so it may not be what you are looking for. I simply store the extents of coordinates for the graph and the container (canvas) width and height. I then provide two extensions functions:
public static System.Windows.Point ConvertToScreen(this System.Windows.Point point, CartesianExtents2D extents, double containerWidth, double containerHeight)
{
var x = (point.X - extents.XMinimum) * containerWidth / (extents.XMaximum - extents.XMinimum);
var y = (extents.YMaximum - point.Y) * containerHeight / (extents.YMaximum - extents.YMinimum);
return new System.Windows.Point(x, y);
}
public static System.Windows.Point ConvertToReal(this System.Windows.Point point, CartesianExtents2D extents, double containerWidth, double containerHeight, )
{
var x = extents.XMinimum + (point.X * (extents.XMaximum - extents.XMinimum)) / containerWidth;
var y = extents.YMaximum - (point.Y * (extents.YMaximum - extents.YMinimum)) / containerHeight;
return new System.Windows.Point(x, y);
}
Call thus:
Point p = new Point();
p.ConvertToReal(...);
I'm hoping the contents of CartesianExtents2D is obvious - just min and max for x and y