public Vector3 CalculateFaceNormal(int faceIndex) { int i; Face face = faces[faceIndex]; int vertCount = face.cornerCount; int[] vindex = face.v; Vector3[] vec = new Vector3[vertCount + 1]; for (i = 0; i < vertCount - 1; ++i) { vec[i] = vertices[vindex[i + 1]].coords - vertices[vindex[i]].coords; } vec[i] = vertices[vindex[0]].coords - vertices[vindex[i]].coords; Vector3 normal = Vector3.zero; if (vertCount == 3) { normal = Vector3.Cross(vec[0], vec[1]); } else { // Sum normals based on consequtive pairs of edges vec[vertCount] = vec[0]; for (i = 0; i < vertCount; ++i) { normal += Vector3.Cross(vec[i], vec[i + 1]); } } UnityUtils.NormalizeSmallVector(ref normal); face.normal = normal; return(face.normal); }
void _determinePdeToConstrainEdge(Edge edge, float weight, ref PlaneDistanceError pde) { int vi0 = edge.v[0]; int vi1 = edge.v[1]; Vector3 v0 = mesh.vertices[vi0].coords; Vector3 vEdge = mesh.vertices[vi1].coords - v0; Vector3 edgeNormal; pde.Clear(); float mag = vEdge.sqrMagnitude; for (int i = 0; i < edge.linkedFaces.Count; ++i) { edgeNormal = mesh.faces[edge.linkedFaces[i]].normal; Vector3 n = Vector3.Cross(vEdge, edgeNormal); // normal to edge and face UnityUtils.NormalizeSmallVector(ref n); float d = -Vector3.Dot(n, v0); pde2.Set(n.x, n.y, n.z, d, mag); // Multiply by face area (factor) for weighting pde2.OpMul(pde2.Factor() * weight * 0.5f); pde.OpAdd(pde2); } }
public Vector3 CalculateVertexNormal(int vertexIndex) { Vertex vert = vertices[vertexIndex]; Vector3 n = Vector3.zero; IndexList linkedFaces = vert.linkedFaces; int num = linkedFaces.Count; for (int i = 0; i < num; ++i) { int faceIndex = linkedFaces[i]; n += faces[faceIndex].normal; } UnityUtils.NormalizeSmallVector(ref n); vert.normal = n; return(n); }
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); }
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 static int CompareEV(ExportVertex a, ExportVertex b) { int res = UnityUtils.Vector3Compare(a.coords, b.coords); if (res != 0) { return(res); } res = UnityUtils.Vector3Compare(a.normal, b.normal); if (res != 0) { return(res); } res = UnityUtils.Vector2Compare(a.uv1, b.uv1); if (res != 0) { return(res); } res = UnityUtils.Vector2Compare(a.uv2, b.uv2); return(res); }
// 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); }
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; } } }