/// <summary> /// Generates an index mapping with the specified vertex elements. /// If no vertex elements are specified, use the whole vertex. /// </summary> /// <param name="vertexBufferBinding">The vertex buffer binding.</param> /// <param name="usages">The vertex element usages to consider.</param> /// <returns></returns> public static unsafe IndexMappingResult GenerateIndexMapping(this VertexBufferBinding vertexBufferBinding, CommandList commandList, params string[] usages) { var bufferData = vertexBufferBinding.Buffer.GetDataSafe(commandList); var vertexStride = vertexBufferBinding.Declaration.VertexStride; var vertexCount = vertexBufferBinding.Count; var activeBytes = stackalloc byte[vertexStride]; var vertexMapping = new List <int>(); var indexMapping = new int[vertexCount]; var mapping = new Dictionary <VertexKey, int>(new VertexKeyEqualityComparer(activeBytes, vertexStride)); // Create a "mask" of part of the vertices that will be used // TODO: Use bit packing? for (int i = 0; i < vertexBufferBinding.Declaration.VertexStride; ++i) { activeBytes[i] = (byte)(usages.Length == 0 ? 1 : 0); } foreach (var vertexElement in vertexBufferBinding.Declaration.EnumerateWithOffsets()) { if (usages.Contains(vertexElement.VertexElement.SemanticAsText)) { for (int i = 0; i < vertexElement.Size; ++i) { activeBytes[vertexElement.Offset + i] = 1; } } } // Generate index buffer fixed(byte *bufferPointerStart = &bufferData[vertexBufferBinding.Offset]) { var bufferPointer = bufferPointerStart; for (int i = 0; i < vertexCount; ++i) { // Create VertexKey (will generate hash) var vertexKey = new VertexKey(bufferPointer, activeBytes, vertexStride); // Get or create new index int currentIndex; if (!mapping.TryGetValue(vertexKey, out currentIndex)) { currentIndex = vertexMapping.Count; mapping.Add(vertexKey, currentIndex); vertexMapping.Add(i); } // Assign index in result buffer indexMapping[i] = currentIndex; bufferPointer += vertexStride; } } return(new IndexMappingResult { Vertices = vertexMapping.ToArray(), Indices = indexMapping }); }
private static VertexKey GetFacePointKey(int si, int fi) { // Bit format: [face index *23][submesh index *6][1] VertexKey key = 0; key ^= (VertexKey)fi; key <<= keyBitsSubmeshes; key ^= (VertexKey)si; key <<= 1; key |= 1; // lowest bit 1 for face point key return(key); }
public static IndexEdge listFind(EdgesType edges, VertexKey key) { foreach (IndexEdge ele in edges) { if (ele.targetID == key) { return(ele); } } return(null); }
private static VertexKey GetEdgePointKey(int si, int fi, int ei) { // Bit format: [face index *23][submesh index *6][face edge index *2][0] VertexKey key = 0; key ^= (VertexKey)fi; key <<= keyBitsSubmeshes; key ^= (VertexKey)si; key <<= 2; key ^= (VertexKey)ei; key <<= 1; key |= 0; // lowest bit 0 for edge point key return(key); }
public void deleteEdge(VertexKey ownerID, VertexKey targetID) { IndexEdge it = listFind(edgeData[ownerID], targetID); if (it == null) { //do nothig } else { edgeData[ownerID].Remove(it); } }
public void TransformVertices(Func <T, T> transformFunc) { _VertexCache.Clear(); for (int i = 0; i < _Vertices.Count; ++i) { _Vertices[i] = transformFunc(_Vertices[i]); var key = new VertexKey(_Vertices, i); _VertexCache[key] = i; } }
public int Use(T v) { _VertexProbe[0] = v; if (_VertexCache.TryGetValue(new VertexKey(_VertexProbe, 0), out int index)) { System.Diagnostics.Debug.Assert(Object.Equals(v, _Vertices[index]), "Vertex equality failed"); return(index); } index = _Vertices.Count; _Vertices.Add(v); var key = new VertexKey(_Vertices, index); _VertexCache[key] = index; return(index); }
public bool Dijkstra(VertexKey origin, out List <WeightType> dist, out List <VertexKey> predecessor) { //最短距离估计dist优先队列 每个顶点入队一次 松弛一次 //static priority_queue<IndexEdge> q; //C# 不允许使用 static 修饰符来声明方法内部的变量。SortedSet PriorityQueue <IndexEdge> q = new PriorityQueue <IndexEdge>(); VertexKey v = -1; //dist.assign(vertexNum, INF); initList(out dist, vertexNum, int.MaxValue); //predecessor.assign(vertexNum, -1); initList(out predecessor, vertexNum, -1); dist[origin] = 0; //q.push({ origin, dist[origin] });//{targetID, weight} q.Push(new IndexEdge(origin, dist[origin])); //核心算法 while (q.Count != 0) { //v = q.top().targetID; q.pop(); v = q.Pop().targetID; LinkedList <IndexEdge> edges = getEdgeList(v); //对图中由v出发的每条边<v, w>的顶点w进行拓展松弛操作 foreach (IndexEdge element in edges) { //松弛操作: 先松弛过的顶点不会被后松弛的优化 若还能只可能是负权边的情况 if (dist[v] + element.weight < dist[element.targetID]) { if (element.weight < 0) { return(false); //错误:有负权边 } dist[element.targetID] = dist[v] + element.weight; //更新w的最短路径距离估计 predecessor[element.targetID] = v; //更新w的最短路径前驱结点 q.Push(new IndexEdge(element.targetID, dist[element.targetID])); } } } return(true); }
public static void RecalculateNormals(Mesh mesh, float angle) { var triangles = mesh.triangles; var vertices = mesh.vertices; var weights = mesh.boneWeights == null || mesh.boneWeights.Length == 0 ? mesh.vertices.Select(v => new BoneWeight()).ToArray() : mesh.boneWeights; var triNormals = new Vector3[triangles.Length / 3]; var normals = new Vector3[vertices.Length]; angle = angle * Mathf.Deg2Rad; var dictionary = new Dictionary <VertexKey, VertexEntry>(vertices.Length); for (var i = 0; i < triangles.Length; i += 3) { var i1 = triangles[i]; var i2 = triangles[i + 1]; var i3 = triangles[i + 2]; var p1 = vertices[i2] - vertices[i1]; var p2 = vertices[i3] - vertices[i1]; var normal = Vector3.Cross(p1, p2).normalized; int triIndex = i / 3; triNormals[triIndex] = normal; VertexEntry entry; VertexKey key; if (!dictionary.TryGetValue(key = new VertexKey(vertices[i1], weights[i1]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i1, triIndex); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i2], weights[i2]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i2, triIndex); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i3], weights[i3]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i3, triIndex); } foreach (var value in dictionary.Values) { for (var i = 0; i < value.Count; ++i) { var sum = new Vector3(); for (var j = 0; j < value.Count; ++j) { if (value.VertexIndex[i] == value.VertexIndex[j]) { sum += triNormals[value.TriangleIndex[j]]; } else { float dot = Vector3.Dot( triNormals[value.TriangleIndex[i]], triNormals[value.TriangleIndex[j]]); dot = Mathf.Clamp(dot, -0.99999f, 0.99999f); float acos = Mathf.Acos(dot); if (acos <= angle) { sum += triNormals[value.TriangleIndex[j]]; } } } normals[value.VertexIndex[i]] = sum.normalized; } } mesh.normals = normals; }
public static MeshX Subdivide(MeshX mesh, Options options) { if (!mesh.helpersInited) { mesh.InitHelpers(); } if (!mesh.normalPerPosition) { mesh.MakeNormalPerPosition(); } MeshX newMesh = new MeshX { name = mesh.name + "/s", content = mesh.content, normalPerPosition = true, }; newMesh.StartBuilding(); CheckForVertexKeys(mesh); var newVertices = new MeshVertices { mesh = newMesh }; var edgeMidPositions = new Dictionary <VertexKey, Vertex>(); // position & normal // reserve indices for new control-points: they keep indices and we need no special keys for them. // later for control-points we set position & normal only: tangent & uv stay the same. for (int pi = 0; pi < mesh.positions.Count; ++pi) { newMesh.AddPosition(default(Vertex)); } for (int vi = 0; vi < mesh.vertexCount; ++vi) { Vertex v = mesh.GetVertex(vi, maskVertex); newMesh.AddVertex(v, addPosition: false); } // add face-points // "for each face, a face point is created which is the average of all the points of the face." foreach (Face f in mesh.IterAllFaces()) { Vertex[] vs = mesh.GetVertices(mesh.GetFaceVertexIndices(f)); Vertex v = mesh.Average(vs); VertexKey keyF = GetFacePointKey(f); newVertices.AddVertices(v, Pair(v, keyF)); } // add edge-points foreach (Edge e in mesh.IterAllEdges()) { EdgeType type = mesh.GetEdgeType(e); if (type == EdgeType.back) { continue; } if (type == EdgeType.boundary) { // "for the edges that are on the border of a hole, the edge point is just the middle of the edge." Vertex midE = mesh.Average( mesh.GetVertices(mesh.GetEdgeVertexIndices(e)) ); VertexKey keyE = GetEdgePointKey(e); edgeMidPositions[keyE] = midE; newVertices.AddVertices(midE, Pair(midE, keyE)); } else if (type == EdgeType.solid) { // "for each edge, an edge point is created which is the average between the center of the edge // and the center of the segment made with the face points of the two adjacent faces." Edge n = mesh.GetNeighbor(e); Vertex midE = mesh.Average( mesh.GetVertices(mesh.GetEdgeVertexIndices(e)) ); VertexKey keyE = GetEdgePointKey(e); edgeMidPositions[keyE] = midE; Vertex v = mesh.Average( new[] { midE, newVertices.GetVertex(GetFacePointKey(e.face)), newVertices.GetVertex(GetFacePointKey(n.face)), }, weights: new[] { 2f, 1f, 1f } ); newVertices.AddVertices(v, Pair(v, keyE)); } else // seam edge { Edge n = mesh.GetNeighbor(e); Vertex midE = mesh.Average( mesh.GetVertices(mesh.GetEdgeVertexIndices(e)) ); Vertex midN = mesh.Average( mesh.GetVertices(mesh.GetEdgeVertexIndices(n)), maskVertex // pos & normal already got in midE ); VertexKey keyE = GetEdgePointKey(e); VertexKey keyN = GetEdgePointKey(n); edgeMidPositions[keyE] = midE; Vertex p = mesh.Average( // pos & normal only new[] { midE, newVertices.GetVertex(GetFacePointKey(e.face), maskPosition), newVertices.GetVertex(GetFacePointKey(n.face), maskPosition), }, maskPosition, new[] { 2f, 1f, 1f } ); newVertices.AddVertices(p, Pair(midE, keyE), Pair(midN, keyN)); } } // move control-points for (int pi = 0; pi < mesh.positions.Count; ++pi) { // count edges var edges = new List <Edge>(); // edges outcoming from the position var boundaries = new List <Edge>(); var front = new List <Edge>(); foreach (int vi in mesh.positionVertices[pi]) { foreach (Edge e in mesh.vertexEdges[vi]) { edges.Add(e); foreach (Edge edge in new[] { e, mesh.GetNextInFace(e, -1) }) { EdgeType type = mesh.GetEdgeType(edge); if (type == EdgeType.boundary) { boundaries.Add(edge); } else if (type != EdgeType.back) { front.Add(edge); } } } } Debug.AssertFormat(boundaries.Count > 0 || (edges.Count == front.Count), "Counting edges error: boundaries: {0}, edges {1}, front: {2}", boundaries.Count, edges.Count, front.Count); Vertex controlPoint; if (boundaries.Count > 0) { bool isCorner = edges.Count == 1; if (options.boundaryInterpolation == Options.BoundaryInterpolation.fixBoundaries || options.boundaryInterpolation == Options.BoundaryInterpolation.fixCorners && isCorner) { controlPoint = mesh.GetPosition(pi); // keep same position } else { // "for the vertex points that are on the border of a hole, the new coordinates are calculated as follows: // 1. in all the edges the point belongs to, only take in account the middles of the edges that are on the border of the hole // 2. calculate the average between these points (on the hole boundary) and the old coordinates (also on the hole boundary)." Vertex[] vs = new Vertex[boundaries.Count + 1]; vs[0] = mesh.GetPosition(pi); for (int e = 0; e < boundaries.Count; ++e) { vs[e + 1] = edgeMidPositions[GetEdgePointKey(boundaries[e])]; } controlPoint = mesh.Average(vs, maskPosition); } } else { // "for each vertex point, its coordinates are updated from (new_coords): // the old coordinates (P), // the average of the face points of the faces the point belongs to (F), // the average of the centers of edges the point belongs to (R), // how many faces a point belongs to (n), then use this formula: // (F + 2R + (n-3)P) / n" // edge-midpoints Vertex[] ms = new Vertex[front.Count]; for (int e = 0; e < front.Count; ++e) { ms[e] = edgeMidPositions[GetEdgePointKey(front[e])]; } Vertex edgeMidAverage = mesh.Average(ms, maskPosition); // face-points Vertex[] fs = new Vertex[edges.Count]; for (int e = 0; e < edges.Count; ++e) { fs[e] = newVertices.GetVertex(GetFacePointKey(edges[e].face), maskPosition); } Vertex faceAverage = mesh.Average(fs, maskPosition); // new control-point controlPoint = mesh.Average( new[] { faceAverage, edgeMidAverage, mesh.GetPosition(pi) }, maskPosition, new[] { 1f, 2f, edges.Count - 3f } ); } // set moved control-point position to reserved index newMesh.SetPosition(pi, controlPoint); } // add 4 new quads per face // eis[i] // fis[i].----.----.fis[i+1] // | | // eis[i-1]. ci. . // | | // .____.____. newMesh.submeshes = new Submesh[mesh.submeshes.Length]; for (int si = 0; si < mesh.submeshes.Length; ++si) { int[][] faces = mesh.submeshes[si].faces; // get new face count int faceCount = 0; foreach (int[] face in faces) { faceCount += face.Length; } newMesh.submeshes[si].faces = new int[faceCount][]; // fill faces int faceIndex = 0; for (int fi = 0; fi < faces.Length; ++fi) { int[] fis = faces[fi]; int edgeCount = fis.Length; // 3 or 4 // Face f = new Face { submesh = si, index = fi }; int ci = newVertices.vertexIndices[GetFacePointKey(f)]; // face-point index // int[] eis = new int[edgeCount]; // edge-point indices for (int i = 0; i < edgeCount; ++i) { Edge e = new Edge { face = f, index = i }; // get neighbor for solid back edges if (mesh.GetEdgeType(e) == EdgeType.back) //!!! here should be some EdgeType.backOfSeam or EdgeType.backOfSolid { Edge n = mesh.GetNeighbor(e); if (mesh.GetEdgeType(n) == EdgeType.solid) { e = n; } } eis[i] = newVertices.vertexIndices[GetEdgePointKey(e)]; } // for (int i = 0; i < edgeCount; ++i) { int[] q = new int[4]; // new faces are always quads int s = edgeCount == 4 ? i : 0; // shift indices (+ i) to keep quad orientation q[(0 + s) % 4] = fis[i]; q[(1 + s) % 4] = eis[i]; q[(2 + s) % 4] = ci; q[(3 + s) % 4] = eis[(i - 1 + edgeCount) % edgeCount]; newMesh.submeshes[si].faces[faceIndex++] = q; } } } newMesh.FinishBuilding(); return(newMesh); }
private static MeshVertices.VertexKeyPair Pair(Vertex v, VertexKey k) // new VertexKeyPair shortcut { return(new MeshVertices.VertexKeyPair { vertex = v, key = k }); }
public Vertex GetVertex(VertexKey key, VertexContent mask = VertexContent.full) { return(mesh.GetVertex(vertexIndices[key], mask)); }
/// <summary> /// Recalculate the normals of a mesh based on an angle threshold. This takes /// into account distinct vertices that have the same position. /// </summary> /// <param name="mesh"></param> /// <param name="angleSameMaterial"> /// The smoothing angle. Note that triangles that already share the same vertex will be smooth regardless of the angle! /// If the vertex is not shared then triangle with normals within this angle and with the same material, are smoothed /// </param> /// <param name="angleDifferentMaterial"> /// See angleSameMaterial. But this is if the triangles are of different materials. /// </param> /// <param name="normalLock"> /// If provided, then for each normal in normals[], this says whether or not the vertex for this normal should /// be locked to that provided by normals[]. Otherwise, if false, that normal will be recalculated. /// </param> /// <param name="providedNormals"> /// Paired with normalLock to decide whether we should calculate the normal /// </param> public static void RecalculateNormals(this Mesh mesh, float angleSameMaterial, float angleDifferentMaterial, bool[] normalLock, Vector3[] providedNormals) { const float kEpsilon = 0.0001f; bool warnedAboutDegenTris = false; bool warnedAboutZeroSumNormal = false; angleSameMaterial = angleSameMaterial * Mathf.Deg2Rad; angleDifferentMaterial = angleDifferentMaterial * Mathf.Deg2Rad; if (providedNormals == null) { // If we were not given provided normals, then we don't do normal lock checking normalLock = null; } var vertices = mesh.vertices; var normals = new Vector3[vertices.Length]; var pointInSpaceToRefTrisDictionary = new Dictionary <VertexKey, VertexEntry>(vertices.Length); if (normalLock != null) { // Make sure all the arrays are the expected length Assert.True(normalLock.Length == providedNormals.Length); Assert.True(normalLock.Length == vertices.Length); } int numSubmeshes = mesh.subMeshCount; int numTotalTriangles = 0; for (int submeshIndex = 0; submeshIndex < numSubmeshes; ++submeshIndex) { int[] triangles = mesh.GetTriangles(submeshIndex); numTotalTriangles += triangles.Length / 3; } var triNormals = new Vector3[numTotalTriangles]; //Holds the normal of each triangle int triOffset = 0; for (int submesh_index = 0; submesh_index < numSubmeshes; ++submesh_index) { int[] triangles = mesh.GetTriangles(submesh_index); // Goes through all the triangles and gathers up data to be used later for (var i = 0; i < triangles.Length; i += 3) { int i1 = triangles[i + 0]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; int i1_tri_id = 0; int i2_tri_id = 0; int i3_tri_id = 0; //Calculate the normal of the triangle Vector3 p1 = vertices[i2] - vertices[i1]; Vector3 p2 = vertices[i3] - vertices[i1]; Vector3 normal = Vector3.Cross(p1, p2).normalized; if (normal.magnitude < kEpsilon) { // Prevent a bad triangle from generating a bad normal if (!warnedAboutDegenTris) { warnedAboutDegenTris = true; Debug.LogWarningFormat("Degenerate triangles found during RecalculateNormals ({0})", mesh.name); } normal = Vector3.forward; } int triIndex = (i / 3) + triOffset; triNormals[triIndex] = normal; VertexEntry entry; VertexKey key; // For our purposes, each submesh is for a different material // But if we should ever have a material represented multiple times // in submeshes, then we need to adjust this as needed. int materialIndex = submesh_index; //For each of the three points of the triangle // Add this triangle as part of the triangles they're connected to. // NOTE!!!! We pass false here for triangleIsPortal, because we assume that by the time this gets called, there are no portal triangles in the mesh. // This is not necessarily the case if debugAddPortalsToRenderGeometry is true. If we want to support that, we will need some way to feed the flag // of whether each triangle is from a portal or not through the UnityEngine.Mesh and into this function somehow. -MFlavin 3/1/2017 if (!pointInSpaceToRefTrisDictionary.TryGetValue(key = new VertexKey(vertices[i1]), out entry)) { entry = new VertexEntry(); pointInSpaceToRefTrisDictionary.Add(key, entry); } entry.Add(i1, i1_tri_id, triIndex, materialIndex, false); if (!pointInSpaceToRefTrisDictionary.TryGetValue(key = new VertexKey(vertices[i2]), out entry)) { entry = new VertexEntry(); pointInSpaceToRefTrisDictionary.Add(key, entry); } entry.Add(i2, i2_tri_id, triIndex, materialIndex, false); if (!pointInSpaceToRefTrisDictionary.TryGetValue(key = new VertexKey(vertices[i3]), out entry)) { entry = new VertexEntry(); pointInSpaceToRefTrisDictionary.Add(key, entry); } entry.Add(i3, i3_tri_id, triIndex, materialIndex, false); } triOffset += triangles.Length / 3; } //Foreach point in space (not necessarily the same vertex index!) //{ // Foreach triangle T1 that point belongs to // { // Foreach other triangle T2 (including self) that point belongs to and that // meets any of the following: // 1) The corresponding vertex is actually the same vertex // 2) The angle between the two triangles is less than the smoothing angle // { // > Add to temporary Vector3 // } // > Normalize temporary Vector3 to find the average // > Assign the normal to corresponding vertex of T1 // } //} foreach (var refTriangles in pointInSpaceToRefTrisDictionary.Values) { for (var i = 0; i < refTriangles.Count; ++i) { var sum = new Vector3(); int materialI = refTriangles.MaterialIndex[i]; int vertexIndexI = refTriangles.VertexIndex[i]; int triangleIndexI = refTriangles.TriangleIndex[i]; for (var j = 0; j < refTriangles.Count; ++j) { int materialJ = refTriangles.MaterialIndex[j]; int vertexIndexJ = refTriangles.VertexIndex[j]; int triangleIndexJ = refTriangles.TriangleIndex[j]; if (vertexIndexI == vertexIndexJ) { // This is the same vertex, it is going to be shared no matter what because // the indices point to the same normal. sum += triNormals[triangleIndexJ]; } else { float dot = Vector3.Dot(triNormals[triangleIndexI], triNormals[triangleIndexJ]); dot = Mathf.Clamp(dot, -0.99999f, 0.99999f); float acos = Mathf.Acos(dot); float smoothingAngle = (materialI == materialJ) ? angleSameMaterial : angleDifferentMaterial; if (acos <= smoothingAngle) { // Within the angle, factor it in sum += triNormals[triangleIndexJ]; } } } if (sum.magnitude <= kEpsilon) { // Prevent a bad normal from being created if (!warnedAboutZeroSumNormal) { warnedAboutZeroSumNormal = true; Debug.LogWarningFormat("Zero-sum normal found during RecalculateNormals ({0})", mesh.name); } sum = Vector3.forward; } if (normalLock != null && normalLock[vertexIndexI]) { // we must take the provided normal normals[vertexIndexI] = providedNormals[vertexIndexI]; } else { normals[vertexIndexI] = sum.normalized; } } } mesh.normals = normals; }
public LinkedList <IndexEdge> getEdgeList(VertexKey v) { return(edgeData[v]); }
public static void RecalculateNormals(this Mesh mesh, float angle) { var triangles = mesh.GetTriangles(0); var vertices = mesh.vertices; var triNormals = new Vector3[triangles.Length / 3]; //Holds the normal of each triangle var normals = new Vector3[vertices.Length]; angle = angle * Mathf.Deg2Rad; var dictionary = new Dictionary<VertexKey, VertexEntry>(vertices.Length); //Goes through all the triangles and gathers up data to be used later for (var i = 0; i < triangles.Length; i += 3) { int i1 = triangles[i]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; //Calculate the normal of the triangle Vector3 p1 = vertices[i2] - vertices[i1]; Vector3 p2 = vertices[i3] - vertices[i1]; Vector3 normal = Vector3.Cross(p1, p2).normalized; int triIndex = i / 3; triNormals[triIndex] = normal; VertexEntry entry; VertexKey key; //For each of the three points of the triangle // > Add this triangle as part of the triangles they're connected to. if (!dictionary.TryGetValue(key = new VertexKey(vertices[i1]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i1, triIndex); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i2]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i2, triIndex); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i3]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i3, triIndex); } //Foreach point in space (not necessarily the same vertex index!) //{ // Foreach triangle T1 that point belongs to // { // Foreach other triangle T2 (including self) that point belongs to and that // meets any of the following: // 1) The corresponding vertex is actually the same vertex // 2) The angle between the two triangles is less than the smoothing angle // { // > Add to temporary Vector3 // } // > Normalize temporary Vector3 to find the average // > Assign the normal to corresponding vertex of T1 // } //} foreach (var value in dictionary.Values) { for (var i = 0; i < value.Count; ++i) { var sum = new Vector3(); for (var j = 0; j < value.Count; ++j) { if (value.VertexIndex[i] == value.VertexIndex[j]) { sum += triNormals[value.TriangleIndex[j]]; } else { float dot = Vector3.Dot( triNormals[value.TriangleIndex[i]], triNormals[value.TriangleIndex[j]]); dot = Mathf.Clamp(dot, -0.999999f, 0.999999f); float acos = Mathf.Acos(dot); if (acos <= angle) { sum += triNormals[value.TriangleIndex[j]]; } } } normals[value.VertexIndex[i]] = sum.normalized; } } mesh.normals = normals; }
/// <summary> /// 根据角度阈值平滑网格法线,此算法会将相同位置的不同顶点进行平滑(Unity 自带的不平滑) /// </summary> public static Vector3[] RecalculateNormals(this Mesh mesh, float angle) { var cosineThreshold = Mathf.Cos(angle * Mathf.Deg2Rad); var vertices = mesh.vertices; var normals = new Vector3[vertices.Length]; var triNormals = new Vector3[mesh.subMeshCount][]; var dictionary = new Dictionary <VertexKey, List <VertexEntry> >(vertices.Length); for (var subMeshIndex = 0; subMeshIndex < mesh.subMeshCount; ++subMeshIndex) { var triangles = mesh.GetTriangles(subMeshIndex); triNormals[subMeshIndex] = new Vector3[triangles.Length / 3]; for (var i = 0; i < triangles.Length; i += 3) { int i1 = triangles[i]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; Vector3 p1 = vertices[i2] - vertices[i1]; Vector3 p2 = vertices[i3] - vertices[i1]; Vector3 normal = Vector3.Cross(p1, p2).normalized; int triIndex = i / 3; triNormals[subMeshIndex][triIndex] = normal; List <VertexEntry> entry; VertexKey key; if (!dictionary.TryGetValue(key = new VertexKey(vertices[i1]), out entry)) { entry = new List <VertexEntry>(4); dictionary.Add(key, entry); } entry.Add(new VertexEntry(subMeshIndex, triIndex, i1)); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i2]), out entry)) { entry = new List <VertexEntry>(); dictionary.Add(key, entry); } entry.Add(new VertexEntry(subMeshIndex, triIndex, i2)); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i3]), out entry)) { entry = new List <VertexEntry>(); dictionary.Add(key, entry); } entry.Add(new VertexEntry(subMeshIndex, triIndex, i3)); } } foreach (var vertList in dictionary.Values) { for (var i = 0; i < vertList.Count; ++i) { var sum = new Vector3(); var lhsEntry = vertList[i]; for (var j = 0; j < vertList.Count; ++j) { var rhsEntry = vertList[j]; if (lhsEntry.VertexIndex == rhsEntry.VertexIndex) { sum += triNormals[rhsEntry.MeshIndex][rhsEntry.TriangleIndex]; } else { var dot = Vector3.Dot( triNormals[lhsEntry.MeshIndex][lhsEntry.TriangleIndex], triNormals[rhsEntry.MeshIndex][rhsEntry.TriangleIndex]); if (dot >= cosineThreshold) { sum += triNormals[rhsEntry.MeshIndex][rhsEntry.TriangleIndex]; } } } normals[lhsEntry.VertexIndex] = sum.normalized; } } return(normals); }
/// <summary> /// Recalculate the normals of a mesh based on an angle threshold. This takes /// into account distinct vertices that have the same position. /// </summary> /// <param name="mesh"></param> /// <param name="angle"> /// The smoothing angle. Note that triangles that already share /// the same vertex will be smooth regardless of the angle! /// </param> public static void RecalculateNormals(this Mesh mesh, float angle) { var cosineThreshold = Mathf.Cos(angle * Mathf.Deg2Rad); var vertices = mesh.vertices; var normals = new Vector3[vertices.Length]; // Holds the normal of each triangle in each sub mesh. var triNormals = new Vector3[mesh.subMeshCount][]; var dictionary = new Dictionary <VertexKey, List <VertexEntry> >(vertices.Length); for (var subMeshIndex = 0; subMeshIndex < mesh.subMeshCount; ++subMeshIndex) { var triangles = mesh.GetTriangles(subMeshIndex); triNormals[subMeshIndex] = new Vector3[triangles.Length / 3]; for (var i = 0; i < triangles.Length; i += 3) { int i1 = triangles[i]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; // Calculate the normal of the triangle Vector3 p1 = vertices[i2] - vertices[i1]; Vector3 p2 = vertices[i3] - vertices[i1]; Vector3 normal = Vector3.Cross(p1, p2).normalized; int triIndex = i / 3; triNormals[subMeshIndex][triIndex] = normal; List <VertexEntry> entry; VertexKey key; if (!dictionary.TryGetValue(key = new VertexKey(vertices[i1]), out entry)) { entry = new List <VertexEntry>(4); dictionary.Add(key, entry); } entry.Add(new VertexEntry(subMeshIndex, triIndex, i1)); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i2]), out entry)) { entry = new List <VertexEntry>(); dictionary.Add(key, entry); } entry.Add(new VertexEntry(subMeshIndex, triIndex, i2)); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i3]), out entry)) { entry = new List <VertexEntry>(); dictionary.Add(key, entry); } entry.Add(new VertexEntry(subMeshIndex, triIndex, i3)); } } // Each entry in the dictionary represents a unique vertex position. foreach (var vertList in dictionary.Values) { for (var i = 0; i < vertList.Count; ++i) { var sum = new Vector3(); var lhsEntry = vertList[i]; for (var j = 0; j < vertList.Count; ++j) { var rhsEntry = vertList[j]; if (lhsEntry.VertexIndex == rhsEntry.VertexIndex) { sum += triNormals[rhsEntry.MeshIndex][rhsEntry.TriangleIndex]; } else { // The dot product is the cosine of the angle between the two triangles. // A larger cosine means a smaller angle. var dot = Vector3.Dot( triNormals[lhsEntry.MeshIndex][lhsEntry.TriangleIndex], triNormals[rhsEntry.MeshIndex][rhsEntry.TriangleIndex]); if (dot >= cosineThreshold) { sum += triNormals[rhsEntry.MeshIndex][rhsEntry.TriangleIndex]; } } } normals[lhsEntry.VertexIndex] = sum.normalized; } } mesh.normals = normals; }
public void ToMesh(Mesh mesh, Facing facing = Facing.Normal, GlobalSurfaceType surfaceType = GlobalSurfaceType.Normal) { var indexByVertexKey = new Dictionary <Vertex, SortedDictionary <VertexKey, int> >(); var trigs = new List <int>(); var verts = new List <Vector3>(); var norms = new List <Vector3>(); var meshuv = new List <Vector2>(); var vertexKeyComparer = new VertexKeyComparer(); int MeshVertex(Halfedge e, Vector3 normal) { if (!indexByVertexKey.TryGetValue(e.vertex, out SortedDictionary <VertexKey, int> indices)) { indices = new SortedDictionary <VertexKey, int>(vertexKeyComparer); indexByVertexKey[e.vertex] = indices; } VertexKey key = new VertexKey(normal, GetUV(e)); if (!indices.TryGetValue(key, out int index)) { index = verts.Count; verts.Add(e.vertex.p); norms.Add(normal); meshuv.Add(GetUV(e)); indices.Add(key, index); } return(index); } int MeshVertexEdgeNormal(Halfedge e) { return(MeshVertex(e, GetNormal(e))); } if (surfaceType == GlobalSurfaceType.AutoSmoothAll || surfaceType == GlobalSurfaceType.SplitTriangleAll) { // No vertex duplication. foreach (Face f in faces) { List <Halfedge> edges = f.edges; int p1 = 0; int p2 = edges.Count - 1; while (p2 - p1 > 1) { trigs.Add(edges[p1].vertex.index); trigs.Add(edges[p1 + 1].vertex.index); trigs.Add(edges[p2].vertex.index); p1++; if (p2 - p1 <= 1) { break; } trigs.Add(edges[p1].vertex.index); trigs.Add(edges[p2 - 1].vertex.index); trigs.Add(edges[p2].vertex.index); p2--; } } mesh.vertices = vertices.Select(v => v.p).ToArray(); mesh.triangles = trigs.ToArray(); } else { foreach (Face f in faces) { FaceType faceType = GetFaceType(f); List <Halfedge> edges = f.edges; if (surfaceType == GlobalSurfaceType.HardPolygonAll || faceType == FaceType.Polygonal) { Vector3 normal = f.CalculateNormal(); int p1 = 0; int p2 = edges.Count - 1; while (p2 - p1 > 1) { trigs.Add(MeshVertex(edges[p1], normal)); trigs.Add(MeshVertex(edges[p1 + 1], normal)); trigs.Add(MeshVertex(edges[p2], normal)); p1++; if (p2 - p1 <= 1) { break; } trigs.Add(MeshVertex(edges[p1], normal)); trigs.Add(MeshVertex(edges[p2 - 1], normal)); trigs.Add(MeshVertex(edges[p2], normal)); p2--; } } else if (surfaceType == GlobalSurfaceType.HardTriangleAll || faceType == FaceType.Triangular) { int p1 = 0; int p2 = edges.Count - 1; while (p2 - p1 > 1) { Vector3 normal1 = Vector3.Cross(edges[p1 + 1].vertex.p - edges[p1].vertex.p, edges[p2].vertex.p - edges[p1].vertex.p).normalized; trigs.Add(MeshVertex(edges[p1], normal1)); trigs.Add(MeshVertex(edges[p1 + 1], normal1)); trigs.Add(MeshVertex(edges[p2], normal1)); p1++; if (p2 - p1 <= 1) { break; } Vector3 normal2 = Vector3.Cross(edges[p2 - 1].vertex.p - edges[p1].vertex.p, edges[p2].vertex.p - edges[p1].vertex.p).normalized; trigs.Add(MeshVertex(edges[p1], normal2)); trigs.Add(MeshVertex(edges[p2 - 1], normal2)); trigs.Add(MeshVertex(edges[p2], normal2)); p2--; } } else if (faceType == FaceType.Smooth) { int p1 = 0; int p2 = edges.Count - 1; while (p2 - p1 > 1) { trigs.Add(MeshVertexEdgeNormal(edges[p1])); trigs.Add(MeshVertexEdgeNormal(edges[p1 + 1])); trigs.Add(MeshVertexEdgeNormal(edges[p2])); p1++; if (p2 - p1 <= 1) { break; } trigs.Add(MeshVertexEdgeNormal(edges[p1])); trigs.Add(MeshVertexEdgeNormal(edges[p2 - 1])); trigs.Add(MeshVertexEdgeNormal(edges[p2])); p2--; } } else if (faceType == FaceType.Directinal1 || faceType == FaceType.Directinal2) { if (edges.Count > 4) { throw new Exception("Directional smooth can only apply to quad or triangle faces!"); } Vector3[] localNormals = edges.Select((e, i) => CalculateDirectionalNormal(e, faceType, i, edges.Count)).ToArray(); if (edges.Count == 4) { trigs.Add(MeshVertex(edges[0], localNormals[0])); trigs.Add(MeshVertex(edges[1], localNormals[1])); trigs.Add(MeshVertex(edges[3], localNormals[3])); trigs.Add(MeshVertex(edges[1], localNormals[1])); trigs.Add(MeshVertex(edges[2], localNormals[2])); trigs.Add(MeshVertex(edges[3], localNormals[3])); } else { trigs.Add(MeshVertex(edges[0], localNormals[0])); trigs.Add(MeshVertex(edges[1], localNormals[1])); trigs.Add(MeshVertex(edges[2], localNormals[2])); } } } mesh.vertices = verts.ToArray(); mesh.triangles = trigs.ToArray(); mesh.normals = norms.ToArray(); mesh.uv = meshuv.ToArray(); } if (facing != Facing.Normal || surfaceType == GlobalSurfaceType.SplitTriangleAll) { // Extra processing through mesh builder. MeshBuilder meshBuilder = new MeshBuilder(mesh); if (facing == Facing.Flipped) { meshBuilder.Invert(); } else if (facing == Facing.TwoSided) { meshBuilder.AddOtherSide(); } if (surfaceType == GlobalSurfaceType.SplitTriangleAll) { meshBuilder.SplitAllTriangles(); } meshBuilder.ToMesh(mesh); } // Finalize the mesh. if (surfaceType == GlobalSurfaceType.AutoSmoothAll) { mesh.RecalculateNormals(); } mesh.RecalculateBounds(); }
public VertexKey ownerID;//边的拥有者ID public Edge(VertexKey ownerID, VertexKey targetID, WeightType weight) : base(targetID, weight) { this.ownerID = ownerID; }
public int weight = -1; //不存在负值称长度 存在负值称权值 public IndexEdge(VertexKey targetID, int weight) { this.weight = weight; this.targetID = targetID; }
/// <summary> /// Recalculate the normals of a mesh based on an angle threshold. This takes /// into account distinct vertices that have the same position. /// </summary> /// <param name="mesh"></param> /// <param name="angle"> /// The smoothing angle. Note that triangles that already share /// the same vertex will be smooth regardless of the angle! /// </param> /// <param name="ignoreFactor"> /// A fraction between 0 and 1. /// Weights smaller than this fraction relative to the largest weight are ignored. /// </param> public static void RecalculateNormals(this Mesh mesh, float angle, float ignoreFactor) { var triangles = mesh.triangles; var vertices = mesh.vertices; var triNormals = new Vector3[triangles.Length / 3]; //Normal of each triangle var triNormalsWeighted = new Vector3[triangles.Length / 3]; //Weighted normal of each triangle var normals = new Vector3[vertices.Length]; angle = angle * Mathf.Deg2Rad; var dictionary = new Dictionary <VertexKey, VertexEntry>(vertices.Length); //Goes through all the triangles and gathers up data to be used later for (var i = 0; i < triangles.Length; i += 3) { int i1 = triangles[i]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; //Calculate the normal of the triangle Vector3 p1 = vertices[i2] - vertices[i1]; Vector3 p2 = vertices[i3] - vertices[i1]; // By not normalizing the cross product, // the face area is pre-multiplied onto the normal for free. Vector3 normal = Vector3.Cross(p1, p2); int triIndex = i / 3; triNormalsWeighted[triIndex] = normal; triNormals[triIndex] = normal.normalized; VertexEntry entry; VertexKey key; //For each of the three points of the triangle // > Add this triangle as part of the triangles they're connected to. if (!dictionary.TryGetValue(key = new VertexKey(vertices[i1]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i1, triIndex); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i2]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i2, triIndex); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i3]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i3, triIndex); } //Foreach point in space (not necessarily the same vertex index!) //{ // Foreach triangle T1 that point belongs to // { // Foreach other triangle T2 (including self) that point belongs to and that // meets any of the following: // 1) The corresponding vertex is actually the same vertex // 2) The angle between the two triangles is less than the smoothing angle // { // > Add to the set of contributing normals // } // > Add the normals in the set together, excluding those smaller than the threshold. // > Normalize the resulting vector to find the average // > Assign the normal to corresponding vertex of T1 // } //} List <Vector3> normalSet = new List <Vector3>(); foreach (var value in dictionary.Values) { for (var i = 0; i < value.Count; ++i) { normalSet.Clear(); float longest = 0; for (var j = 0; j < value.Count; ++j) { bool use = false; if (value.VertexIndex[i] == value.VertexIndex[j]) { use = true; } else { float dot = Vector3.Dot( triNormals[value.TriangleIndex[i]], triNormals[value.TriangleIndex[j]]); dot = Mathf.Clamp(dot, -0.99999f, 0.99999f); float acos = Mathf.Acos(dot); if (acos <= angle) { use = true; } } if (use) { Vector3 normal = triNormalsWeighted[value.TriangleIndex[j]]; normalSet.Add(normal); float length = normal.magnitude; if (length > longest) { longest = length; } } } var sum = new Vector3(); var threshold = longest * ignoreFactor; for (int j = 0; j < normalSet.Count; j++) { if (normalSet[j].magnitude >= threshold) { sum += normalSet[j]; } } normals[value.VertexIndex[i]] = sum.normalized; } } mesh.normals = normals; }
/// <summary> /// Generates initial data for DD Toon shaders in the vertex colors of a mesh. /// RGB hold the outline normals /// A is the outline scale /// </summary> /// <param name="mesh"></param> public static void GenerateToonData(this Mesh mesh) { var vertices = mesh.vertices; var colors = new Color[vertices.Length]; // Holds the normal of each triangle in each sub mesh. var triNormals = new Vector3[mesh.subMeshCount][]; var dictionary = new Dictionary <VertexKey, List <VertexEntry> >(vertices.Length); for (var subMeshIndex = 0; subMeshIndex < mesh.subMeshCount; ++subMeshIndex) { var triangles = mesh.GetTriangles(subMeshIndex); triNormals[subMeshIndex] = new Vector3[triangles.Length / 3]; for (var i = 0; i < triangles.Length; i += 3) { int i1 = triangles[i]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; // Calculate the normal of the triangle Vector3 p1 = vertices[i2] - vertices[i1]; Vector3 p2 = vertices[i3] - vertices[i1]; Vector3 normal = Vector3.Cross(p1, p2).normalized; int triIndex = i / 3; triNormals[subMeshIndex][triIndex] = normal; List <VertexEntry> entry; VertexKey key; if (!dictionary.TryGetValue(key = new VertexKey(vertices[i1]), out entry)) { entry = new List <VertexEntry>(4); dictionary.Add(key, entry); } entry.Add(new VertexEntry(subMeshIndex, triIndex, i1)); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i2]), out entry)) { entry = new List <VertexEntry>(); dictionary.Add(key, entry); } entry.Add(new VertexEntry(subMeshIndex, triIndex, i2)); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i3]), out entry)) { entry = new List <VertexEntry>(); dictionary.Add(key, entry); } entry.Add(new VertexEntry(subMeshIndex, triIndex, i3)); } } // Each entry in the dictionary represents a unique vertex position. foreach (var vertList in dictionary.Values) { for (var i = 0; i < vertList.Count; ++i) { var sum = new Vector3(); var lhsEntry = vertList[i]; for (var j = 0; j < vertList.Count; ++j) { var rhsEntry = vertList[j]; sum += triNormals[rhsEntry.MeshIndex][rhsEntry.TriangleIndex]; } sum = sum.normalized; colors[lhsEntry.VertexIndex].r = sum[0]; colors[lhsEntry.VertexIndex].g = sum[1]; colors[lhsEntry.VertexIndex].b = sum[2]; colors[lhsEntry.VertexIndex].a = 1.0f; } } mesh.colors = colors; }
/// <summary> /// Recalculate the normals of a mesh based on an angle threshold. This takes /// into account distinct vertices that have the same position. /// </summary> /// <param name="mesh"></param> /// <param name="angle"> /// The smoothing angle. Note that triangles that already share /// the same vertex will be smooth regardless of the angle! /// </param> public static void RecalculateNormals(this Mesh mesh, float angle) { var triangles = mesh.GetTriangles(0); var vertices = mesh.vertices; var triNormals = new Vector3[triangles.Length / 3]; //Holds the normal of each triangle var normals = new Vector3[vertices.Length]; angle = angle * Mathf.Deg2Rad; var dictionary = new Dictionary <VertexKey, VertexEntry>(vertices.Length); //Goes through all the triangles and gathers up data to be used later for (var i = 0; i < triangles.Length; i += 3) { int i1 = triangles[i]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; //Calculate the normal of the triangle Vector3 p1 = vertices[i2] - vertices[i1]; Vector3 p2 = vertices[i3] - vertices[i1]; Vector3 normal = Vector3.Cross(p1, p2).normalized; int triIndex = i / 3; triNormals[triIndex] = normal; VertexEntry entry; VertexKey key; //For each of the three points of the triangle // > Add this triangle as part of the triangles they're connected to. if (!dictionary.TryGetValue(key = new VertexKey(vertices[i1]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i1, triIndex); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i2]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i2, triIndex); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i3]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i3, triIndex); } //Foreach point in space (not necessarily the same vertex index!) //{ // Foreach triangle T1 that point belongs to // { // Foreach other triangle T2 (including self) that point belongs to and that // meets any of the following: // 1) The corresponding vertex is actually the same vertex // 2) The angle between the two triangles is less than the smoothing angle // { // > Add to temporary Vector3 // } // > Normalize temporary Vector3 to find the average // > Assign the normal to corresponding vertex of T1 // } //} foreach (var value in dictionary.Values) { for (var i = 0; i < value.Count; ++i) { var sum = new Vector3(); for (var j = 0; j < value.Count; ++j) { if (value.VertexIndex[i] == value.VertexIndex[j]) { sum += triNormals[value.TriangleIndex[j]]; } else { float dot = Vector3.Dot( triNormals[value.TriangleIndex[i]], triNormals[value.TriangleIndex[j]]); dot = Mathf.Clamp(dot, -0.99999f, 0.99999f); float acos = Mathf.Acos(dot); if (acos <= angle) { sum += triNormals[value.TriangleIndex[j]]; } } } normals[value.VertexIndex[i]] = sum.normalized; } } mesh.normals = normals; }
public static void Optimize(this BrickMeshInfo mesh, float angle) { if (mesh == null || mesh.Vertices.Count == 0) { return; } var triangles = mesh.Triangles; var vertices = mesh.Vertices; var colors = mesh.ColorIndices; var triNormals = new Vector3[triangles.Count / 3]; //Holds the normal of each triangle //Debug.Log(string.Format("Start optimize {0}: vtCnt:{1}, triCnt:{2}, colorCnt:{3}", // mesh.name, vertices.Count, triangles.Count, colors.Count)); angle = angle * Mathf.Deg2Rad; var dictionary = new Dictionary <VertexKey, VertexEntry>(vertices.Count); // Set vertex index dictionary var vtIndices = new Dictionary <int, VertexIndexEnry>(vertices.Count); for (int i = 0; i < vertices.Count; ++i) { vtIndices.Add(i, new VertexIndexEnry(i)); } // Goes through all the triangles and gathers up data to be used later for (var i = 0; i < triangles.Count; i += 3) { int i1 = triangles[i]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; int color1 = colors[i1]; int color2 = colors[i2]; int color3 = colors[i3]; //Calculate the normal of the triangle Vector3 p1 = vertices[i2] - vertices[i1]; Vector3 p2 = vertices[i3] - vertices[i1]; Vector3 normal = Vector3.Cross(p1, p2).normalized; int triIndex = i / 3; triNormals[triIndex] = normal; VertexEntry entry; VertexKey key; //For each of the three points of the triangle // > Add this triangle as part of the triangles they're connected to. if (!dictionary.TryGetValue(key = new VertexKey(vertices[i1]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i1, triIndex, color1); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i2]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i2, triIndex, color2); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i3]), out entry)) { entry = new VertexEntry(); dictionary.Add(key, entry); } entry.Add(i3, triIndex, color3); } bool isShrinkNeeded = false; // Shrink vertcies index dictionary foreach (var value in dictionary.Values) { for (var i = 0; i < value.Count; ++i) { for (var j = i + 1; j < value.Count; ++j) { if (value.vertexIndex[i] == value.vertexIndex[j]) { continue; } if (value.colorIndex[i] != value.colorIndex[j]) { continue; } float dot = Vector3.Dot( triNormals[value.triangleIndex[i]], triNormals[value.triangleIndex[j]]); dot = Mathf.Clamp(dot, -0.99999f, 0.99999f); float acos = Mathf.Acos(dot); if (acos <= angle) { var srcIndex = value.vertexIndex[j]; var targetIndex = value.vertexIndex[i]; //Debug.Log(string.Format("Mark replace: {0} to {1}", srcIndex, targetIndex)); vtIndices[srcIndex].replaceFlag = true; vtIndices[srcIndex].replaceIndex = targetIndex; isShrinkNeeded = true; } } } } if (isShrinkNeeded) { List <Vector3> shrinkedVertices = new List <Vector3>(); List <short> shrinkedColors = new List <short>(); var vtKeys = vtIndices.Keys.ToList(); int serialIndex = 0; foreach (var key in vtKeys) { if (vtIndices[key].replaceFlag) { int firstReplaceIndex = vtIndices[key].replaceIndex; int finalReplaceIndex = firstReplaceIndex; while (vtIndices[finalReplaceIndex].replaceFlag) { finalReplaceIndex = vtIndices[finalReplaceIndex].replaceIndex; if (finalReplaceIndex == firstReplaceIndex) { //Debug.Log(string.Format("Cancle Replace: {0} with {1}", vtIndices[key].replaceIndex, firstReplaceIndex)); vtIndices[key].replaceFlag = false; break; } } if (vtIndices[key].replaceFlag) { //Debug.Log(string.Format("Replace: {0} to {1}", vtIndices[key].replaceIndex, finalReplaceIndex)); vtIndices[key].replaceIndex = finalReplaceIndex; continue; } } shrinkedVertices.Add(vertices[key]); shrinkedColors.Add(colors[key]); vtIndices[key].validPos = serialIndex++; } for (var i = 0; i < triangles.Count; i++) { var oriIndex = triangles[i]; var resultIndex = vtIndices[oriIndex].replaceFlag ? vtIndices[vtIndices[oriIndex].replaceIndex].validPos : vtIndices[oriIndex].validPos; triangles[i] = resultIndex; } //Debug.Log(string.Format("Reduced vertices of {0} : {1} to {2}", mesh.name, vertices.Count, shrinkedVertices.Count)); mesh.Vertices = shrinkedVertices; mesh.ColorIndices = shrinkedColors; } }