public void NoTransformTest() { Float3 p = new Float3(1, 3, 7); Float4x4 M = Float4x4.identity; Assert.AreEqual(p, M.transformPoint(p)); }
private Float4x4 GetTransform() { Float4x4 transform = Float4x4.Identity; //Get scale based on asset meta-data XmlElement unitElement = colladaElement.GetChild("asset")?.GetChild("unit"); if (unitElement != null) { string meter = unitElement.Tag.GetAttributeValue("meter"); if (!string.IsNullOrEmpty(meter)) { float meterScale = float.Parse( meter.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture); transform *= Float4x4.CreateScale(meterScale); } } //Get rotation based on asset meta-data XmlElement axisElement = colladaElement.GetChild("asset")?.GetChild("up_axis"); if (axisElement != null && axisElement.FirstData != null) { long startPos = axisElement.FirstData.Value.StartBytePosition; long endPos = axisElement.FirstData.Value.EndBytePosition; par.Seek(startPos); string val = par.ConsumeUntil(() => par.CurrentBytePosition >= endPos); if (val == "Z_UP") { transform *= Float4x4.CreateRotationFromXAngle(-90f * FloatUtils.DEG_TO_RAD); } } return(transform); }
public void MultiplicationTest() { Float4x4 M = Float4x4.identity; Float4x4 B = M * 3; Float4x4 C = B * (1f / 3f); Assert.AreEqual(M, C); }
internal void PreDraw(int swapchainIndex, Float3 sunDirection, float shadowDistance) { //Get the 'normal' scene projections for current camera and aspect CameraData sceneCameraData = CameraData.FromCamera(scene.Camera, swapchainAspect); //Rotation from world to 'sun' direction Float4x4 rotationMatrix = Float4x4.CreateRotationFromAxis(sunDirection, Float3.Forward); //Calculate a sphere in 'lightspace' that fits the frustum of the camera //using a sphere so that the size stays constant when the camera rotates, this avoids //the shimmering when the shadow map is resized all the time FloatSphere shadowSphere = GetShadowSphere( sceneCameraData.InverseViewProjectionMatrix, rotationMatrix.Invert(), shadowDistance); //Derive the projection values from the sphere Float3 shadowCenter = shadowSphere.Center; float radius = shadowSphere.Radius.Round(); float shadowSize = radius * 2f; float shadowNearClip = -radius; float shadowFarClip = radius; //Calculate the matrices for the shadow projection Float4x4 cameraMatrix = rotationMatrix * Float4x4.CreateTranslation(shadowCenter); Float4x4 viewMatrix = cameraMatrix.Invert(); Float4x4 projectionMatrix = Float4x4.CreateOrthographicProjection( shadowSize.XX(), shadowNearClip, shadowFarClip); Float4x4 viewProjectionMatrix = projectionMatrix * viewMatrix; //Calculate a rounding matrix to fix shadow 'shimmering' as objects constantly 'switch' //between pixels in the shadowmap float targetHalfSize = targetSize / 2f; Float2 shadowOrigin = viewProjectionMatrix.TransformPoint((0f, 0f, 0f)).XY *targetHalfSize; Float2 rounding = (shadowOrigin.Round() - shadowOrigin) / targetHalfSize; Float4x4 roundingMat = Float4x4.CreateTranslation(rounding.XY0); //Apply rounding projectionMatrix = roundingMat * projectionMatrix; viewProjectionMatrix = roundingMat * viewProjectionMatrix; //Update shadow projection data in the buffer cameraBuffer.Write(new CameraData( cameraMatrix, viewMatrix, projectionMatrix, viewProjectionMatrix, viewProjectionMatrix.Invert(), shadowNearClip, shadowFarClip), offset: CameraData.SIZE * swapchainIndex); }
public void AddTranslationToExistingMatrixTests() { Float3 p = new Float3(0, 0, 1); Float3 t = new Float3(1, 3, 323); Float4x4 X = Float4x4.identity; X.setTranslation(t); Float4x4 minusX = Float4x4.identity; minusX.setTranslation(t.getOpposite()); Assert.AreEqual(p + t, X.transformPoint(p)); Assert.AreEqual(p, minusX.transformPoint(X.transformPoint(p))); }
public void SimpleTranslationTest() { Float3 p = new Float3(1, 3, 7); Float3 t = new Float3(1, 2, 3); Float4x4 M = new Float4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, t.z, 1); Float4x4 M2 = Float4x4.getTranslationMatrix(t); Assert.AreEqual(M, M2); Assert.AreEqual(p + t, M.transformPoint(p)); Assert.AreEqual(p + t, M2.transformPoint(p)); }
internal CameraData( Float4x4 cameraMatrix, Float4x4 viewMatrix, Float4x4 projectionMatrix, Float4x4 viewProjectionMatrix, Float4x4 inverseViewProjectionMatrix, float nearClipDistance, float farClipDistance) { CameraMatrix = cameraMatrix; ViewMatrix = viewMatrix; ProjectionMatrix = projectionMatrix; ViewProjectionMatrix = viewProjectionMatrix; InverseViewProjectionMatrix = inverseViewProjectionMatrix; NearClipDistance = nearClipDistance; FarClipDistance = farClipDistance; }
public void SimpleProjectionTest() { Float3 p = new Float3(2, 4, 2); const float n = 3f; const float f = 100f; const float b = -10; const float t = 10; const float r = 10; const float l = -10; Float4x4 Projection = Float4x4.getProjectionMatrix(n, f, t, b, r, l); Float3 p2 = Projection.transformPoint(new Float3(r, b, n)); Float3 p1 = Projection.transformPoint(p); Assert.IsTrue(p1.x < 1f); }
internal static CameraData FromCameraAndProjection( Float4x4 cameraMatrix, Float4x4 projectionMatrix, float nearClipDistance, float farClipDistance) { Float4x4 viewMatrix = cameraMatrix.Invert(); Float4x4 viewProjectionMatrix = projectionMatrix * viewMatrix; return(new CameraData( cameraMatrix, viewMatrix, projectionMatrix, viewProjectionMatrix, viewProjectionMatrix.Invert(), nearClipDistance, farClipDistance)); }
public override void Update() { base.Update(); SceneViewUI sceneView = Root.Find <SceneViewUI>(); Camera camera = sceneView?.Profile.Scene?.camera; if (camera == null) { return; } if (Mouse.IsButtonPressed(Mouse.Button.Right)) { Int2 mouse = Mouse.GetPosition().As(); Float4x4 matrix = camera.LocalToWorld; if (!lastEnabled) { lastEnabled = true; lastMouse = mouse; } Int2 delta = mouse - lastMouse; Float2 input = new Int2 ( KeyDown(Keyboard.Key.D) - KeyDown(Keyboard.Key.A), KeyDown(Keyboard.Key.W) - KeyDown(Keyboard.Key.S) ).Normalized; Float3 movement = input.X_Y * (float)Root.application.DeltaTime; Float3 change = camera.LocalToWorld.MultiplyDirection(movement); camera.Rotation += delta.YX_ * MouseSensitivity; camera.Position += change * MovementSpeed; lastMouse = mouse; if (matrix != camera.LocalToWorld) { sceneView.RequestRedraw(); }
public Mesh Parse() { //Get transform to use when reading the data (so we can scale / rotate based on meta-data) Float4x4 transform = GetTransform(); XmlElement geometriesElement = colladaElement.GetChild("library_geometries"); if (geometriesElement == null) { throw new Exception($"[{nameof(ColladaParser)}] No 'library_geometries' element found"); } //At the moment we only parse the first mesh out of the file, we can expand this later //to load a set of meshes out of the file XmlElement geometryElement = geometriesElement.GetChild(index: 0); if (geometryElement == null) { throw new Exception($"[{nameof(ColladaParser)}] No geometry found"); } return(ParseGeometry(geometryElement, transform)); }
private static FloatSphere GetShadowSphere( Float4x4 ndcToWorldMat, Float4x4 worldToLightMat, float shadowDistance) { //Frustum of the camera that will be covered by the shadow map in NDC space //Note: this covers the entire screen but only to a certain depth FloatBox shadowNDC = new FloatBox( min: (-1f, -1f, 0f), max: (1f, 1f, DepthUtils.LinearToDepth( shadowDistance, Camera.NEAR_CLIP_DISTANCE, Camera.FAR_CLIP_DISTANCE))); //Gather points of the frustum Span <Float3> points = stackalloc Float3[8]; shadowNDC.GetPoints(points); //Transform all the points to lightspace (ndc -> world -> lightspace) Float3 center = Float3.Zero; for (int i = 0; i < points.Length; i++) { points[i] = (worldToLightMat * ndcToWorldMat).TransformPoint(points[i]); center = i == 0 ? points[i] : (center + points[i]); } center /= points.Length; //The the longest diagonal of the frustum and base our sphere on that float squareDiag1 = (points[0] - points[6]).SquareMagnitude; float squareDiag2 = (points[2] - points[4]).SquareMagnitude; float radius = FloatUtils.SquareRoot(FloatUtils.Max(squareDiag1, squareDiag2)) * .5f; return(new FloatSphere(center, radius)); }
public void Render(SceneObject sObject, Float3 viewDirection, Float3 lightDirection, bool useProjection = true) { Mesh mesh = sObject.mesh; Color wireFrameColor = Color.LightGreen; RenderType renderType = sObject.material.renderType; // Vertex uniforms // scale matrix Float3x3 S = Float3x3.identity * sObject.uniformScale; // rotation matrix Float3x3 R = Float3x3.getRotationMatrix(sObject.rotation); Float3x3 CombinedLinear = S * R; // translation Float4x4 Tr = Float4x4.identity; Tr.setTranslation(sObject.localPosition); // projection Float4x4 Pr = useProjection ? Float4x4.getProjectionMatrix(10f, 1300f, 1f, 1f) : Float4x4.identity; // BACK FACE CULLING if (backFaceCulling) { for (int i = mesh.Triangles.Count - 1; i >= 0; i--) { Triangle t = mesh.Triangles[i]; Float3 v1 = mesh.Vertices[t[0] - 1].position; Float3 v2 = mesh.Vertices[t[1] - 1].position; Float3 v3 = mesh.Vertices[t[2] - 1].position; Float3 normal = Utils.getTriangleNormalR(v1, v2, v3); // remove faced back triangles if (viewDirection.dot(normal) >= 0) { mesh.Triangles.Remove(t); } } } // VERTEX SHADER for (int i = 0; i < mesh.Vertices.Count; i++) { Vertex v = mesh.Vertices[i]; // scale var p = v.position.mul(S); // rotate p = p.mul(R); // translate p = Tr.transformPoint(p); // project if (useProjection) { p = Pr.transformPoint(p); } // TODO: Transforming normals while NON UNIFORM TRANSFORMS v.normal = v.normal.mul(R); // TODO: place to center of screen if (useProjection) { v.position = new Float3(p.x * Defaults.WIDTH + Defaults.WIDTH / 2f, p.y * Defaults.HEIGHT + Defaults.HEIGHT / 2f, p.z); } else { v.position = new Float3(p.x + Defaults.WIDTH / 2f, p.y + Defaults.HEIGHT / 2f, p.z); } } if ((renderType & RenderType.Regular) != 0) { RenderRegular(mesh, sObject.material, lightDirection); } if ((renderType & RenderType.Wireframe) != 0) { RenderWireframe(mesh, wireFrameColor); } if ((renderType & RenderType.Normals) != 0) { DrawVertexNormals(mesh, Color.Red); } }
public void UniformMatrix4(int location, bool transpose, Float4x4 value) { }
internal Float4x4 GetProjection(float aspect) => Float4x4.CreatePerspectiveProjection(GetFrustum(aspect));
public void UniformMatrix4(int location, bool transpose, Float4x4 value) { // Who is always changing this function creating a temp array? Please stop committing your workarounds. Thanks TKGL.UniformMatrix4(location, 1, transpose, ref value.M11); }
public static void Clip(Float4x4 x) => throw new InvalidExecutionContextException($"{typeof(Hlsl)}.{nameof(Clip)}({typeof(Float4x4)})");
public InstanceData(Float4x4 modelMatrix, float age = 0f) { ModelMatrix = modelMatrix; Age = age; }
public void UniformMatrix4(int location, bool transpose, Float4x4 value) { TKGL.UniformMatrix4(location, 1, transpose, ref value.M11); }
static void Main(string[] args) { Float3 f3 = new Float3(10, 20, 30); Float3 f32 = new Float3(20, 20, 30); Float3SSE f3sse = new Float3SSE(new float[3] { 10, 20, 30 }); Float3SSE f3sse2 = new Float3SSE(new float[3] { 20, 20, 30 }); Float4 float4 = new Float4(2, 2, 2, 2); Float4 float42 = new Float4(-1, -31.32f, -12.3f, 2); Float4x4 float4X4 = new Float4x4(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6); Float4SSE float4sse = new Float4SSE(new float[] { 2, 2, 2, 2 }); Float4SSE float42sse = new Float4SSE(new float[] { -1, -31.32f, -12.3f, 2 }); Float4x4SSE float4X4sse = new Float4x4SSE(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6); Stopwatch stopwatch = new Stopwatch(); //Float3 Console.WriteLine("Float3"); stopwatch.Start(); Float3 a = f3 + f32; stopwatch.Stop(); Console.WriteLine("sumaFPU" + a.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); Float3SSE asse = f3sse + f3sse2; stopwatch.Stop(); Console.WriteLine("sumaSSE" + asse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a = f3 - f32; stopwatch.Stop(); Console.WriteLine("rożnicaFPU" + a.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); asse = f3sse - f3sse2; stopwatch.Stop(); Console.WriteLine("rożnicaSSE" + asse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a = f3 * f32; stopwatch.Stop(); Console.WriteLine("ilocztnFPU" + a.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); asse = f3sse * f3sse2; stopwatch.Stop(); Console.WriteLine("iloczynSSE" + asse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a = f3 / f32; stopwatch.Stop(); Console.WriteLine("iloraz FPU" + a.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); asse = f3sse / f3sse2; stopwatch.Stop(); Console.WriteLine("iloraz SSE" + asse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); ///// /// Console.WriteLine("\n\n Skalar"); stopwatch.Start(); a = f3 + 1; stopwatch.Stop(); Console.WriteLine("suma FPU" + a.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); asse = f3sse + 1; stopwatch.Stop(); Console.WriteLine("suma SSE" + asse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a = f3 - 1; stopwatch.Stop(); Console.WriteLine("Rożnica FPU" + a.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); asse = f3sse - 1; stopwatch.Stop(); Console.WriteLine("Ronica SSE" + asse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a = f3 * 2; stopwatch.Stop(); Console.WriteLine("iloczyn FPU" + a.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); asse = f3sse * 2; stopwatch.Stop(); Console.WriteLine("iloczyn SSE" + asse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a = f3 / 2.5f; stopwatch.Stop(); Console.WriteLine("iloraz FPU" + a.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); asse = f3sse / 2.5f; stopwatch.Stop(); Console.WriteLine("iloraz SSE" + asse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a = f3.Normalized; stopwatch.Stop(); Console.WriteLine("Normalizacjia FPU" + a.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); asse = f3sse.Normalized; stopwatch.Stop(); Console.WriteLine("Normalizacjia SSE" + asse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a = f3.Reflect(f32); stopwatch.Stop(); Console.WriteLine("Reflect FPU" + a.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); asse = f3sse.Reflect(f3sse2); stopwatch.Stop(); Console.WriteLine("Reflect SSE" + asse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a = f3.saturate(); stopwatch.Stop(); Console.WriteLine("Saturate FPU" + a.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); asse = f3sse.Saturat(); stopwatch.Stop(); Console.WriteLine("Saturate SSE" + asse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); float s = f3.Dot(f32); stopwatch.Stop(); Console.WriteLine("dot FPU" + s.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); asse = f3sse.Dot(f3sse2); stopwatch.Stop(); Console.WriteLine("dot SSE" + asse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); //// ///Float4 Console.WriteLine("Float3"); stopwatch.Start(); Float4 a4 = float4 + float42; stopwatch.Stop(); Console.WriteLine("sumaFPU" + a4.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); Float4SSE a4sse = float42sse + float4sse; stopwatch.Stop(); Console.WriteLine("sumaSSE" + a4sse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a4 = float4 - float42; stopwatch.Stop(); Console.WriteLine("rożnicaFPU" + a4.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); a4sse = float4sse - float42sse; stopwatch.Stop(); Console.WriteLine("rożnicaSSE" + a4sse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a4 = float4 * float42; stopwatch.Stop(); Console.WriteLine("ilocztnFPU" + a4.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); a4sse = float4sse * float42sse; stopwatch.Stop(); Console.WriteLine("iloczynSSE" + a4sse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a4 = float4 / float42; stopwatch.Stop(); Console.WriteLine("iloraz FPU" + a4.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); a4sse = float4sse / float42sse; stopwatch.Stop(); Console.WriteLine("iloraz SSE" + a4sse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); ///// /// Console.WriteLine("\n\n Skalar"); stopwatch.Start(); a4 = float4 + 1; stopwatch.Stop(); Console.WriteLine("suma FPU" + a4.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); a4sse = float4sse + 1; stopwatch.Stop(); Console.WriteLine("suma SSE" + a4sse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a4 = float4 - 1; stopwatch.Stop(); Console.WriteLine("Rożnica FPU" + a4.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); a4sse = float4sse - 1; stopwatch.Stop(); Console.WriteLine("Ronica SSE" + a4sse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a4 = float4 * 2; stopwatch.Stop(); Console.WriteLine("iloczyn FPU" + a4.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); a4sse = float4sse * 2; stopwatch.Stop(); Console.WriteLine("iloczyn SSE" + a4sse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// stopwatch.Start(); a4 = float4 / 2.5f; stopwatch.Stop(); Console.WriteLine("iloraz FPU" + a4.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); a4sse = float4sse / 2.5f; stopwatch.Stop(); Console.WriteLine("iloraz SSE" + a4sse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); /// /// Console.WriteLine("macierz"); stopwatch.Start(); Float4 aas = float4X4 * float4; stopwatch.Stop(); Console.WriteLine("dot FPU" + aas.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); float4sse = float4X4sse * float4sse; stopwatch.Stop(); Console.WriteLine("dot SSE" + float4sse.Vector.ToString()); Console.WriteLine("time" + stopwatch.Elapsed.TotalMilliseconds); stopwatch.Restart(); Console.ReadKey(); }
private Mesh ParseGeometry(XmlElement geometryElement, Float4x4 transform) { XmlElement meshElement = geometryElement.GetChild("mesh"); if (meshElement == null) { throw new Exception($"[{nameof(ColladaParser)}] Mesh element missing"); } //Parse the triangles ParseTriangles(meshElement); //Parse input data int minTexcoordSet = GetMinSet("TEXCOORD"); for (int i = 0; i < inputs.Count; i++) { Input input = inputs.Data[i]; XmlElement dataElement = meshElement.GetChildWithAttribute( name: "source", attributeName: "id", attributeValue: input.Source); switch (input.Semantic) { case "POSITION": ParseFloatSetArray(dataElement, positions); break; case "COLOR": ParseFloatSetArray(dataElement, colors); break; case "NORMAL": ParseFloatSetArray(dataElement, normals); break; case "TEXCOORD": if (input.Set == minTexcoordSet) { ParseFloatSetArray(dataElement, texcoords1); } else if (input.Set == minTexcoordSet + 1) { ParseFloatSetArray(dataElement, texcoords2); } break; } } //Build a mesh from the parsed data MeshBuilder meshBuilder = new MeshBuilder(); for (int i = 0; i < triangleCount; i++) { //Surface normal is used when a vertex doesn't specify explict normals Float3 surfaceNormal = Triangle.GetNormal( GetPosition(i, vertexIndex: 2), GetPosition(i, vertexIndex: 1), GetPosition(i, vertexIndex: 0)); //In reverse as collada uses counter-clockwise triangles and we use clockwise for (int j = 3 - 1; j >= 0; j--) { Float3 position = GetPosition(i, vertexIndex: j); int colorIndex = GetIndex(i, vertexIndex: j, semantic: "COLOR"); Float4 color = colorIndex < 0 ? Float4.One : colors.Data[colorIndex]; int normalIndex = GetIndex(i, vertexIndex: j, semantic: "NORMAL"); Float3 normal = normalIndex < 0 ? surfaceNormal : transform.TransformDirection(Float3.FastNormalize(normals.Data[normalIndex])); int texcoord1Index = GetIndex(i, vertexIndex: j, semantic: "TEXCOORD", set: minTexcoordSet); Float2 texcoord1 = texcoord1Index < 0 ? Float2.Zero : texcoords1.Data[texcoord1Index]; int texcoord2Index = GetIndex(i, vertexIndex: j, semantic: "TEXCOORD", set: minTexcoordSet + 1); Float2 texcoord2 = texcoord2Index < 0 ? Float2.Zero : texcoords2.Data[texcoord2Index]; meshBuilder.PushVertex(new Vertex( position: position, color: color, normal: normal, //Convert uv to be origin = bottom left uv1: (texcoord1.X, 1f - texcoord1.Y), uv2: (texcoord2.X, 1f - texcoord2.Y))); } } return(meshBuilder.ToMesh()); Float3 GetPosition(int triangleIndex, int vertexIndex) { int index = GetIndex(triangleIndex, vertexIndex, semantic: "POSITION"); if (index < 0) { throw new Exception( $"[{nameof(ColladaParser)}] No position data found for: triangle: {triangleIndex}, vertex: {vertexIndex}"); } return(transform.TransformPoint(positions.Data[index])); } int GetIndex(int triangleIndex, int vertexIndex, string semantic, int set = -1) { int offset = GetOffset(semantic, set); if (offset < 0) { return(-1); } int triangleStartOffset = triangleIndex * inputStride * 3; int vertexStartOffset = vertexIndex * inputStride; return(indices.Data[triangleStartOffset + vertexStartOffset + offset]); } int GetOffset(string semantic, int set) { for (int i = 0; i < inputs.Count; i++) { if (inputs.Data[i].Semantic == semantic && inputs.Data[i].Set == set) { return(inputs.Data[i].Offset); } } //If we don't care about a particular set (set < 0) we take any set that matches //the given semantic if (set < 0) { for (int i = 0; i < inputs.Count; i++) { if (inputs.Data[i].Semantic == semantic) { return(inputs.Data[i].Offset); } } } return(-1); } int GetMinSet(string semantic) { int min = int.MaxValue; for (int i = 0; i < inputs.Count; i++) { if (inputs.Data[i].Semantic == semantic && inputs.Data[i].Set < min) { min = inputs.Data[i].Set; } } return(min); } }