Пример #1
0
        /// <summary>
        /// Recalculates a mesh's normals while retaining smoothed common vertices.
        /// </summary>
        /// <param name="mesh"></param>
        internal static void RecalculateNormals(PolyMesh mesh)
        {
            //null checks
            if (mesh == null)
            {
                return;
            }

            int[][] smooth = GetSmoothSeamLookup(mesh);

            mesh.RecalculateNormals();

            if (smooth != null)
            {
                Vector3[] normals = mesh.normals;

                for (int i = 0; i < smooth.Length; ++i)
                {
                    int[]   l = smooth[i];
                    Vector3 n = Math.Average(normals, l);

                    for (int j = 0; j < l.Length; ++j)
                    {
                        normals[l[j]] = n;
                    }
                }

                mesh.normals = normals;
            }
        }
Пример #2
0
        /// <summary>
        /// Builds a lookup with each vertex index and a list of all neighboring indices.
        /// </summary>
        /// <param name="mesh"></param>
        /// <returns></returns>
        internal static Dictionary <int, int[]> GetAdjacentVertices(PolyMesh mesh)
        {
            //null checks
            if (mesh == null)
            {
                return(null);
            }

            int[][] common = GetCommonVertices(mesh);
            Dictionary <int, int> lookup = common.GetCommonLookup <int>();

            List <CommonEdge>  edges = GetEdges(mesh, lookup);
            List <List <int> > map   = new List <List <int> >();

            for (int i = 0; i < common.Count(); i++)
            {
                map.Add(new List <int>());
            }

            for (int i = 0; i < edges.Count; i++)
            {
                map[edges[i].cx].Add(edges[i].y);
                map[edges[i].cy].Add(edges[i].x);
            }

            Dictionary <int, int[]> adjacent          = new Dictionary <int, int[]>();
            IEnumerable <int>       distinctTriangles = mesh.GetTriangles().Distinct();

            foreach (int i in distinctTriangles)
            {
                adjacent.Add(i, map[lookup[i]].ToArray());
            }

            return(adjacent);
        }
Пример #3
0
        /// <summary>
        /// Initialize a SplatSet with mesh and attribute layout.
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="attributes"></param>
        internal SplatSet(PolyMesh mesh, AttributeLayout[] attributes) : this(mesh.vertexCount, attributes, false)
        {
            foreach (var kvp in channelMap)
            {
                switch (kvp.Key)
                {
                case MeshChannel.UV0:
                case MeshChannel.UV2:
                case MeshChannel.UV3:
                case MeshChannel.UV4:
                {
                    List <Vector4> uv = mesh.GetUVs(MeshChannelUtility.UVChannelToIndex(kvp.Key));
                    weights[kvp.Value] = uv.Count == weightCount?uv.ToArray() : new Vector4[weightCount];
                }
                break;

                case MeshChannel.Color:
                {
                    Color32[] color = mesh.colors;
                    weights[kvp.Value] = color != null && color.Length == weightCount?System.Array.ConvertAll(color, x => Color32ToVec4(x)) : new Vector4[weightCount];
                }
                break;

                case MeshChannel.Tangent:
                {
                    Vector4[] tangent = mesh.tangents;
                    weights[kvp.Value] = tangent != null && tangent.Length == weightCount ? tangent : new Vector4[weightCount];
                }
                break;
                }
            }
        }
Пример #4
0
        private static HashSet <CommonEdge> GetEdgesDistinct(PolyMesh m, Dictionary <int, int> lookup, out List <CommonEdge> duplicates)
        {
            int[] tris  = m.GetTriangles();
            int   count = tris.Length;

            HashSet <CommonEdge> edges = new HashSet <CommonEdge>();

            duplicates = new List <CommonEdge>();

            for (int i = 0; i < count; i += 3)
            {
                CommonEdge a = new CommonEdge(tris[i + 0], tris[i + 1], lookup[tris[i + 0]], lookup[tris[i + 1]]);
                CommonEdge b = new CommonEdge(tris[i + 1], tris[i + 2], lookup[tris[i + 1]], lookup[tris[i + 2]]);
                CommonEdge c = new CommonEdge(tris[i + 2], tris[i + 0], lookup[tris[i + 2]], lookup[tris[i + 0]]);

                if (!edges.Add(a))
                {
                    duplicates.Add(a);
                }

                if (!edges.Add(b))
                {
                    duplicates.Add(b);
                }

                if (!edges.Add(c))
                {
                    duplicates.Add(c);
                }
            }

            return(edges);
        }
Пример #5
0
        /// <summary>
        /// Apply the weights to "mesh"
        /// </summary>
        /// <param name="mesh">The mesh we want to apply weights to</param>
        internal void Apply(PolyMesh mesh)
        {
            foreach (AttributeLayout al in attributeLayout)
            {
                switch (al.channel)
                {
                case MeshChannel.UV0:
                case MeshChannel.UV2:
                case MeshChannel.UV3:
                case MeshChannel.UV4:
                {
                    List <Vector4> uv = new List <Vector4>(weights[channelMap[al.channel]]);
                    mesh.SetUVs(MeshChannelUtility.UVChannelToIndex(al.channel), uv);
                }
                break;

                case MeshChannel.Color:
                {
                    // @todo consider storing Color array separate from Vec4 since this cast costs ~5ms
                    mesh.colors = System.Array.ConvertAll(weights[channelMap[al.channel]], x => Vec4ToColor32(x));
                    break;
                }

                case MeshChannel.Tangent:
                {
                    mesh.tangents = weights[channelMap[MeshChannel.Tangent]];
                    break;
                }
                }
            }
        }
Пример #6
0
        /// <summary>
        /// Builds a list<group> with each vertex index and a list of all other vertices sharing a position.
        /// </summary>
        /// <returns>
        /// key: Index in vertices array
        /// value: List of other indices in positions array that share a point with this index.
        /// </returns>
        internal static int[][] GetCommonVertices(PolyMesh mesh)
        {
            //null checks
            if (mesh == null)
            {
                return(null);
            }

            CommonVertexCache cache;

            if (commonVerticesCache.TryGetValue(mesh, out cache))
            {
                if (cache.IsValidForMesh(mesh.mesh))
                {
                    return(cache.indices);
                }
            }

            if (!commonVerticesCache.ContainsKey(mesh))
            {
                commonVerticesCache.Add(mesh, cache = new CommonVertexCache(mesh.mesh));
            }
            else
            {
                commonVerticesCache[mesh] = cache = new CommonVertexCache(mesh.mesh);
            }

            return(cache.indices);
        }
Пример #7
0
        /// <summary>
        /// Recalculates a mesh's normals while retaining smoothed common vertices.
        /// </summary>
        /// <param name="mesh"></param>
        internal static void RecalculateNormals(PolyMesh mesh)
        {
            //null checks
            if (mesh == null)
            {
                return;
            }

            List <List <int> > smooth = GetSmoothSeamLookup(mesh);

            mesh.RecalculateNormals();

            if (smooth != null)
            {
                Vector3[] normals = mesh.normals;

                foreach (List <int> l in smooth)
                {
                    Vector3 n = PolyMath.Average(normals, l);

                    foreach (int i in l)
                    {
                        normals[i] = n;
                    }
                }

                mesh.normals = normals;
            }
        }
Пример #8
0
        internal override void OnVerticesMoved(PolyMesh mesh)
        {
#if DO_RENDER_OVERLAY_MESH
            Vector3[] v = mesh.vertices;

            if (wireframeMesh != null)
            {
                wireframeMesh.vertices = v;
            }

            if (drawVertexBillboards)
            {
                int len = System.Math.Min(ushort.MaxValue / 4, common.Count);

                if (vertexMesh != null)
                {
                    Vector3[] v2 = new Vector3[len * 4];

                    for (int i = 0; i < len; i++)
                    {
                        int ind = common[i][0];

                        v2[i * 4 + 0] = v[ind];
                        v2[i * 4 + 1] = v[ind];
                        v2[i * 4 + 2] = v[ind];
                        v2[i * 4 + 3] = v[ind];
                    }

                    vertexMesh.vertices = v2;
                }
            }
#endif
        }
Пример #9
0
        /// <summary>
        /// Vertices that are common, form a seam, and should be smoothed.
        /// </summary>
        /// <param name="mesh"></param>
        /// <returns></returns>
        internal static int[][] GetSmoothSeamLookup(PolyMesh mesh)
        {
            //null checks
            if (mesh == null)
            {
                return(null);
            }

            Vector3[] normals = mesh.normals;

            if (normals == null)
            {
                return(null);
            }

            int[][] lookup = null;

            if (commonNormalsCache.TryGetValue(mesh, out lookup))
            {
                return(lookup);
            }

            int[][] common = GetCommonVertices(mesh);

            var z = common
                    .SelectMany(x => x.GroupBy(i => (RndVec3)normals[i]))
                    .Where(n => n.Count() > 1)
                    .Select(t => t.ToArray())
                    .ToArray();

            commonNormalsCache.Add(mesh, z);

            return(z);
        }
Пример #10
0
        internal static HashSet <CommonEdge> GetEdgesDistinct(PolyMesh mesh, out List <CommonEdge> duplicates)
        {
            //null checks
            if (mesh == null)
            {
                duplicates = null;
                return(null);
            }

            Dictionary <int, int> lookup = GetCommonVertices(mesh).GetCommonLookup <int>();

            return(GetEdgesDistinct(mesh, lookup, out duplicates));
        }
Пример #11
0
        /// <summary>
        /// Returns all vertex indices that are on an open edge.
        /// </summary>
        /// <param name="mesh"></param>
        /// <returns></returns>
        internal static HashSet <int> GetNonManifoldIndices(PolyMesh mesh)
        {
            if (mesh == null)
            {
                return(null);
            }

            List <CommonEdge>    duplicates;
            HashSet <CommonEdge> edges = GetEdgesDistinct(mesh, out duplicates);

            edges.ExceptWith(duplicates);
            HashSet <int> hash = CommonEdge.ToHashSet(edges);

            return(hash);
        }
Пример #12
0
        internal static List <CommonEdge> GetEdges(PolyMesh m, Dictionary <int, int> lookup)
        {
            int[] tris  = m.GetTriangles();
            int   count = tris.Length;

            List <CommonEdge> edges = new List <CommonEdge>(count);

            for (int i = 0; i < count; i += 3)
            {
                edges.Add(new CommonEdge(tris[i + 0], tris[i + 1], lookup[tris[i + 0]], lookup[tris[i + 1]]));
                edges.Add(new CommonEdge(tris[i + 1], tris[i + 2], lookup[tris[i + 1]], lookup[tris[i + 2]]));
                edges.Add(new CommonEdge(tris[i + 2], tris[i + 0], lookup[tris[i + 2]], lookup[tris[i + 0]]));
            }

            return(edges);
        }
Пример #13
0
        /// <summary>
        /// Builds a lookup table for each vertex index and it's average normal with other vertices sharing a position.
        /// </summary>
        /// <param name="mesh"></param>
        /// <returns></returns>
        internal static Dictionary <int, Vector3> GetSmoothNormalLookup(PolyMesh mesh)
        {
            //null checks
            if (mesh == null)
            {
                return(null);
            }

            Vector3[] n = mesh.normals;
            Dictionary <int, Vector3> normals = new Dictionary <int, Vector3>();

            if (n == null || n.Length != mesh.vertexCount)
            {
                return(normals);
            }

            int[][] groups = GetCommonVertices(mesh);

            Vector3 avg = Vector3.zero;
            Vector3 a   = Vector3.zero;

            foreach (var group in groups)
            {
                avg.x = 0f;
                avg.y = 0f;
                avg.z = 0f;

                foreach (int i in group)
                {
                    a = n[i];

                    avg.x += a.x;
                    avg.y += a.y;
                    avg.z += a.z;
                }

                avg /= (float)group.Count();

                foreach (int i in group)
                {
                    normals.Add(i, avg);
                }
            }

            return(normals);
        }
Пример #14
0
        /// <summary>
        /// Builds a list<group> with each vertex index and a list of all other vertices sharing a position.
        /// </summary>
        /// <returns>
        /// key: Index in vertices array
        /// value: List of other indices in positions array that share a point with this index.
        /// </returns>
        internal static int[][] GetCommonVertices(PolyMesh mesh)
        {
            //null checks
            if (mesh == null)
            {
                return(null);
            }

            int[][] indices;

            if (commonVerticesCache.TryGetValue(mesh, out indices))
            {
                // int min = mesh.vertexCount, max = 0;

                // for(int x = 0; x < indices.Count; x++)
                // {
                //  for(int y = 0; y < indices[x].Count; y++)
                //  {
                //      int index = indices[x][y];
                //      if(index < min) min = index;
                //      if(index > max) max = index;
                //  }
                // }

                // if(max - min + 1 == mesh.vertexCount)
                return(indices);
            }

            Vector3[] v = mesh.vertices;
            int[]     t = Util.Fill <int>((x) => { return(x); }, v.Length);
            indices = t.ToLookup(x => (RndVec3)v[x])
                      .Select(y => y.ToArray())
                      .ToArray();

            if (!commonVerticesCache.ContainsKey(mesh))
            {
                commonVerticesCache.Add(mesh, indices);
            }
            else
            {
                commonVerticesCache[mesh] = indices;
            }

            return(indices);
        }
Пример #15
0
        /// <summary>
        /// Creates a new mesh using only the "src" positions, normals, and a new color array.
        /// </summary>
        /// <param name="src">source Mesh</param>
        /// <returns></returns>
        internal static Mesh CreateOverlayMesh(PolyMesh src)
        {
            //null checks
            if (src == null)
            {
                return(null);
            }

            Mesh m = new Mesh();

            m.name         = "Overlay Mesh: " + src.name;
            m.vertices     = src.vertices;
            m.normals      = src.normals;
            m.colors       = Util.Fill <Color>(new Color(0f, 0f, 0f, 0f), m.vertexCount);
            m.subMeshCount = src.subMeshCount;

            for (int i = 0; i < src.subMeshCount; i++)
            {
                SubMesh      subMesh         = src.subMeshes[i];
                MeshTopology subMeshTopology = subMesh.topology;
                int[]        subMeshIndices  = subMesh.indexes;

                if (subMeshTopology == MeshTopology.Triangles)
                {
                    int[] tris  = subMeshIndices;
                    int[] lines = new int[tris.Length * 2];
                    int   index = 0;
                    for (int n = 0; n < tris.Length; n += 3)
                    {
                        lines[index++] = tris[n + 0];
                        lines[index++] = tris[n + 1];
                        lines[index++] = tris[n + 1];
                        lines[index++] = tris[n + 2];
                        lines[index++] = tris[n + 2];
                        lines[index++] = tris[n + 0];
                    }
                    m.SetIndices(lines, MeshTopology.Lines, i);
                }
                else
                {
                    m.SetIndices(subMeshIndices, subMeshTopology, i);
                }
            }
            return(m);
        }
Пример #16
0
        /// <summary>
        /// Set the Overlay mesh from a `PolyMesh`
        /// </summary>
        /// <param name="m"></param>
        internal void SetMesh(PolyMesh m)
        {
#if DO_RENDER_OVERLAY_MESH
            wireframeMesh = PolyMeshUtility.CreateOverlayMesh(m);

            wireframeMesh.hideFlags = HideFlags.HideAndDontSave;

            w_colors = new Color[wireframeMesh.vertexCount];

            if (drawVertexBillboards)
            {
                common               = PolyMeshUtility.GetCommonVertices(m);
                vertexMesh           = PolyMeshUtility.CreateVertexBillboardMesh(m, common);
                vertexMesh.hideFlags = HideFlags.HideAndDontSave;
                v_colors             = new Color[vertexMesh.vertexCount];
            }
#endif
        }
        /// <summary>
        /// Initializes non-serialized internal cache in the component.
        /// Should be called after instantiation.
        /// Use <see cref="isInitialized"/> to check if it has already been initialized.
        /// </summary>
        internal void Initialize()
        {
            if (isInitialized)
            {
                return;
            }

            if (!m_ComponentsCache.IsValid())
            {
                m_ComponentsCache = new MeshComponentsCache(gameObject);
            }

            if (m_PolyMesh == null)
            {
                m_PolyMesh = new PolyMesh();
            }

            Mesh mesh = null;

            if (m_ComponentsCache.MeshFilter != null)
            {
                mesh = m_ComponentsCache.MeshFilter.sharedMesh;
                if (m_OriginalMeshObject == null)
                {
                    m_OriginalMeshObject = mesh;
                }
            }
            else if (m_ComponentsCache.SkinnedMeshRenderer != null)
            {
                mesh = m_ComponentsCache.SkinnedMeshRenderer.sharedMesh;
            }

            if (!polyMesh.IsValid() && mesh)
            {
                SetMesh(mesh);
            }
            else if (polyMesh.IsValid())
            {
                SetMesh(polyMesh.ToUnityMesh());
            }

            m_Initialized = true;
        }
Пример #18
0
        internal override void OnVerticesMoved(PolyMesh mesh)
        {
#if DO_RENDER_OVERLAY_MESH
            Vector3[] v = mesh.vertices;

            if (wireframeMesh != null)
            {
                wireframeMesh.vertices = v;
            }

            if (drawVertexBillboards)
            {
                int len = System.Math.Min(ushort.MaxValue / 4, common.Length);

                if (vertexMesh != null)
                {
                    int bufferSize = len * 4;
                    if (s_vertexMeshVerticesBuffer.Length != bufferSize)
                    {
                        Array.Resize <Vector3>(ref s_vertexMeshVerticesBuffer, bufferSize);
                    }

                    for (int i = 0; i < len; i++)
                    {
                        int ind = common[i][0];

                        s_vertexMeshVerticesBuffer[i * 4 + 0] = v[ind];
                        s_vertexMeshVerticesBuffer[i * 4 + 1] = v[ind];
                        s_vertexMeshVerticesBuffer[i * 4 + 2] = v[ind];
                        s_vertexMeshVerticesBuffer[i * 4 + 3] = v[ind];
                    }

                    vertexMesh.vertices = s_vertexMeshVerticesBuffer;
                }
            }
#endif
        }
Пример #19
0
        internal static Mesh CreateVertexBillboardMesh(PolyMesh src, List <List <int> > common)
        {
            //null checks
            if (src == null || common == null)
            {
                return(null);
            }

            int vertexCount = System.Math.Min(ushort.MaxValue / 4, common.Count());

            Vector3[] positions = new Vector3[vertexCount * 4];
            Vector2[] uv0       = new Vector2[vertexCount * 4];
            Vector2[] uv2       = new Vector2[vertexCount * 4];
            Color[]   colors    = new Color[vertexCount * 4];
            int[]     tris      = new int[vertexCount * 6];

            int n = 0;
            int t = 0;

            Vector3 up    = Vector3.up;         // * .1f;
            Vector3 right = Vector3.right;      // * .1f;

            Vector3[] v = src.vertices;

            for (int i = 0; i < vertexCount; i++)
            {
                int tri = common[i][0];

                positions[t + 0] = v[tri];
                positions[t + 1] = v[tri];
                positions[t + 2] = v[tri];
                positions[t + 3] = v[tri];

                uv0[t + 0] = Vector3.zero;
                uv0[t + 1] = Vector3.right;
                uv0[t + 2] = Vector3.up;
                uv0[t + 3] = Vector3.one;

                uv2[t + 0] = -up - right;
                uv2[t + 1] = -up + right;
                uv2[t + 2] = up - right;
                uv2[t + 3] = up + right;

                tris[n + 0] = t + 0;
                tris[n + 1] = t + 1;
                tris[n + 2] = t + 2;
                tris[n + 3] = t + 1;
                tris[n + 4] = t + 3;
                tris[n + 5] = t + 2;

                colors[t + 0] = clear;
                colors[t + 1] = clear;
                colors[t + 2] = clear;
                colors[t + 3] = clear;

                t += 4;
                n += 6;
            }

            Mesh m = new Mesh();

            m.vertices  = positions;
            m.uv        = uv0;
            m.uv2       = uv2;
            m.colors    = colors;
            m.triangles = tris;

            return(m);
        }
Пример #20
0
        internal static Mesh CreateVertexBillboardMesh(PolyMesh src, int[][] common)
        {
            if (src == null || common == null)
            {
                return(null);
            }

            int vertexCount = System.Math.Min(ushort.MaxValue / 4, common.Count());

            Vector3[] positions = new Vector3[vertexCount * 4];
            Vector2[] uv0       = new Vector2[vertexCount * 4];
            Vector2[] uv2       = new Vector2[vertexCount * 4];
            Color[]   colors    = new Color[vertexCount * 4];
            int[]     tris      = new int[vertexCount * 6];

            int n = 0;
            int t = 0;

            Vector3[] v = src.vertices;

            for (int i = 0; i < vertexCount; i++)
            {
                int tri = common[i][0];

                positions[t + 0] = v[tri];
                positions[t + 1] = v[tri];
                positions[t + 2] = v[tri];
                positions[t + 3] = v[tri];

                uv0[t + 0] = k_VertexBillboardUV0Content[0];
                uv0[t + 1] = k_VertexBillboardUV0Content[1];
                uv0[t + 2] = k_VertexBillboardUV0Content[2];
                uv0[t + 3] = k_VertexBillboardUV0Content[3];

                uv2[t + 0] = k_VertexBillboardUV2Content[0];
                uv2[t + 1] = k_VertexBillboardUV2Content[1];
                uv2[t + 2] = k_VertexBillboardUV2Content[2];
                uv2[t + 3] = k_VertexBillboardUV2Content[3];

                tris[n + 0] = t + 0;
                tris[n + 1] = t + 1;
                tris[n + 2] = t + 2;
                tris[n + 3] = t + 1;
                tris[n + 4] = t + 3;
                tris[n + 5] = t + 2;

                colors[t + 0] = clear;
                colors[t + 1] = clear;
                colors[t + 2] = clear;
                colors[t + 3] = clear;

                t += 4;
                n += 6;
            }

            Mesh m = new Mesh();

            m.vertices  = positions;
            m.uv        = uv0;
            m.uv2       = uv2;
            m.colors    = colors;
            m.triangles = tris;

            return(m);
        }
Пример #21
0
        internal static List <CommonEdge> GetEdges(PolyMesh m)
        {
            Dictionary <int, int> lookup = GetCommonVertices(m).GetCommonLookup <int>();

            return(GetEdges(m, lookup));
        }
Пример #22
0
 /// <summary>
 /// Let the temp mesh know that vertex positions have changed.
 /// </summary>
 /// <param name="mesh"></param>
 internal virtual void OnVerticesMoved(PolyMesh mesh)
 {
 }
Пример #23
0
        /// <summary>
        /// Returns a dictionary where each PolyEdge is mapped to a list of triangle indices that share that edge.
        /// To translate triangle list to vertex indices, multiply by 3 and take those indices (ex, triangles[index+{0,1,2}])
        /// </summary>
        /// <param name="mesh">mesh to use</param>
        /// <returns>see summary</returns>
        internal static Dictionary <PolyEdge, List <int> > GetAdjacentTriangles(PolyMesh mesh)
        {
            //null checks
            if (mesh == null)
            {
                return(null);
            }

            int len = mesh.GetTriangles().Length;

            if (len % 3 != 0 || len / 3 == mesh.vertexCount)
            {
                return(new Dictionary <PolyEdge, List <int> >());
            }

            Dictionary <PolyEdge, List <int> > lookup = null;

            // @todo - should add some checks to make sure triangle structure hasn't changed
            if (adjacentTrianglesCache.TryGetValue(mesh, out lookup) && lookup.Count == mesh.vertexCount)
            {
                return(lookup);
            }

            if (adjacentTrianglesCache.ContainsKey(mesh))
            {
                adjacentTrianglesCache.Remove(mesh);
            }

            int subMeshCount = mesh.subMeshCount;

            lookup = new Dictionary <PolyEdge, List <int> >();
            List <int> connections;

            for (int n = 0; n < subMeshCount; n++)
            {
                int[] tris = mesh.subMeshes[n].indexes;

                for (int i = 0; i < tris.Length; i += 3)
                {
                    int index = i / 3;

                    PolyEdge a = new PolyEdge(tris[i], tris[i + 1]);
                    PolyEdge b = new PolyEdge(tris[i + 1], tris[i + 2]);
                    PolyEdge c = new PolyEdge(tris[i + 2], tris[i]);

                    if (lookup.TryGetValue(a, out connections))
                    {
                        connections.Add(index);
                    }
                    else
                    {
                        lookup.Add(a, new List <int>()
                        {
                            index
                        });
                    }

                    if (lookup.TryGetValue(b, out connections))
                    {
                        connections.Add(index);
                    }
                    else
                    {
                        lookup.Add(b, new List <int>()
                        {
                            index
                        });
                    }

                    if (lookup.TryGetValue(c, out connections))
                    {
                        connections.Add(index);
                    }
                    else
                    {
                        lookup.Add(c, new List <int>()
                        {
                            index
                        });
                    }
                }
            }

            adjacentTrianglesCache.Add(mesh, lookup);

            return(lookup);
        }