/// <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
            });
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
 public static IndexEdge listFind(EdgesType edges, VertexKey key)
 {
     foreach (IndexEdge ele in edges)
     {
         if (ele.targetID == key)
         {
             return(ele);
         }
     }
     return(null);
 }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        public void deleteEdge(VertexKey ownerID, VertexKey targetID)
        {
            IndexEdge it = listFind(edgeData[ownerID], targetID);

            if (it == null)
            {
                //do nothig
            }
            else
            {
                edgeData[ownerID].Remove(it);
            }
        }
Beispiel #6
0
        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;
            }
        }
Beispiel #7
0
        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);
        }
Beispiel #8
0
        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);
        }
Beispiel #9
0
        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;
        }
Beispiel #10
0
        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);
        }
Beispiel #11
0
 private static MeshVertices.VertexKeyPair Pair(Vertex v, VertexKey k)   // new VertexKeyPair shortcut
 {
     return(new MeshVertices.VertexKeyPair {
         vertex = v, key = k
     });
 }
Beispiel #12
0
 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;
    }
Beispiel #14
0
 public LinkedList <IndexEdge> getEdgeList(VertexKey v)
 {
     return(edgeData[v]);
 }
Beispiel #15
0
        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;
        }
Beispiel #16
0
        /// <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);
    }
Beispiel #17
0
    /// <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;
    }
Beispiel #18
0
    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();
    }
Beispiel #19
0
 public VertexKey ownerID;//边的拥有者ID
 public Edge(VertexKey ownerID, VertexKey targetID, WeightType weight) : base(targetID, weight)
 {
     this.ownerID = ownerID;
 }
Beispiel #20
0
 public int weight         = -1;                      //不存在负值称长度 存在负值称权值
 public IndexEdge(VertexKey targetID, int weight)
 {
     this.weight   = weight;
     this.targetID = targetID;
 }
Beispiel #21
0
    /// <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;
    }
Beispiel #22
0
    /// <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;
    }
Beispiel #23
0
    /// <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;
    }
Beispiel #24
0
    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;
        }
    }