/// <summary> /// Generate the dual of this geometry. ( The dual mesh translates vertices to faces, and faces to vertices ). /// Used for rendering the world. /// </summary> /// <typeparam name="AltVertex">The dual's vertex format</typeparam> /// <param name="colorProvider">A system for coloring the vertices of the dual</param> /// <returns></returns> public Geometry <AltVertex> GenerateDual <AltVertex>(IColorProvider colorProvider) where AltVertex : struct, IVertex { // For each edge, // get centroids of triangles each side // make 2 new triangles from verts of edge + centroids List <AltVertex> newVerts = new List <AltVertex>(mesh.vertices.Length + Topology.Edges.Count * 2); // Initialize the first V verts for (int i = 0; i < mesh.vertices.Length; ++i) { newVerts.Add(new AltVertex()); } List <uint> newIndices = new List <uint>(); foreach (var iter in Topology.Edges) { Int64 key = iter.Key; int index1 = (int)(key & 0xffffffff); int index2 = (int)((key >> 32) & 0xffffffff); Edge e = iter.Value; Vector3 centroid1 = Topology.Centroids[e.triangle1].position; Vector3 centroid2 = Topology.Centroids[e.triangle2].position; // To find which order of vertices to use; // if triangle1 contains index1 followed by index2, // new tris are index1, c2, c1; index2, c1, c2 // otherwise, the opposite order. bool edgeOrderIsAnticlockwise = false; for (int i = 0; i < 3; i++) { if (indices[e.triangle1 * 3 + i] == index1) { if (indices[e.triangle1 * 3 + (i + 1) % 3] == index2) { edgeOrderIsAnticlockwise = true; } break; } } // Get the color of the face from the color provider for the vertex in the primary mesh Vector4 c1 = colorProvider.GetColor(index1); Vector4 c2 = colorProvider.GetColor(index2); if (edgeOrderIsAnticlockwise) { AddTriangle2(ref newVerts, ref newIndices, index1, ref centroid2, ref centroid1, c1); AddTriangle2(ref newVerts, ref newIndices, index2, ref centroid1, ref centroid2, c2); } else { AddTriangle2(ref newVerts, ref newIndices, index1, ref centroid1, ref centroid2, c1); AddTriangle2(ref newVerts, ref newIndices, index2, ref centroid2, ref centroid1, c2); } } var newMesh = new Mesh <AltVertex>(newVerts.ToArray()); var newGeom = new Geometry <AltVertex>(newMesh, newIndices.ToArray()); return(newGeom); }
public void TweakTriangles(float ratio, Random rand) { NeedsUpdate = true; // Assumptions: minimised mesh. Shared edges won't work without shared verts. Topology.GenerateEdges(); // How many edges do we have? exchange some percentage of them... int numPerturbations = (int)((float)Topology.Edges.Count * ratio); int numTriangles = mesh.vertices.Length; List <Int64> keys = new List <Int64>(Topology.Edges.Keys); List <int> visitedTris = new List <int>(); for (int i = 0; i < numPerturbations; ++i) { // Choose a random edge: int edgeIndex = rand.Next(Topology.Edges.Count); // Check out the tris around the edge. Int64 key = keys[edgeIndex]; Edge edge = Topology.Edges[key]; // TODO - add flag to triangle to avoid n2/2 lookup. bool r = false; foreach (var visited in visitedTris) { if (edge.triangle1 == visited || edge.triangle2 == visited) { r = true; break; } } if (r) { continue; } visitedTris.Add(edge.triangle1); visitedTris.Add(edge.triangle2); if (IsObtuse(edge.triangle1) || IsObtuse(edge.triangle2)) { continue; } uint index1 = (uint)(key & 0xffffffff); uint index2 = (uint)((key >> 32) & 0xffffffff); uint index3; uint index4; uint a = index1; int cornerA = GetTriOffset(edge.triangle1, a); // get the next coord in the triangle (in ccw order) uint b = indices[edge.triangle1 * 3 + ((cornerA + 1) % 3)]; uint c = indices[edge.triangle1 * 3 + ((cornerA + 2) % 3)]; uint d = 0; if (b == index2) { d = indices[edge.triangle2 * 3 + ((GetTriOffset(edge.triangle2, a) + 1) % 3)]; index3 = c; index4 = d; } else { d = indices[edge.triangle2 * 3 + ((GetTriOffset(edge.triangle2, a) + 2) % 3)]; index3 = b; index4 = d; } if (Topology.TrianglesPerVertex[index1] <= 5 || Topology.TrianglesPerVertex[index2] <= 5 || Topology.TrianglesPerVertex[index3] >= 7 || Topology.TrianglesPerVertex[index4] >= 7) { continue; } // Check edge lengths Vector3 pos1 = MeshAttr.GetPosition(ref mesh.vertices[index1]); Vector3 pos2 = MeshAttr.GetPosition(ref mesh.vertices[index2]); Vector3 pos3 = MeshAttr.GetPosition(ref mesh.vertices[index3]); Vector3 pos4 = MeshAttr.GetPosition(ref mesh.vertices[index4]); float oldLength = new Vector3(pos2 - pos1).Length; float newLength = new Vector3(pos4 - pos3).Length; if (oldLength / newLength >= 2 || oldLength / newLength <= 0.5f) { continue; } Topology.TrianglesPerVertex[index1]--; Topology.TrianglesPerVertex[index2]--; Topology.TrianglesPerVertex[index3]++; Topology.TrianglesPerVertex[index4]++; // Need to keep tris in CCW order. if (b == index2) { // order is a b c; c is the non-shared vertex. // new tris are: ADC, DBC // tri2 order is a d b indices[edge.triangle1 * 3] = a; indices[edge.triangle1 * 3 + 1] = d; indices[edge.triangle1 * 3 + 2] = c; indices[edge.triangle2 * 3] = d; indices[edge.triangle2 * 3 + 1] = b; indices[edge.triangle2 * 3 + 2] = c; } else { // order is a b c; b is the non-shared value // new tris are ACD, CBD // tri2 order is a b d indices[edge.triangle1 * 3] = a; indices[edge.triangle1 * 3 + 1] = b; indices[edge.triangle1 * 3 + 2] = d; indices[edge.triangle2 * 3] = c; indices[edge.triangle2 * 3 + 1] = d; indices[edge.triangle2 * 3 + 2] = b; } } Topology.Regenerate(); }