// What this basically does is search for common faces in the two linkedFaces lists // It uses & destroys face marks public void CollectVertexPairFaces(VertexPair pair, IndexList commonFaces) { IndexList il0 = vertices[pair.v[0]].linkedFaces; // surrounding faces IndexList il1 = vertices[pair.v[1]].linkedFaces; // surrounding faces int[] v0Faces = il0.array; int[] v1Faces = il1.array; int v0Count = il0.Count; int v1Count = il1.Count; int tag = GetUniqueTag(); commonFaces.GrowToCapacity(v1Count); for (int i = 0; i < v0Count; ++i) { faces[v0Faces[i]].mark = tag; } for (int i = 0; i < v1Count; ++i) { int faceIndex = v1Faces[i]; if (faces[faceIndex].mark == tag) { commonFaces.AddUnsafe(faceIndex); } } }
// VERTEXPAIRS // public bool isVertexPairValid(VertexPair vp) { if (vp.v[0] == vp.v[1]) { return(false); } if (IsVertexValid(vp.v[0]) == false) { return(false); } if (IsVertexValid(vp.v[1]) == false) { return(false); } return(true); }
public void CollectCollapseFacesForVertexPair(VertexPair pair, IndexList changeFaces0, IndexList changeFaces1, IndexList commonFaces) { IndexList il0 = vertices[pair.v[0]].linkedFaces; IndexList il1 = vertices[pair.v[1]].linkedFaces; int[] v0Faces = il0.array; int[] v1Faces = il1.array; int v0Count = il0.Count; int v1Count = il1.Count; int tag = GetUniqueTag(); // Grow target lists to save on checks later changeFaces0.GrowToCapacity(v0Count); changeFaces1.GrowToCapacity(v1Count); commonFaces.GrowToCapacity(v0Count); // could be min(v0count, v1count), but that's probably slower for (int i = 0; i < v1Count; ++i) { faces[v1Faces[i]].mark = tag; } for (int i = 0; i < v0Count; ++i) { int faceIndex = v0Faces[i]; Face f = faces[faceIndex]; // if (f.valid) { if (f.mark == tag) { commonFaces.AddUnsafe(faceIndex); f.mark = 0; } else { changeFaces0.AddUnsafe(faceIndex); } // } } for (int i = 0; i < v1Count; ++i) { int faceIndex = v1Faces[i]; Face f = faces[faceIndex]; if (/*f.valid &&*/ f.mark == tag) { changeFaces1.AddUnsafe(faceIndex); } } }
// REQUIRES triangulated mesh! public int CollapseVertexPair(CollapseInfo info) { if (topology != MeshTopology.Triangles) { Debug.LogError("KrablMesh: Collapsing a vertex pair requires a triangle mesh"); return(0); } VertexPair pair = info.vp; int vindex0 = pair.v[0]; int vindex1 = pair.v[1]; Vertex vertex0 = vertices[vindex0]; Vertex vertex1 = vertices[vindex1]; int i, j; changeFaces0.Clear(); changeFaces1.Clear(); removeFaces.Clear(); CollectCollapseFacesForVertexPair(pair, changeFaces0, changeFaces1, removeFaces); // Adjust parameters of vertex0 to the new position float ratio1 = info.Ratio(this); // try baricentric projection on all the removeFaces (usually 2) int projFaceIndex = -1; Face projFace = null; int projCorner0 = 0, projCorner1 = 0; Vector3 bari = Vector3.zero; int[] v = null; for (i = 0; i < removeFaces.Count; ++i) { Face f = faces[removeFaces[i]]; v = f.v; bari = UnityUtils.BaricentricProjection(info.targetPosition, vertices[v[0]].coords, vertices[v[1]].coords, vertices[v[2]].coords); if (UnityUtils.AreBaricentricCoordsInsideTriangle(bari)) { projFaceIndex = removeFaces[i]; projFace = f; projCorner0 = projFace.CornerIndexTriangle(vindex0); projCorner1 = projFace.CornerIndexTriangle(vindex1); break; } } // There must not be invalid faces in changeFaces0 or changeFaces1 !!! /* for (i = 0; i < changeFaces0.Count; ++i) if (faces[changeFaces0[i]].valid == false) Debug.LogError("NOOO!"); * for (i = 0; i < changeFaces1.Count; ++i) if (faces[changeFaces1[i]].valid == false) Debug.LogError("NOOO!"); * for (i = 0; i < removeFaces.Count; ++i) if (faces[removeFaces[i]].valid == false) Debug.LogError("NOOO!"); */ // Deal with vertex colors and boneweights. these are per vertex. if (projFace != null) { if (hasVertexColors) { vertex0.color = bari.x * vertices[v[0]].color + bari.y * vertices[v[1]].color + bari.z * vertices[v[2]].color; } if (hasBoneWeights) { vertex0.boneWeight = UnityUtils.BoneWeightBaricentricInterpolation(vertices[v[0]].boneWeight, vertices[v[1]].boneWeight, vertices[v[2]].boneWeight, bari.x, bari.y, bari.z); } } else { if (hasVertexColors) { vertex0.color = Color.Lerp(vertex0.color, vertex1.color, ratio1); } if (hasBoneWeights) { vertex0.boneWeight = UnityUtils.BoneWeightLerp(vertex0.boneWeight, vertex1.boneWeight, ratio1); } } // Determine corner numbers for v0 in changefaces0 and v1 in changefaces1 IndexList corners0 = new IndexList(changeFaces0.Count); for (i = 0; i < changeFaces0.Count; ++i) { corners0[i] = faces[changeFaces0[i]].CornerIndexTriangle(vindex0); } IndexList corners1 = new IndexList(changeFaces1.Count); for (i = 0; i < changeFaces1.Count; ++i) { corners1[i] = faces[changeFaces1[i]].CornerIndexTriangle(vindex1); } #region Face-Dependent Attributes (Vertex normals, uv1, uv2) // NORMALS int count = 0, filterTag = GetUniqueTag(); Vector3 projNormalNew = Vector3.zero; if (projFace != null) { projNormalNew = bari.x * projFace.vertexNormal[0] + bari.y * projFace.vertexNormal[1] + bari.z * projFace.vertexNormal[2]; count = _replaceCornerNormalInFaceGroup(projFace.vertexNormal[projCorner0], projNormalNew, changeFaces0, corners0, filterTag); } if (count < changeFaces0.Count) { // there are faces which cannot use baricentric projection for (j = 0; j < removeFaces.Count; ++j) { if (removeFaces[j] != projFaceIndex) { Face f2 = faces[removeFaces[j]]; int c0 = f2.CornerIndexTriangle(vindex0), c1 = f2.CornerIndexTriangle(vindex1); Vector3 oldNormal = f2.vertexNormal[c0]; _replaceCornerNormalInFaceGroup(oldNormal, Vector3.Lerp(oldNormal, f2.vertexNormal[c1], ratio1), changeFaces0, corners0, filterTag); } } } count = 0; filterTag = GetUniqueTag(); if (projFace != null) { count = _replaceCornerNormalInFaceGroup(projFace.vertexNormal[projCorner1], projNormalNew, changeFaces1, corners1, filterTag); } if (count < changeFaces1.Count) { // there are faces which cannot use baricentric projection for (j = 0; j < removeFaces.Count; ++j) { if (removeFaces[j] != projFaceIndex) { Face f2 = faces[removeFaces[j]]; int c0 = f2.CornerIndexTriangle(vindex0), c1 = f2.CornerIndexTriangle(vindex1); Vector3 oldNormal = f2.vertexNormal[c1]; _replaceCornerNormalInFaceGroup(oldNormal, Vector3.Lerp(f2.vertexNormal[c0], oldNormal, ratio1), changeFaces1, corners1, filterTag); } } } if (hasUV1) { count = 0; filterTag = GetUniqueTag(); Vector2 projUV1New = Vector2.zero; if (projFace != null) { projUV1New = bari.x * projFace.uv1[0] + bari.y * projFace.uv1[1] + bari.z * projFace.uv1[2]; count = _replaceCornerUV1InFaceGroup(projFace.uv1[projCorner0], projUV1New, changeFaces0, corners0, filterTag); } if (count < changeFaces0.Count) { // there are faces which cannot use baricentric projection for (j = 0; j < removeFaces.Count; ++j) { if (removeFaces[j] != projFaceIndex) { Face f2 = faces[removeFaces[j]]; int c0 = f2.CornerIndexTriangle(vindex0), c1 = f2.CornerIndexTriangle(vindex1); Vector2 oldUV1 = f2.uv1[c0]; _replaceCornerUV1InFaceGroup(oldUV1, Vector2.Lerp(oldUV1, f2.uv1[c1], ratio1), changeFaces0, corners0, filterTag); } } } count = 0; filterTag = GetUniqueTag(); if (projFace != null) { count = _replaceCornerUV1InFaceGroup(projFace.uv1[projCorner1], projUV1New, changeFaces1, corners1, filterTag); } if (count < changeFaces1.Count) { // there are faces which cannot use baricentric projection for (j = 0; j < removeFaces.Count; ++j) { if (removeFaces[j] != projFaceIndex) { Face f2 = faces[removeFaces[j]]; int c0 = f2.CornerIndexTriangle(vindex0), c1 = f2.CornerIndexTriangle(vindex1); Vector2 oldUV1 = f2.uv1[c1]; _replaceCornerUV1InFaceGroup(oldUV1, Vector2.Lerp(f2.uv1[c0], oldUV1, ratio1), changeFaces1, corners1, filterTag); } } } } if (hasUV2) { count = 0; filterTag = GetUniqueTag(); Vector2 projUV2New = Vector2.zero; if (projFace != null) { projUV2New = bari.x * projFace.uv2[0] + bari.y * projFace.uv2[1] + bari.z * projFace.uv2[2]; count = _replaceCornerUV2InFaceGroup(projFace.uv2[projCorner0], projUV2New, changeFaces0, corners0, filterTag); } if (count < changeFaces0.Count) { // there are faces which cannot use baricentric projection for (j = 0; j < removeFaces.Count; ++j) { if (removeFaces[j] != projFaceIndex) { Face f2 = faces[removeFaces[j]]; int c0 = f2.CornerIndexTriangle(vindex0), c1 = f2.CornerIndexTriangle(vindex1); Vector2 oldUV2 = f2.uv2[c0]; _replaceCornerUV2InFaceGroup(oldUV2, Vector2.Lerp(oldUV2, f2.uv2[c1], ratio1), changeFaces0, corners0, filterTag); } } } count = 0; filterTag = GetUniqueTag(); if (projFace != null) { count = _replaceCornerUV2InFaceGroup(projFace.uv2[projCorner1], projUV2New, changeFaces1, corners1, filterTag); } if (count < changeFaces1.Count) { // there are faces which cannot use baricentric projection for (j = 0; j < removeFaces.Count; ++j) { if (removeFaces[j] != projFaceIndex) { Face f2 = faces[removeFaces[j]]; int c0 = f2.CornerIndexTriangle(vindex0), c1 = f2.CornerIndexTriangle(vindex1); Vector2 oldUV2 = f2.uv2[c1]; _replaceCornerUV2InFaceGroup(oldUV2, Vector2.Lerp(f2.uv2[c0], oldUV2, ratio1), changeFaces1, corners1, filterTag); } } } } #endregion // Move vertex to goal position vertex0.coords = info.targetPosition; // remove faces // Debug.Log("Change faces 1 num: " + changeFaces0.Count); // Debug.Log("Change faces 2 num: " + changeFaces1.Count); // Debug.Log("Remove faces num: " + removeFaces.Count); for (i = 0; i < removeFaces.Count; ++i) { UnlinkFace(removeFaces[i]); } // change vertex on vindex1 faces, update surrounding faces on vindex0 for (i = 0; i < changeFaces1.Count; ++i) { int faceIndex = changeFaces1[i]; Face f = faces[faceIndex]; if (f.valid) { f.ReplaceVertex(vindex1, vindex0); vertex0.linkedFaces.Add(faceIndex); } } // mark vindex1 as invalid vertex1.linkedFaces.Clear(); if (vertex1.valid == true) { numValidVerts--; vertex1.valid = false; } else { Debug.LogError("vindex1 was already invalid"); } return(vindex0); }
public Vector3 CalculateVertexPairCenter(VertexPair vp) { return(0.5f * (vertices[vp.v[0]].coords + vertices[vp.v[1]].coords)); }
public int Collapse(int numEdgesToCollapse = 1, float maxCost = 1e6f) { int collapsesDone = 0; while (numEdgesToCollapse > 0) { HeapNode <CollapseInfo> node; do { node = heap.Extract(); } while (node != null && mesh.isVertexPairValid(node.obj.vp) == false); if (node == null) { break; } if (node.heapValue > maxCost) { break; } CollapseInfo cinfo = node.obj; VertexPair vp = cinfo.vp; // Add the error terms .. v[0] is the vertex that survives the collapse pdePerVertex[vp.v[0]].OpAdd(pdePerVertex[vp.v[1]]); // DO THE COLLAPSE int vindex = mesh.CollapseVertexPair(cinfo); // vindex is the surviving vertex index // Need to update the cost of all edges around all linked faces of vindex // these are the edges connected to vindex + all the edges connected to vindex neighbours _edgesToUpdate.Clear(); _surroundingVerts.Clear(); mesh.CollectVerticesAroundVertex(vindex, ref _surroundingVerts); // TODO: do this better -> why? int mark = mesh.GetUniqueTag(); for (int i = 0; i < _surroundingVerts.Count; ++i) { List <int> lEdges = mesh.linkedEdgesForVert(_surroundingVerts[i]); for (int j = 0; j < lEdges.Count; ++j) { int edgeIndex = lEdges[j]; Edge e = mesh.edges[edgeIndex]; if (e.mark != mark) { e.mark = mark; if (mesh.IsEdgeValid(edgeIndex)) { _edgesToUpdate.Add(edgeIndex); } } } } // DO the update for (int i = 0; i < _edgesToUpdate.Count; ++i) { int edgeIndex = _edgesToUpdate[i]; HeapNode <CollapseInfo> hnode = heapNodes[edgeIndex]; if (mesh.edges[edgeIndex].ContainsVertex(vindex)) { _calculateEdgeCost(edgeIndex, hnode.obj); } else { _updateEdgePenalties(edgeIndex, hnode.obj, vindex); } heap.Update(hnode, hnode.obj.cost); } numEdgesToCollapse--; collapsesDone++; } return(collapsesDone); }
public new int CollapseVertexPair(CollapseInfo info) { VertexPair pair = info.vp; int v0 = pair.v[0]; int v1 = pair.v[1]; List <int> v0Edges = linkedEdges[v0]; List <int> v1Edges = linkedEdges[v1]; int i; // Update edges // Mark the vertices that are connected by edges for (i = 0; i < v1Edges.Count; ++i) { int edgeIndex = v1Edges[i]; int other = edges[edgeIndex].OtherVertex(v1); vertices[other].mark = -1; } for (i = 0; i < v0Edges.Count; ++i) { int edgeIndex = v0Edges[i]; int other = edges[edgeIndex].OtherVertex(v0); vertices[other].mark = edgeIndex; } // now v1 verts that are only connected to v1 have value -1, double edge-connected verts have the edgeindex as mark for (i = 0; i < v1Edges.Count; ++i) { int edgeIndex = v1Edges[i]; if (vertices[edges[edgeIndex].OtherVertex(v1)].mark == -1) { edges[edgeIndex].ReplaceVertex(v1, v0); if (IsEdgeValid(edgeIndex)) { v0Edges.Add(edgeIndex); } } else { Edge e1 = edges[edgeIndex]; int vindex = e1.OtherVertex(v1); Edge e0 = edges[vertices[vindex].mark]; // vertex mark is edge index! // There has to be another edge connecting v0 to vertex vindex e0.crease = Mathf.Max(e1.crease, e0.crease); // keep the max crease value UnlinkEdge(edgeIndex); // no more need for this! } } // Remove invalid edges from mesh for (i = v0Edges.Count - 1; i >= 0; --i) // backwards should be faster and i stays valid! { int edgeIndex = v0Edges[i]; if (IsEdgeValid(edgeIndex) == false) { UnlinkEdge(edgeIndex); //v0Edges.Remove(edgeIndex); } } // Deal with vertices and faces in baseclass info.vp = new VertexPair(v0, v1); // the original might have been invalidated THIS IS BAD base.CollapseVertexPair(info); v1Edges.Clear(); // rebuild linkedfaces for the remaining edges for (i = 0; i < v0Edges.Count; ++i) { Edge edge = edges[v0Edges[i]]; edge.linkedFaces.Clear(); CollectVertexPairFaces(edge, edge.linkedFaces); } return(v0); }