int _replaceCornerUV2InFaceGroup(Vector2 oldUV2, Vector2 newUV2, IndexList faceList, IndexList corners, int filterTag) { int count = 0; for (int i = 0; i < faceList.Count; ++i) { Face f = faces[faceList[i]]; if (f.mark == filterTag) { continue; } if (UnityUtils.Vector2CompareWithTolerance(f.uv2[corners[i]], oldUV2, equalityTolerance) == 0) { f.uv2[corners[i]] = newUV2; f.mark = filterTag; count++; } } return(count); }
public void RebuildMesh(ref int[] vertTable, ref int[] faceTable, bool verbose = false) { InvalidateUnconnectedVertices(); InvalidateDegenerateFaces(); int numVerts = vertCount(); int numFaces = faceCount(); vertTable = new int[numVerts]; faceTable = new int[numFaces]; List <Vertex> newVerts = new List <Vertex>(); List <Face> newFaces = new List <Face>(); for (int i = 0; i < numVerts; ++i) { if (vertices[i].valid) { vertTable[i] = newVerts.Count; newVerts.Add(vertices[i]); } else { vertTable[i] = -1; } } for (int i = 0; i < numFaces; ++i) { if (faces[i].valid) { faceTable[i] = newFaces.Count; newFaces.Add(faces[i]); } else { faceTable[i] = -1; } } vertices = newVerts; faces = newFaces; // Update index if (verbose) { Debug.Log("Rebuild Vertex Count " + numVerts + " -> " + vertCount()); Debug.Log("Rebuild Face Count " + numFaces + " -> " + faceCount()); } numVerts = vertCount(); numFaces = faceCount(); for (int i = 0; i < numVerts; ++i) { IndexList lFaces = vertices[i].linkedFaces; IndexList lFacesNew = new IndexList(18); for (int j = 0; j < lFaces.Count; ++j) { int l = faceTable[lFaces[j]]; if (l != -1) { lFacesNew.Add(l); } else { Debug.LogError("!!!!!"); } } vertices[i].linkedFaces = lFacesNew; } for (int i = 0; i < numFaces; ++i) { int[] v = faces[i].v; for (int j = 0; j < faces[i].cornerCount; ++j) { v[j] = vertTable[v[j]]; } } numValidVerts = numVerts; numValidFaces = numFaces; }
// 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); }
int _replaceCornerNormalInFaceGroup(Vector3 oldNormal, Vector3 newNormal, IndexList faceList, IndexList corners, int filterTag) { int count = 0; for (int i = 0; i < faceList.Count; ++i) { Face f = faces[faceList[i]]; if (f.mark == filterTag) { continue; } if (UnityUtils.Vector3CompareWithTolerance(f.vertexNormal[corners[i]], oldNormal, equalityTolerance) == 0) { f.vertexNormal[corners[i]] = newNormal; f.mark = filterTag; count++; } } return(count); }
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); } } }
void _updateEdgePenalties(int edgeIndex, CollapseInfo cinfo, int movedVertexIndex = -1) { Edge edge = mesh.edges[edgeIndex]; int vindex0 = edge.v[0]; int vindex1 = edge.v[1]; Vertex v0 = mesh.vertices[vindex0]; Vertex v1 = mesh.vertices[vindex1]; faces0.Clear(); faces1.Clear(); commonFaces.Clear(); bool hadPenalty = (cinfo.cost >= kMeshPenaltyMaxEdgesPerVertex); cinfo.cost = cinfo.positionCost; // reset cost // determine the faces involved in the collapse .. mesh.CollectCollapseFacesForVertexPair(mesh.edges[edgeIndex], faces0, faces1, commonFaces); // Penalties int filterTag = 0; if (_parameters.preventNonManifoldEdges && _producesNonManifold(mesh, cinfo)) { cinfo.cost += kMeshPenaltyNonManifold; // largest penalty first } else { if (movedVertexIndex != -1 && hadPenalty == false) { // For cinfos that are not new and had no penalty before, all faces besides the onces connected // to the moved vertex can be skipped. filterTag = mesh.GetUniqueTag(); IndexList linkedFaces = mesh.vertices[movedVertexIndex].linkedFaces; int count = linkedFaces.Count; for (int i = 0; i < count; ++i) { mesh.faces[linkedFaces[i]].mark = filterTag; } } if (_parameters.checkTopology && _producesBadTopology(mesh, cinfo, filterTag)) { // Apply penalties for bad collapses cinfo.cost += kMeshPenaltyBadTopology; } else if (_parameters.maxEdgesPerVertex > 0 && _vertexDegreeAfterCollapse(mesh, cinfo) > _parameters.maxEdgesPerVertex) // Hard coded at 18 for now.. rarely reached, but always check! // Avoid collapses leading to excessive stars (many verts connected to one) { cinfo.cost += kMeshPenaltyMaxEdgesPerVertex; } } // Additional penalties: float val = 0.0f; if (_parameters.boneWeightProtection > 0.0f) { val += UnityUtils.BoneWeightDeltaSqr(v0.boneWeight, v1.boneWeight) * _parameters.boneWeightProtection; } if (_parameters.vertexColorProtection > 0.0f) { val += UnityUtils.ColorDeltaSqr(v0.color, v1.color) * _parameters.vertexColorProtection; } if (val != 0.0f) { cinfo.cost += 0.1f * val * mesh.EdgeLengthSqr(edgeIndex); } }
public void CalculateFaceVertexNormalsFromEdgeCreasesForVertex(int vertexIndex, ref List <int>[] faceLinkedEdges) { int i, j, grp; List <int> grpCornerIndex = new List <int>(); Vertex v = vertices[vertexIndex]; IndexList vertexFaces = v.linkedFaces; int vertexFaceCount = vertexFaces.Count; // List<int> vertexEdges = linkedEdges[vertexIndex]; // Clear face marks around vertex for (j = 0; j < vertexFaceCount; ++j) { faces[vertexFaces[j]].mark = -1; // TODO: could be faster with uniqueTag } // This will add each facemark to a groupIndex int groupIndex = 0; for (j = 0; j < vertexFaceCount; ++j) { int faceIndex = vertexFaces[j]; if (faces[faceIndex].mark == -1) // face still available { _markGroupFaceNeightbours(vertexIndex, vertexFaces[j], ref faceLinkedEdges, groupIndex); groupIndex++; } } // Build group arrays List <int>[] groups = new List <int> [groupIndex]; // are these too many allocations? for (i = 0; i < groupIndex; ++i) { groups[i] = new List <int>(); } for (i = 0; i < vertexFaceCount; ++i) { int faceIndex = vertexFaces[i]; int mark = faces[faceIndex].mark; groups[mark].Add(faceIndex); } // Calculate and set normal for each face on the vertex based on the groups Vector3 normal; for (grp = 0; grp < groupIndex; ++grp) { normal = Vector3.zero; List <int> grpFaces = groups[grp]; int cnt = grpFaces.Count; grpCornerIndex.Clear(); for (i = 0; i < cnt; ++i) { Face f = faces[grpFaces[i]]; if (f.normal == Vector3.zero) { // Debug.Log("face has zero normal .. valid " + f.valid); } // Multiply with corner angle (=SLOW?) int corner = f.CornerIndex(vertexIndex); float fact = CornerAngle(grpFaces[i], corner); normal += f.normal * fact; grpCornerIndex.Add(corner); } UnityUtils.NormalizeSmallVector(ref normal); if (normal == Vector3.zero) { // Debug.Log("NORMAL == ZERO facecount " + cnt); } // Now set the normal to all group faces for (i = 0; i < cnt; ++i) { faces[grpFaces[i]].vertexNormal[grpCornerIndex[i]] = normal; } } }
public static bool CheckMeshIntegrity(MeshEdges mesh) { // Vertex linked faces int numVerts = mesh.vertCount(); int numFaces = mesh.faceCount(); int numEdges = mesh.edgeCount(); List <int>[] mVertexLinkedFaces = new List <int> [numVerts]; for (int i = 0; i < numVerts; ++i) { mVertexLinkedFaces[i] = new List <int>(); } for (int i = 0; i < numFaces; ++i) { Face f = mesh.faces[i]; if (f.valid) { for (int j = 0; j < f.cornerCount; ++j) { int vertIndex = f.v[j]; if (mesh.IsVertexValid(vertIndex)) { mVertexLinkedFaces[vertIndex].Add(i); } } } } for (int i = 0; i < numVerts; ++i) { if (mesh.IsVertexValid(i)) { IndexList test = new IndexList(4); for (int j = 0; j < mesh.vertices[i].linkedFaces.Count; ++j) { if (mesh.faces[mesh.vertices[i].linkedFaces[j]].valid) { test.Add(mesh.vertices[i].linkedFaces[j]); } } /*if (_areIntListsTheSame(mesh.vertices[i].linkedFaces, mVertexLinkedFaces[i]) == false) { * Debug.LogError("INVALID MESH vertexLinkedFaces not correct for vertex " + i); * return false; * }*/ } } // Vertex linked edges for (int i = 0; i < numVerts; ++i) { if (mesh.IsVertexValid(i)) { List <int> li1 = new List <int>(); mesh.CollectVerticesAroundVertex(i, ref li1); List <int> li2 = new List <int>(); List <int> le = mesh.linkedEdgesForVert(i); for (int j = 0; j < le.Count; ++j) { if (mesh.IsEdgeValid(le[j])) { li2.Add(mesh.edges[le[j]].OtherVertex(i)); } } if (_areIntListsTheSame(li1, li2) == false) { Debug.LogError("INVALID MESH vertexLinkedEdges not correct for vertex" + i); return(false); } } } // Edge linked faces for (int i = 0; i < numEdges; ++i) { if (mesh.IsEdgeValid(i)) { IndexList test = new IndexList(18); mesh.CollectVertexPairFaces(mesh.edges[i], test); /* if (_areIntListsTheSame(test, mesh.edges[i).linkedFaces) == false) { * Debug.LogError("INVALID MESH edgeLinkedFaces not correct for edge" + i); * return false; * }*/ } } Debug.Log("Mesh integrity is good."); return(true); }
void _calculateVertexPositions(MeshEdges mesh) { int numVerts = mesh.vertCount(); int numFaces = mesh.faceCount(); int numEdges = mesh.edgeCount(); vertPoints = new Vector3[numVerts]; facePoints = new Vector3[numFaces]; edgePoints = new Vector3[numEdges]; // In any case the new face points are the center of the faces for (int i = 0; i < numFaces; ++i) { facePoints[i] = mesh.CalculateFaceCenter(i); } if (smooth) { // First count the number of creases connected to each vertex int[] vertexNumCreases = new int[numVerts]; for (int i = 0; i < numVerts; ++i) { List <int> linkedEdges = mesh.linkedEdgesForVert(i); for (int j = 0; j < linkedEdges.Count; ++j) { int edgeIndex = linkedEdges[j]; if (mesh.IsEdgeValid(edgeIndex) && mesh.edges[edgeIndex].crease > 0.0) { vertexNumCreases[i]++; } } } // Edge is the average of the two ends and the connected face centers for (int i = 0; i < numEdges; ++i) { Edge e = mesh.edges[i]; IndexList linkedFaces = e.linkedFaces; edgePoints[i] = mesh.CalculateVertexPairCenter(e); // If an edge connects two verts that do not move, use the center to prevent overlaps //bool betweenNonMovableVertices = (vertexNumCreases[e.v[0]] > 2 && vertexNumCreases[e.v[1]] > 2); //if (betweenNonMovableVertices) Debug.Log("Between NONmovable verts"); if (/*betweenNonMovableVertices == false &&*/ e.crease == 0.0f && linkedFaces.Count >= 2) { // creases and borders stay at edge centers // other edges use the edge center + the center of all attached face centers Vector3 faceCenterCenter = facePoints[linkedFaces[0]]; int numLinked = e.linkedFaces.Count; for (int j = 1; j < numLinked; ++j) { faceCenterCenter += facePoints[linkedFaces[j]]; } edgePoints[i] = 0.5f * (edgePoints[i] + faceCenterCenter * (1.0f / ((float)numLinked))); } } // Vert for (int i = 0; i < numVerts; ++i) { Vector3 oldPosition = mesh.vertices[i].coords; List <int> linkedEdges = mesh.linkedEdgesForVert(i); if (_isVertexBorder(mesh, i) == false) { // First deal with edges. the number of creases needs to be known! Vector3 edgesCenter = Vector3.zero; float numCreases = 0.0f; Vector3 creaseCenter = Vector3.zero; float nEdges = 0.0f; for (int j = 0; j < linkedEdges.Count; ++j) { int edgeIndex = linkedEdges[j]; if (mesh.IsEdgeValid(edgeIndex)) { Edge e = mesh.edges[edgeIndex]; Vector3 center = mesh.CalculateVertexPairCenter(e); if (e.crease > 0.0f) { numCreases += 1.0f; creaseCenter += center; } nEdges += 1.0f; edgesCenter += center; } } edgesCenter *= 1.0f / nEdges; // For points without crease edges or just one -> do nothing if (numCreases == 2.0f) { // like a border connection vertPoints[i] = 0.5f * oldPosition + creaseCenter * (0.5f / numCreases); } else if (numCreases > 2.0f) { // A sharp corner vertPoints[i] = oldPosition; } else { // Full formula including faces center which needs to be calculated IndexList linkedFaces = mesh.vertices[i].linkedFaces; Vector3 facesCenter = Vector3.zero; float n = 0.0f; for (int j = 0; j < linkedFaces.Count; ++j) { int faceIndex = linkedFaces[j]; if (mesh.faces[faceIndex].valid) { facesCenter += facePoints[faceIndex]; n += 1.0f; } } float invN = 1.0f / n; vertPoints[i] = (facesCenter * invN + 2.0f * edgesCenter + (n - 3.0f) * oldPosition) * invN; // useless for 2 creases!!! } } else { // Border Vertex. get center of all connected border centers. if (vertexNumCreases[i] > 2) { vertPoints[i] = oldPosition; } else { float nBorders = 0.0f; Vector3 borderCenter = Vector3.zero; for (int j = 0; j < linkedEdges.Count; ++j) { int edgeIndex = linkedEdges[j]; if (mesh.IsEdgeValid(edgeIndex)) { Edge e = mesh.edges[edgeIndex]; if (mesh.IsEdgeBorder(edgeIndex)) { borderCenter += mesh.CalculateVertexPairCenter(e); nBorders += 1.0f; } } } vertPoints[i] = 0.5f * oldPosition + borderCenter * (0.5f / nBorders); } } } } else { // Subdivision without smoothing for (int i = 0; i < numVerts; ++i) { vertPoints[i] = mesh.vertices[i].coords; } for (int i = 0; i < numEdges; ++i) { edgePoints[i] = mesh.CalculateVertexPairCenter(mesh.edges[i]); } } }