/// <summary>
        /// Creates a manifold, consisting of a topology and vertex positions, in the shape of a cube.
        /// </summary>
        /// <param name="surface">The spherical surface describing the overall shape of the manifold.</param>
        /// <param name="topology">The topology created.</param>
        /// <param name="vertexPositions">The vertex positions created.</param>
        public static void CreateCube(SphericalSurface surface, out Topology topology, out Vector3[] vertexPositions)
        {
            var a = surface.radius / Mathf.Sqrt(3f);

            vertexPositions    = new Vector3[8];
            vertexPositions[0] = new Vector3(+a, +a, +a);
            vertexPositions[1] = new Vector3(+a, +a, -a);
            vertexPositions[2] = new Vector3(-a, +a, -a);
            vertexPositions[3] = new Vector3(-a, +a, +a);
            vertexPositions[4] = new Vector3(+a, -a, +a);
            vertexPositions[5] = new Vector3(+a, -a, -a);
            vertexPositions[6] = new Vector3(-a, -a, -a);
            vertexPositions[7] = new Vector3(-a, -a, +a);

            var orientation = surface.orientation;

            for (int i = 0; i < vertexPositions.Length; ++i)
            {
                vertexPositions[i] = orientation * vertexPositions[i];
            }

            var indexer = new ManualFaceNeighborIndexer(8, 24, 6);

            if (!surface.isInverted)
            {
                indexer.AddFace(0, 1, 2, 3);
                indexer.AddFace(0, 4, 5, 1);
                indexer.AddFace(1, 5, 6, 2);
                indexer.AddFace(2, 6, 7, 3);
                indexer.AddFace(3, 7, 4, 0);
                indexer.AddFace(7, 6, 5, 4);
            }
            else
            {
                indexer.AddFace(3, 2, 1, 0);
                indexer.AddFace(1, 5, 4, 0);
                indexer.AddFace(2, 6, 5, 1);
                indexer.AddFace(3, 7, 6, 2);
                indexer.AddFace(0, 4, 7, 3);
                indexer.AddFace(4, 5, 6, 7);
            }

            topology = TopologyUtility.BuildTopology(indexer);
        }
        /// <summary>
        /// Creates a manifold, consisting of a topology and vertex positions, in the shape of an octahedron.
        /// </summary>
        /// <param name="surface">The spherical surface describing the overall shape of the manifold.</param>
        /// <param name="topology">The topology created.</param>
        /// <param name="vertexPositions">The vertex positions created.</param>
        public static void CreateOctahedron(SphericalSurface surface, out Topology topology, out Vector3[] vertexPositions)
        {
            vertexPositions    = new Vector3[6];
            vertexPositions[0] = new Vector3(0, +surface.radius, 0);
            vertexPositions[1] = new Vector3(+surface.radius, 0, 0);
            vertexPositions[2] = new Vector3(0, 0, -surface.radius);
            vertexPositions[3] = new Vector3(-surface.radius, 0, 0);
            vertexPositions[4] = new Vector3(0, 0, +surface.radius);
            vertexPositions[5] = new Vector3(0, -surface.radius, 0);

            var orientation = surface.orientation;

            for (int i = 0; i < vertexPositions.Length; ++i)
            {
                vertexPositions[i] = orientation * vertexPositions[i];
            }

            var indexer = new ManualFaceNeighborIndexer(6, 24, 8);

            if (!surface.isInverted)
            {
                indexer.AddFace(0, 1, 2);
                indexer.AddFace(0, 2, 3);
                indexer.AddFace(0, 3, 4);
                indexer.AddFace(0, 4, 1);
                indexer.AddFace(2, 1, 5);
                indexer.AddFace(3, 2, 5);
                indexer.AddFace(4, 3, 5);
                indexer.AddFace(1, 4, 5);
            }
            else
            {
                indexer.AddFace(2, 1, 0);
                indexer.AddFace(3, 2, 0);
                indexer.AddFace(4, 3, 0);
                indexer.AddFace(1, 4, 0);
                indexer.AddFace(5, 1, 2);
                indexer.AddFace(5, 2, 3);
                indexer.AddFace(5, 3, 4);
                indexer.AddFace(5, 4, 1);
            }

            topology = TopologyUtility.BuildTopology(indexer);
        }
        /// <summary>
        /// Creates a manifold, consisting of a topology and vertex positions, in the shape of a tetrahedron.
        /// </summary>
        /// <param name="surface">The spherical surface describing the overall shape of the manifold.</param>
        /// <param name="topology">The topology created.</param>
        /// <param name="vertexPositions">The vertex positions created.</param>
        public static void CreateTetrahedron(SphericalSurface surface, out Topology topology, out Vector3[] vertexPositions)
        {
            var y  = surface.radius * -1f / 3f;
            var z0 = surface.radius * 2f / 3f * Mathf.Sqrt(2f);
            var z1 = surface.radius * -Mathf.Sqrt(2f / 9f);
            var x  = surface.radius * Mathf.Sqrt(2f / 3f);

            vertexPositions    = new Vector3[4];
            vertexPositions[0] = new Vector3(0, surface.radius, 0);
            vertexPositions[1] = new Vector3(0, y, z0);
            vertexPositions[2] = new Vector3(+x, y, z1);
            vertexPositions[3] = new Vector3(-x, y, z1);

            var orientation = surface.orientation;

            for (int i = 0; i < vertexPositions.Length; ++i)
            {
                vertexPositions[i] = orientation * vertexPositions[i];
            }

            var indexer = new ManualFaceNeighborIndexer(4, 12, 4);

            if (!surface.isInverted)
            {
                indexer.AddFace(0, 1, 2);
                indexer.AddFace(0, 2, 3);
                indexer.AddFace(0, 3, 1);
                indexer.AddFace(3, 2, 1);
            }
            else
            {
                indexer.AddFace(2, 1, 0);
                indexer.AddFace(3, 2, 0);
                indexer.AddFace(1, 3, 0);
                indexer.AddFace(1, 2, 3);
            }

            topology = TopologyUtility.BuildTopology(indexer);
        }
示例#4
0
        private static void SubdivideTriangle(ManualFaceNeighborIndexer indexer, Topology.Face face, int degree, Func <Vector3, Vector3, float, Vector3> interpolator, List <Vector3> subdividedVertexPositions, int[] subdividedEdgeVertices, ref int currentVertexCount)
        {
            var rightEdge  = face.firstEdge;
            var bottomEdge = rightEdge.next;
            var leftEdge   = bottomEdge.next;

            var topVertex         = leftEdge.vertex;
            var bottomRightVertex = rightEdge.vertex;
            var bottomLeftVertex  = bottomEdge.vertex;

            int rightVertices  = rightEdge.index * degree;            // Progresses from the top vertex down to the bottom right vertex.
            int bottomVertices = bottomEdge.twinIndex * degree;       // Progresses from the bottom left vertex over to the bottom right vertex.
            int leftVertices   = leftEdge.twinIndex * degree;         // Progresses from the top vertex down to the bottom left vertex.

            if (degree > 2)
            {
                // Top triangle
                indexer.AddFace(subdividedEdgeVertices[leftVertices], topVertex.index, subdividedEdgeVertices[rightVertices]);

                // Next three triangles above the top inner subdivided vertex
                indexer.AddFace(subdividedEdgeVertices[leftVertices + 1], subdividedEdgeVertices[leftVertices], currentVertexCount);
                indexer.AddFace(currentVertexCount, subdividedEdgeVertices[leftVertices], subdividedEdgeVertices[rightVertices]);
                indexer.AddFace(currentVertexCount, subdividedEdgeVertices[rightVertices], subdividedEdgeVertices[rightVertices + 1]);

                // Top inner subdivided vertex
                subdividedVertexPositions.Add(interpolator(subdividedVertexPositions[subdividedEdgeVertices[leftVertices + 1]], subdividedVertexPositions[subdividedEdgeVertices[rightVertices + 1]], 0.5f));
                ++currentVertexCount;

                float   t;
                float   dt;
                Vector3 p0;
                Vector3 p1;

                int yEnd = degree - 1;

                // Middle rows of inner subdivided vertices
                for (int y = 1; y < yEnd; ++y)
                {
                    t  = dt = 1f / (y + 2);
                    p0 = subdividedVertexPositions[subdividedEdgeVertices[leftVertices + y + 1]];
                    p1 = subdividedVertexPositions[subdividedEdgeVertices[rightVertices + y + 1]];

                    // First two triangles of the row of faces above this row of inner subdivided vertices
                    indexer.AddFace(subdividedEdgeVertices[leftVertices + y + 1], subdividedEdgeVertices[leftVertices + y], currentVertexCount);
                    indexer.AddFace(currentVertexCount, subdividedEdgeVertices[leftVertices + y], currentVertexCount - y);

                    subdividedVertexPositions.Add(interpolator(p0, p1, t));
                    ++currentVertexCount;
                    t += dt;

                    for (int x = 1; x < y; ++x)
                    {
                        // Next two triangles above the vertex being added
                        indexer.AddFace(currentVertexCount - 1, currentVertexCount - y - 1, currentVertexCount);
                        indexer.AddFace(currentVertexCount, currentVertexCount - y - 1, currentVertexCount - y);
                        subdividedVertexPositions.Add(interpolator(p0, p1, t));
                        ++currentVertexCount;
                        t += dt;
                    }

                    // Last three triangles of the row of faces above this row of inner subdivided vertices
                    indexer.AddFace(currentVertexCount - 1, currentVertexCount - y - 1, currentVertexCount);
                    indexer.AddFace(currentVertexCount, currentVertexCount - y - 1, subdividedEdgeVertices[rightVertices + y]);
                    indexer.AddFace(currentVertexCount, subdividedEdgeVertices[rightVertices + y], subdividedEdgeVertices[rightVertices + y + 1]);

                    subdividedVertexPositions.Add(interpolator(p0, p1, t));
                    ++currentVertexCount;
                }

                // First two triangles of the last row of faces
                indexer.AddFace(bottomLeftVertex.index, subdividedEdgeVertices[leftVertices + yEnd], subdividedEdgeVertices[bottomVertices]);
                indexer.AddFace(subdividedEdgeVertices[bottomVertices], subdividedEdgeVertices[leftVertices + yEnd], currentVertexCount - yEnd);

                for (int x = 1; x < yEnd; ++x)
                {
                    // Next two triangles of the last row of faces
                    indexer.AddFace(subdividedEdgeVertices[bottomVertices + x - 1], currentVertexCount - yEnd + x - 1, subdividedEdgeVertices[bottomVertices + x]);
                    indexer.AddFace(subdividedEdgeVertices[bottomVertices + x], currentVertexCount - yEnd + x - 1, currentVertexCount - yEnd + x);
                }

                // Last three triangles of the last row of faces
                indexer.AddFace(subdividedEdgeVertices[bottomVertices + yEnd - 1], currentVertexCount - 1, subdividedEdgeVertices[bottomVertices + yEnd]);
                indexer.AddFace(subdividedEdgeVertices[bottomVertices + yEnd], currentVertexCount - 1, subdividedEdgeVertices[rightVertices + yEnd]);
                indexer.AddFace(subdividedEdgeVertices[bottomVertices + yEnd], subdividedEdgeVertices[rightVertices + yEnd], bottomRightVertex.index);
            }
            else if (degree == 2)
            {
                indexer.AddFace(subdividedEdgeVertices[leftVertices], topVertex.index, subdividedEdgeVertices[rightVertices]);
                indexer.AddFace(subdividedEdgeVertices[leftVertices + 1], subdividedEdgeVertices[leftVertices], currentVertexCount);
                indexer.AddFace(currentVertexCount, subdividedEdgeVertices[leftVertices], subdividedEdgeVertices[rightVertices]);
                indexer.AddFace(currentVertexCount, subdividedEdgeVertices[rightVertices], subdividedEdgeVertices[rightVertices + 1]);
                indexer.AddFace(bottomLeftVertex.index, subdividedEdgeVertices[leftVertices + 1], subdividedEdgeVertices[bottomVertices]);
                indexer.AddFace(subdividedEdgeVertices[bottomVertices], subdividedEdgeVertices[leftVertices + 1], currentVertexCount);
                indexer.AddFace(subdividedEdgeVertices[bottomVertices], currentVertexCount, subdividedEdgeVertices[bottomVertices + 1]);
                indexer.AddFace(subdividedEdgeVertices[bottomVertices + 1], currentVertexCount, subdividedEdgeVertices[rightVertices + 1]);
                indexer.AddFace(subdividedEdgeVertices[bottomVertices + 1], subdividedEdgeVertices[rightVertices + 1], bottomRightVertex.index);

                subdividedVertexPositions.Add(interpolator(subdividedVertexPositions[subdividedEdgeVertices[leftVertices + 1]], subdividedVertexPositions[subdividedEdgeVertices[rightVertices + 1]], 0.5f));
                ++currentVertexCount;
            }
            else if (degree == 1)
            {
                indexer.AddFace(subdividedEdgeVertices[leftVertices], topVertex.index, subdividedEdgeVertices[rightVertices]);
                indexer.AddFace(bottomLeftVertex.index, subdividedEdgeVertices[leftVertices], subdividedEdgeVertices[bottomVertices]);
                indexer.AddFace(subdividedEdgeVertices[bottomVertices], subdividedEdgeVertices[leftVertices], subdividedEdgeVertices[rightVertices]);
                indexer.AddFace(subdividedEdgeVertices[bottomVertices], subdividedEdgeVertices[rightVertices], bottomRightVertex.index);
            }
        }
示例#5
0
        /// <summary>
        /// Creates a new topology based on the one provided, subdividing each face into multiple smaller faces, and adding extra vertices and edges accordingly.
        /// </summary>
        /// <param name="topology">The original topology to be subdivided.  Cannot contain internal faces with neighbor counts of any value other than 3 or 4.</param>
        /// <param name="vertexPositions">The positions of the original topology's vertices.</param>
        /// <param name="degree">The degree of subdivision, equivalent to the number of additional vertices that will be added along each original edge.  Must be non-negative, and a value of zero will result in an exact duplicate with no subdivision.</param>
        /// <param name="interpolator">The function that interpolates between two vertex positions along an edge for determining the positions of new vertices.  Particularly useful for curved surfaces.</param>
        /// <param name="subdividedTopology">The copied and subdivided topology.</param>
        /// <param name="subdividedVertexPositions">The positions of the subdivided vertices.</param>
        public static void Subdivide(Topology topology, IVertexAttribute <Vector3> vertexPositions, int degree, Func <Vector3, Vector3, float, Vector3> interpolator, out Topology subdividedTopology, out Vector3[] subdividedVertexPositions)
        {
            if (degree < 0)
            {
                throw new ArgumentOutOfRangeException("degree", degree, "Topology subdivision degree cannot be negative.");
            }
            if (degree == 0)
            {
                subdividedTopology = (Topology)topology.Clone();
                var subdividedVertexPositionsArray = new Vector3[topology.vertices.Count];
                vertexPositions.CopyTo(subdividedVertexPositionsArray, 0);
                subdividedVertexPositions = subdividedVertexPositionsArray;
                return;
            }

            int vertexCount       = 0;
            int edgeCount         = 0;
            int internalFaceCount = 0;

            vertexCount += topology.vertices.Count + (topology.halfEdges.Count / 2) * degree;

            foreach (var face in topology.internalFaces)
            {
                switch (face.neighborCount)
                {
                case 3:
                    vertexCount       += (degree * (degree - 1)) / 2;
                    edgeCount         += (degree + 1) * (degree + 1) * 3;
                    internalFaceCount += (degree + 1) * (degree + 1);
                    break;

                case 4:
                    vertexCount       += degree * degree;
                    edgeCount         += (degree + 1) * (degree + 1) * 4;
                    internalFaceCount += (degree + 1) * (degree + 1);
                    break;

                default:
                    throw new InvalidOperationException("Cannot subdivide a face with anything other than 3 or 4 sides.");
                }
            }

            foreach (var face in topology.externalFaces)
            {
                edgeCount += face.neighborCount * (degree + 1);
            }

            var indexer = new ManualFaceNeighborIndexer(vertexCount, edgeCount, internalFaceCount, topology.externalFaces.Count);
            var subdividedVertexPositionsList = new List <Vector3>();

            foreach (var vertex in topology.vertices)
            {
                subdividedVertexPositionsList.Add(vertexPositions[vertex]);
            }

            var currentVertexCount = topology.vertices.Count;

            var dt   = 1f / (degree + 1);
            var tEnd = 1f - dt * 0.5f;

            var subdividedEdgeVertices = new int[topology.halfEdges.Count * degree];

            foreach (var edge in topology.vertexEdges)
            {
                if (edge.isFirstTwin)
                {
                    var p0 = vertexPositions[edge.nearVertex];
                    var p1 = vertexPositions[edge.farVertex];
                    var t  = dt;
                    var subdividedEdgeVertexIndex = edge.index * degree;
                    while (t < tEnd)
                    {
                        subdividedEdgeVertices[subdividedEdgeVertexIndex++] = currentVertexCount++;
                        subdividedVertexPositionsList.Add(interpolator(p0, p1, t));
                        t += dt;
                    }
                }
                else
                {
                    var subdividedEdgeVertexIndex     = edge.index * degree;
                    var subdividedTwinEdgeVertexIndex = (edge.twinIndex + 1) * degree - 1;
                    for (int i = 0; i < degree; ++i)
                    {
                        subdividedEdgeVertices[subdividedEdgeVertexIndex + i] = subdividedEdgeVertices[subdividedTwinEdgeVertexIndex - i];
                    }
                }
            }

            foreach (var face in topology.internalFaces)
            {
                switch (face.neighborCount)
                {
                case 3:
                    SubdivideTriangle(indexer, face, degree, interpolator, subdividedVertexPositionsList, subdividedEdgeVertices, ref currentVertexCount);
                    break;

                case 4:
                    SubdivideQuadrilateral(indexer, face, degree, interpolator, subdividedVertexPositionsList, subdividedEdgeVertices, ref currentVertexCount);
                    break;

                default:
                    throw new InvalidOperationException("Cannot subdivide a face with anything other than 3 or 4 sides.");
                }
            }

            subdividedTopology        = TopologyUtility.BuildTopology(indexer);
            subdividedVertexPositions = subdividedVertexPositionsList.ToArray();
        }
示例#6
0
        private static void SubdivideQuadrilateral(ManualFaceNeighborIndexer indexer, Topology.Face face, int degree, Func <Vector3, Vector3, float, Vector3> interpolator, List <Vector3> subdividedVertexPositions, int[] subdividedEdgeVertices, ref int currentVertexCount)
        {
            var topEdge    = face.firstEdge;
            var rightEdge  = topEdge.next;
            var bottomEdge = rightEdge.next;
            var leftEdge   = bottomEdge.next;

            var topLeftVertex     = leftEdge.vertex;
            var topRightVertex    = topEdge.vertex;
            var bottomRightVertex = rightEdge.vertex;
            var bottomLeftVertex  = bottomEdge.vertex;

            int topVertices    = topEdge.index * degree;          // Progresses from top left vertex over to top right vertex.
            int bottomVertices = bottomEdge.twinIndex * degree;   // Progresses from bottom left vertex over to bottom right vertex.
            int rightVertices  = rightEdge.index * degree;        // Progresses from top right vertex down to bottom right vertex.
            int leftVertices   = leftEdge.twinIndex * degree;     // Progresses from top left vertex down to bottom left vertex.

            var dt = 1f / (degree + 1);

            if (degree > 2)
            {
                int yEnd = degree - 1;
                int xEnd = degree - 1;

                float   t;
                Vector3 p0;
                Vector3 p1;

                // Top row of subdivided faces
                indexer.AddFace(topLeftVertex.index, subdividedEdgeVertices[topVertices], currentVertexCount, subdividedEdgeVertices[leftVertices]);
                for (int x = 0; x < xEnd; ++x)
                {
                    indexer.AddFace(subdividedEdgeVertices[topVertices + x], subdividedEdgeVertices[topVertices + x + 1], currentVertexCount + x + 1, currentVertexCount + x);
                }
                indexer.AddFace(subdividedEdgeVertices[topVertices + xEnd], topRightVertex.index, subdividedEdgeVertices[rightVertices], currentVertexCount + xEnd);

                // Middle rows of subdivided faces
                for (int y = 0; y < yEnd; ++y)
                {
                    var rowFirstVertexIndex = currentVertexCount + y * degree;
                    indexer.AddFace(subdividedEdgeVertices[leftVertices + y], rowFirstVertexIndex, rowFirstVertexIndex + degree, subdividedEdgeVertices[leftVertices + y + 1]);
                    for (int x = 0; x < xEnd; ++x)
                    {
                        indexer.AddFace(rowFirstVertexIndex + x, rowFirstVertexIndex + x + 1, rowFirstVertexIndex + degree + x + 1, rowFirstVertexIndex + degree + x);
                    }
                    indexer.AddFace(rowFirstVertexIndex + xEnd, subdividedEdgeVertices[rightVertices + y], subdividedEdgeVertices[rightVertices + y + 1], rowFirstVertexIndex + degree + xEnd);
                }

                // Bottom row of subdivided faces
                var lastRowFirstVertexIndex = currentVertexCount + yEnd * degree;
                indexer.AddFace(subdividedEdgeVertices[leftVertices + yEnd], lastRowFirstVertexIndex, subdividedEdgeVertices[bottomVertices], bottomLeftVertex.index);
                for (int x = 0; x < xEnd; ++x)
                {
                    indexer.AddFace(lastRowFirstVertexIndex + x, lastRowFirstVertexIndex + x + 1, subdividedEdgeVertices[bottomVertices + x + 1], subdividedEdgeVertices[bottomVertices + x]);
                }
                indexer.AddFace(lastRowFirstVertexIndex + xEnd, subdividedEdgeVertices[rightVertices + yEnd], bottomRightVertex.index, subdividedEdgeVertices[bottomVertices + xEnd]);

                // Subdivided vertices
                for (int y = 0; y <= yEnd; ++y)
                {
                    t  = dt;
                    p0 = subdividedVertexPositions[subdividedEdgeVertices[leftVertices + y]];
                    p1 = subdividedVertexPositions[subdividedEdgeVertices[rightVertices + y]];

                    for (int x = 0; x <= xEnd; ++x)
                    {
                        subdividedVertexPositions.Add(interpolator(p0, p1, t));
                        t += dt;
                    }
                }
                currentVertexCount += degree * degree;
            }
            else if (degree == 2)
            {
                indexer.AddFace(topLeftVertex.index, subdividedEdgeVertices[topVertices], currentVertexCount, subdividedEdgeVertices[leftVertices]);
                indexer.AddFace(subdividedEdgeVertices[topVertices], subdividedEdgeVertices[topVertices + 1], currentVertexCount + 1, currentVertexCount);
                indexer.AddFace(subdividedEdgeVertices[topVertices + 1], topRightVertex.index, subdividedEdgeVertices[rightVertices], currentVertexCount + 1);

                indexer.AddFace(subdividedEdgeVertices[leftVertices], currentVertexCount, currentVertexCount + 2, subdividedEdgeVertices[leftVertices + 1]);
                indexer.AddFace(currentVertexCount, currentVertexCount + 1, currentVertexCount + 3, currentVertexCount + 2);
                indexer.AddFace(currentVertexCount + 1, subdividedEdgeVertices[rightVertices], subdividedEdgeVertices[rightVertices + 1], currentVertexCount + 3);

                indexer.AddFace(subdividedEdgeVertices[leftVertices + 1], currentVertexCount + 2, subdividedEdgeVertices[bottomVertices], bottomLeftVertex.index);
                indexer.AddFace(currentVertexCount + 2, currentVertexCount + 3, subdividedEdgeVertices[bottomVertices + 1], subdividedEdgeVertices[bottomVertices]);
                indexer.AddFace(currentVertexCount + 3, subdividedEdgeVertices[rightVertices + 1], bottomRightVertex.index, subdividedEdgeVertices[bottomVertices + 1]);

                subdividedVertexPositions.Add(interpolator(subdividedVertexPositions[subdividedEdgeVertices[leftVertices]], subdividedVertexPositions[subdividedEdgeVertices[rightVertices]], dt));
                subdividedVertexPositions.Add(interpolator(subdividedVertexPositions[subdividedEdgeVertices[leftVertices]], subdividedVertexPositions[subdividedEdgeVertices[rightVertices]], dt + dt));
                subdividedVertexPositions.Add(interpolator(subdividedVertexPositions[subdividedEdgeVertices[leftVertices + 1]], subdividedVertexPositions[subdividedEdgeVertices[rightVertices + 1]], dt));
                subdividedVertexPositions.Add(interpolator(subdividedVertexPositions[subdividedEdgeVertices[leftVertices + 1]], subdividedVertexPositions[subdividedEdgeVertices[rightVertices + 1]], dt + dt));
                currentVertexCount += 4;
            }
            else if (degree == 1)
            {
                indexer.AddFace(topLeftVertex.index, subdividedEdgeVertices[topVertices], currentVertexCount, subdividedEdgeVertices[leftVertices]);
                indexer.AddFace(subdividedEdgeVertices[topVertices], topRightVertex.index, subdividedEdgeVertices[rightVertices], currentVertexCount);
                indexer.AddFace(subdividedEdgeVertices[leftVertices], currentVertexCount, subdividedEdgeVertices[bottomVertices], bottomLeftVertex.index);
                indexer.AddFace(currentVertexCount, subdividedEdgeVertices[rightVertices], bottomRightVertex.index, subdividedEdgeVertices[bottomVertices]);

                subdividedVertexPositions.Add(interpolator(subdividedVertexPositions[subdividedEdgeVertices[leftVertices]], subdividedVertexPositions[subdividedEdgeVertices[rightVertices]], dt));
                ++currentVertexCount;
            }
        }
        /// <summary>
        /// Creates a manifold, consisting of a topology and vertex positions, in the shape of an icosahedron.
        /// </summary>
        /// <param name="surface">The spherical surface describing the overall shape of the manifold.</param>
        /// <param name="topology">The topology created.</param>
        /// <param name="vertexPositions">The vertex positions created.</param>
        public static void CreateIcosahedron(SphericalSurface surface, out Topology topology, out Vector3[] vertexPositions)
        {
            var latitude     = Mathf.Atan2(1, 2);
            var longitude    = Mathf.PI * 0.2f;
            var cosLat       = Mathf.Cos(latitude);
            var scaledCosLat = surface.radius * cosLat;

            var x0 = 0.0f;
            var x1 = scaledCosLat * Mathf.Sin(longitude);
            var x2 = scaledCosLat * Mathf.Sin(longitude * 2.0f);
            var y0 = +surface.radius;
            var y1 = surface.radius * Mathf.Sin(latitude);
            var y2 = -surface.radius;
            var z0 = scaledCosLat;
            var z1 = scaledCosLat * Mathf.Cos(longitude);
            var z2 = scaledCosLat * Mathf.Cos(longitude * 2.0f);

            vertexPositions     = new Vector3[12];
            vertexPositions[0]  = new Vector3(x0, y0, 0f);
            vertexPositions[1]  = new Vector3(x0, +y1, -z0);
            vertexPositions[2]  = new Vector3(-x2, +y1, -z2);
            vertexPositions[3]  = new Vector3(-x1, +y1, +z1);
            vertexPositions[4]  = new Vector3(+x1, +y1, +z1);
            vertexPositions[5]  = new Vector3(+x2, +y1, -z2);
            vertexPositions[6]  = new Vector3(x0, -y1, +z0);
            vertexPositions[7]  = new Vector3(-x2, -y1, +z2);
            vertexPositions[8]  = new Vector3(-x1, -y1, -z1);
            vertexPositions[9]  = new Vector3(+x1, -y1, -z1);
            vertexPositions[10] = new Vector3(+x2, -y1, +z2);
            vertexPositions[11] = new Vector3(x0, y2, 0f);

            var orientation = surface.orientation;

            for (int i = 0; i < vertexPositions.Length; ++i)
            {
                vertexPositions[i] = orientation * vertexPositions[i];
            }

            var indexer = new ManualFaceNeighborIndexer(12, 60, 20);

            if (!surface.isInverted)
            {
                indexer.AddFace(0, 1, 2);
                indexer.AddFace(0, 2, 3);
                indexer.AddFace(0, 3, 4);
                indexer.AddFace(0, 4, 5);
                indexer.AddFace(0, 5, 1);
                indexer.AddFace(1, 8, 2);
                indexer.AddFace(2, 8, 7);
                indexer.AddFace(2, 7, 3);
                indexer.AddFace(3, 7, 6);
                indexer.AddFace(3, 6, 4);
                indexer.AddFace(4, 6, 10);
                indexer.AddFace(4, 10, 5);
                indexer.AddFace(5, 10, 9);
                indexer.AddFace(5, 9, 1);
                indexer.AddFace(1, 9, 8);
                indexer.AddFace(11, 6, 7);
                indexer.AddFace(11, 7, 8);
                indexer.AddFace(11, 8, 9);
                indexer.AddFace(11, 9, 10);
                indexer.AddFace(11, 10, 6);
            }
            else
            {
                indexer.AddFace(2, 1, 0);
                indexer.AddFace(3, 2, 0);
                indexer.AddFace(4, 3, 0);
                indexer.AddFace(5, 4, 0);
                indexer.AddFace(1, 5, 0);
                indexer.AddFace(2, 8, 1);
                indexer.AddFace(7, 8, 2);
                indexer.AddFace(3, 7, 2);
                indexer.AddFace(6, 7, 3);
                indexer.AddFace(4, 6, 3);
                indexer.AddFace(10, 6, 4);
                indexer.AddFace(5, 10, 4);
                indexer.AddFace(9, 10, 5);
                indexer.AddFace(1, 9, 5);
                indexer.AddFace(8, 9, 1);
                indexer.AddFace(7, 6, 11);
                indexer.AddFace(8, 7, 11);
                indexer.AddFace(9, 8, 11);
                indexer.AddFace(10, 9, 11);
                indexer.AddFace(6, 10, 11);
            }

            topology = TopologyUtility.BuildTopology(indexer);
        }