void _markGroupFaceNeightbours(int vertexIndex, int faceIndex, ref List <int>[] faceLinkedEdges, int groupIndex) { Stack <int> stack = new Stack <int>(); stack.Push(faceIndex); do { faceIndex = stack.Pop(); Face f = faces[faceIndex]; if (f.mark == -1) { f.mark = groupIndex; // mark face group. -1 = available // locate neighbours connected by non-creases List <int> faceEdges = faceLinkedEdges[faceIndex]; // Search for other faces connected with non-crease edges for (int e = 0; e < faceEdges.Count; ++e) { Edge edge = edges[faceEdges[e]]; if (IsEdgeValid(faceEdges[e]) && edge.crease < 1.0f && edge.ContainsVertex(vertexIndex)) { IndexList edgeFaces = edge.linkedFaces; for (int k = 0; k < edgeFaces.Count; ++k) { int edgeFaceIndex = edgeFaces[k]; if (faces[edgeFaceIndex].mark == -1) { stack.Push(edgeFaceIndex); } } } } } } while (stack.Count > 0); }
// Dissolve an edge between two triangles. // the first is a quad afterwards and the second triangle is destroyed // expects valid input, no checks! public void DissolveEdgeTriangles(int edgeIndex) { int i; Edge e = edges[edgeIndex]; // e needs to have two linked faces int f1index = e.linkedFaces[0]; int f2index = e.linkedFaces[1]; Face f1 = faces[f1index]; Face f2 = faces[f2index]; // f1 and f2 have two common vertices, need to be triangles int v2uniqueCorner = -1; for (i = 0; i < 3; ++i) { if (e.ContainsVertex(f2.v[i]) == false) { v2uniqueCorner = i; break; } } for (i = 0; i < 3; ++i) { if (e.ContainsVertex(f1.v[i]) == false) { int insertPos = i + 2; if (insertPos >= 3) { insertPos -= 3; } f1.cornerCount = 4; // f1 is now a quad UnlinkFace(f2index); vertices[f2.v[v2uniqueCorner]].linkedFaces.Add(f1index); // Move verts backwards for (int j = 3; j > insertPos; --j) { f1.CopyVertexInfoFromFace(f1, j - 1, j); } f1.CopyVertexInfoFromFace(f2, v2uniqueCorner, insertPos); e.linkedFaces.Clear(); break; } } }
// TrisToQuads. Needs mesh with edgelist /// <summary> /// Joins neighbour triangles to quads in a mesh by dissolving selected edges. /// This method needs the mesh to have its edges calculated. The edges are sorted /// by their angles and dissolved in order until the maximum edge angle is reached. /// Special edges such as uv borders or material seams are not dissolved and concave quads /// are avoided. This method is used by the quad-based subdivision algorithm as it works /// much better with quads. /// </summary> /// <param name='mesh'> /// The mesh to process. /// </param> /// <param name='maximumEdgeAngle'> /// The maximum angle between two triangles to be joined to a (non-planar) quad. /// </param> public static void TrisToQuads(MeshEdges mesh, float maximumEdgeAngle) { if (mesh.topology != MeshTopology.Triangles) { return; } int i, j; mesh.topology = MeshTopology.Mixed; // Most likely we'll end up with a mixed topology as some triangles will be left. mesh.CalculateFaceNormals(); mesh.CalculateEdgeLinkedFaces(); // Calculate the edge angles and compile list of 2-face edges int numEdges = mesh.edgeCount(); List <Edge> inneredges = new List <Edge>(); for (i = 0; i < numEdges; ++i) { Edge e = mesh.edges[i] ; if (e.linkedFaces.Count == 2 && mesh.CanEdgeBeDissolved(i)) { e.mark = i; // Save the index! e.angle = mesh.CalculateEdgeAngle(i); if (e.angle < maximumEdgeAngle) { inneredges.Add(e); } } } // Sort by angle inneredges.Sort(delegate(Edge a, Edge b) { return(a.angle.CompareTo(b.angle)); }); int iecount = inneredges.Count; // Debug.Log("Number of inneredges " + iecount); Vector3[] qVec = new Vector3[5]; for (i = 0; i < iecount; ++i) { Edge e = inneredges[i]; Face f1 = mesh.faces[e.linkedFaces[0]]; Face f2 = mesh.faces[e.linkedFaces[1]]; // check if edge is between two triangles if (f1.valid && f2.valid && (f1.cornerCount == 3) && (f2.cornerCount == 3)) { // Make sure this doesn't lead to a concave quad Vector3 newNormal = f1.normal + f2.normal; //newNormal.Normalize(); // Collect all vertex coordinates qVec[0] = mesh.vertices[e.v[0]].coords; qVec[2] = mesh.vertices[e.v[1]].coords; for (j = 0; j < 3; ++j) { if (e.ContainsVertex(f1.v[j]) == false) { qVec[1] = mesh.vertices[f1.v[j]].coords; break; } } for (j = 0; j < 3; ++j) { if (e.ContainsVertex(f2.v[j]) == false) { qVec[3] = mesh.vertices[f2.v[j]].coords; break; } } // Flip order if it doesn't conform to f1 if (f1.VerticesInOrder(e.v[0], e.v[1]) == true) { Vector3 temp = qVec[1]; qVec[1] = qVec[3]; qVec[3] = temp; } // calculate edge vectors qVec[4] = qVec[0]; for (j = 0; j < 4; ++j) { qVec[j] -= qVec[j + 1]; } qVec[4] = qVec[0]; bool convex = true; for (j = 0; j < 4; ++j) { Vector3 localN = Vector3.Cross(qVec[j], qVec[j + 1]); //localN.Normalize(); float nAngleCos = Vector3.Dot(newNormal, localN); //Debug.Log("Angle " + nAngleCod); if (nAngleCos <= 0.0f) { convex = false; break; } } if (convex) { // Debug.Log("Dissolving edge"); mesh.DissolveEdgeTriangles(e.mark); } else { // Debug.Log("not dissolving edge -> concave quad"); } } } // There are now invalid faces and edges mesh.RebuildMesh(); mesh.GenerateEdgeTopology(); }