/// <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);
        }
Example #4
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();
        }
 /// <summary>
 /// Creates a topology from the grid specifications of the current surface.
 /// </summary>
 /// <returns>A topology corresponding to the grid specifications of the surface.</returns>
 public Topology CreateTopology()
 {
     return(TopologyUtility.BuildTopology(this));
 }
        /// <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);
        }