/// <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); }
/// <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); }