public void TestAngleAxis() { Assert.AreEqual( new Vec3HighPrecision(0, 0, 1), (new Vec4(0, 0, -1, 0) * Mat4HighPrecision.AngleAxisRot(new Vec3HighPrecision(0, 1, 0), -1.0, 0.0)).ToVec3() // PI = -1 ); Vec3HighPrecision.AssertAreEqual( new Vec3HighPrecision(-1, 0, 0), (new Vec4(0, 0, -1, 0) * Mat4HighPrecision.AngleAxisRot(new Vec3HighPrecision(0, 1, 0), 0.0, 1.0)).ToVec3(), // PI/2 = 0 "Rotation by 90 degrees along Y axis failed"); Vec3HighPrecision.AssertAreEqual( new Vec3HighPrecision(1, 0, 0), (new Vec4(0, 0, -1, 0) * Mat4HighPrecision.AngleAxisRot(new Vec3HighPrecision(0, 1, 0), 0.0, -1.0)).ToVec3(), // -PI/2 = 0 "Rotation by -90 degrees along Y axis failed"); Vec3HighPrecision.AssertAreEqual( new Vec3HighPrecision(0, -1, 0), (new Vec4(0, 0, -1, 0) * Mat4HighPrecision.AngleAxisRot(new Vec3HighPrecision(1, 0, 0), 0.0, -1.0)).ToVec3(), // -PI/2 = 0 "Rotation by 90 degrees along X axis failed"); var rnd = new System.Random(); for (int i = 0; i < 100; i++) { var k = (new Vec3HighPrecision(rnd.NextDouble(), rnd.NextDouble(), rnd.NextDouble()) - new Vec3HighPrecision(0.5, 0.5, 0.5)) * 100.0; var v = (new Vec3HighPrecision(rnd.NextDouble(), rnd.NextDouble(), rnd.NextDouble()) - new Vec3HighPrecision(0.5, 0.5, 0.5)) * 100.0; var a = rnd.NextDouble() * Math.PI * 2.0 - Math.PI; var vr = (new Vec4(v, 1.0) * Mat4HighPrecision.AngleAxisRot(k.Normalized, Math.Cos(a), Math.Sin(a))).ToVec3(); var vproj = k.Normalized * Vec3HighPrecision.Dot(v, k.Normalized); var ang = Math.Acos(Vec3HighPrecision.Dot((vproj - v).Normalized, (vproj - vr).Normalized)); Assert.That(Math.Abs(a), Is.EqualTo(ang).Within(0.00001), "Random rotation around random axis failed."); } }
static UnwrappedMesh MakeUnwrappedMesh(IReadOnlyList <Vec3> vertices, IReadOnlyList <Tri> triangles, Action <UnwrapIter> pushAction, Func <UnwrapIter> popAction) { var connections = GenerateEdgeConnections(triangles); int firstTri = 0; if (triangles.Count <= firstTri) { throw new Exception("firstTri out of bounds"); } // transform first triangle on XZ plane Mat4HighPrecision firstRot; { var tri = triangles[firstTri]; var p0 = vertices[tri.p0]; var p1 = vertices[tri.p1]; var p2 = vertices[tri.p2]; var e0 = p1 - p0; var e1 = p2 - p0; var n = Vec3.Cross(e0, e1); firstRot = Mat4HighPrecision.Axes(e0.Normalized, n.Normalized, Vec3.Cross(e0, n).Normalized).Transposed(); } var firstTf = firstRot; var firstEdge = new Edge(triangles[firstTri].p0, triangles[firstTri].p1); var iteratedTris = new HashSet <int>(); List <Vec3> unwrappedVertices = new List <Vec3>(); List <Vec3> originalVertices = new List <Vec3>(); List <Tri> unwrappedTriangles = new List <Tri>(); originalVertices.Add(vertices[firstEdge.p0]); originalVertices.Add(vertices[firstEdge.p1]); unwrappedVertices.Add((new Vec4(vertices[firstEdge.p0], 1.0) * firstRot).ToVec3()); unwrappedVertices.Add((new Vec4(vertices[firstEdge.p1], 1.0) * firstRot).ToVec3()); pushAction(new UnwrapIter(firstTri, firstTf, firstEdge, 1, 0)); int N = 0; while (unwrappedTriangles.Count < triangles.Count) { var i = popAction(); if (iteratedTris.Contains(i.TriIndex)) { continue; } if (i.TriIndex >= triangles.Count) { throw new Exception("TriIndex " + i.TriIndex + " out of bounds, triangle count: " + triangles.Count); } iteratedTris.Add(i.TriIndex); var t = triangles[i.TriIndex]; var a = vertices[t.p0]; var b = vertices[t.p1]; var c = vertices[t.p2]; var p0 = (new Vec4(a, 1.0) * i.Transform).ToVec3(); var p1 = (new Vec4(b, 1.0) * i.Transform).ToVec3(); var p2 = (new Vec4(c, 1.0) * i.Transform).ToVec3(); var e01 = i.EdgeToUnwrap.Equals(new Edge(t.p0, t.p1)); var e12 = i.EdgeToUnwrap.Equals(new Edge(t.p1, t.p2)); var e20 = i.EdgeToUnwrap.Equals(new Edge(t.p2, t.p0)); //AssertOnXZPlane(p0,p1,p2, string.Format("Failed after dequeue, N={0}", N)); Vec3HighPrecision e0, e1, mp; if (e01) { e0 = p1 - p0; e1 = p2 - p0; mp = (p1 + p0) * 0.5; } else if (e12) { e0 = p2 - p1; e1 = p0 - p1; mp = (p2 + p1) * 0.5; } else if (e20) { e0 = p0 - p2; e1 = p1 - p2; mp = (p0 + p2) * 0.5; } else { throw new Exception("Unreachable code executed."); } var n = Vec3.Cross(e0, e1).Normalized; var rotDir = Math.Sign(Vec3.Dot(Vec3.Cross(n, new Vec3(0, 1, 0)), e0)); // rotate next triangle to XZ plane rotating around the shared edge with the previous triangle (which is already on XZ plane) var rot = Mat4HighPrecision.Translate(mp * -1.0) * Mat4HighPrecision.AngleAxisRot(e0.Normalized, n.Y, Math.Sqrt(1 - n.Y * n.Y) * rotDir) * Mat4HighPrecision.Translate(mp * 1.0); var ntf = i.Transform * rot; var np0 = (new Vec4(a, 1.0) * ntf).ToVec3(); var np1 = (new Vec4(b, 1.0) * ntf).ToVec3(); var np2 = (new Vec4(c, 1.0) * ntf).ToVec3(); int v0, v1, v2; if (e01) { v0 = i.UnwrappedInd1; v1 = i.UnwrappedInd0; v2 = unwrappedVertices.Count; unwrappedVertices.Add(np2); originalVertices.Add(c); } else if (e12) { v0 = unwrappedVertices.Count; v1 = i.UnwrappedInd1; v2 = i.UnwrappedInd0; unwrappedVertices.Add(np0); originalVertices.Add(a); } else if (e20) { v0 = i.UnwrappedInd0; v1 = unwrappedVertices.Count; v2 = i.UnwrappedInd1; unwrappedVertices.Add(np1); originalVertices.Add(b); } else { throw new Exception("Unreachable code executed."); } unwrappedTriangles.Add(new Tri(v0, v1, v2)); EnqueueNeighborTriangles(i.TriIndex, t, ntf, v0, v1, v2, pushAction, connections); N++; } return(new UnwrappedMesh { Vertices = unwrappedVertices, Triangles = unwrappedTriangles, VerticesOriginalPositions = originalVertices }); }