/// <summary> /// Generates a cone of height and radius 2. If the <see cref="sideCount"/> is 3, generates a triangular-based pyramid. /// </summary> /// <param name="sideCount">Side count for the cone (if 3, generates a triangular-based pyramid.).</param> /// <returns>Polygons to be supplied to a brush.</returns> public static Polygon[] GenerateCone(int sideCount = 20) { Polygon[] polygons = new Polygon[sideCount * 2]; // the cone generator will create a slightly distorted off-center output for 3 sides. // if the user sets the side count to 3 we will generate a triangular-based pyramid. if (sideCount == 3) { polygons = new Polygon[sideCount + 1]; polygons[0] = new Polygon(new Vertex[] { new Vertex(new Vector3(0, -1, -1), new Vector3(0.8402f, 0.2425f, -0.4851f), new Vector2(0.0000f, 0.0000f)), new Vertex(new Vector3(0, 1, 0), new Vector3(0.8402f, 0.2425f, -0.4851f), new Vector2(0.5000f, 1.0000f)), new Vertex(new Vector3(1, -1, 1), new Vector3(0.8402f, 0.2425f, -0.4851f), new Vector2(1.0000f, 0.0000f)), }, null, false, false); polygons[1] = new Polygon(new Vertex[] { new Vertex(new Vector3(1, -1, 1), new Vector3(-0.0000f, 0.2425f, 0.9701f), new Vector2(0.0000f, 0.0000f)), new Vertex(new Vector3(0, 1, 0), new Vector3(-0.0000f, 0.2425f, 0.9701f), new Vector2(0.5000f, 1.0000f)), new Vertex(new Vector3(-1, -1, 1), new Vector3(-0.0000f, 0.2425f, 0.9701f), new Vector2(1.0000f, 0.0000f)), }, null, false, false); polygons[2] = new Polygon(new Vertex[] { new Vertex(new Vector3(-1, -1, 1), new Vector3(-0.8402f, 0.2425f, -0.4851f), new Vector2(0.0000f, 0.0000f)), new Vertex(new Vector3(0, 1, 0), new Vector3(-0.8402f, 0.2425f, -0.4851f), new Vector2(0.5000f, 1.0000f)), new Vertex(new Vector3(0, -1, -1), new Vector3(-0.8402f, 0.2425f, -0.4851f), new Vector2(1.0000f, 0.0000f)), }, null, false, false); polygons[3] = new Polygon(new Vertex[] { new Vertex(new Vector3(0, -1, -1), Vector3.down, new Vector2(0.0000f, 0.0000f)), new Vertex(new Vector3(1, -1, 1), Vector3.down, new Vector2(1.0000f, 1.0000f)), new Vertex(new Vector3(-1, -1, 1), Vector3.down, new Vector2(1.0000f, 0.0000f)), }, null, false, false); return(polygons); } float angleDelta = Mathf.PI * 2 / sideCount; for (int i = 0; i < sideCount; i++) { Vector3 normal = new Vector3(Mathf.Sin((i + 0.5f) * angleDelta), 0, Mathf.Cos((i + 0.5f) * angleDelta)); polygons[i] = new Polygon(new Vertex[] { new Vertex(new Vector3(Mathf.Sin(i * angleDelta), -1, Mathf.Cos(i * angleDelta)), normal, new Vector2(i * (1f / sideCount), 0)), new Vertex(new Vector3(Mathf.Sin((i + 1) * angleDelta), -1, Mathf.Cos((i + 1) * angleDelta)), normal, new Vector2((i + 1) * (1f / sideCount), 0)), new Vertex(new Vector3(0, 1, 0), normal, new Vector2((((i + 1) * (1f / sideCount)) + (i * (1f / sideCount))) / 2.0f, 1.0f)), }, null, false, false); } Vertex capCenterVertex = new Vertex(new Vector3(0, -1, 0), Vector3.down, new Vector2(0, 0)); for (int i = 0; i < sideCount; i++) { Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * -angleDelta), -1, Mathf.Cos(i * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta))); Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i + 1) * -angleDelta), -1, Mathf.Cos((i + 1) * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin((i + 1) * angleDelta), Mathf.Cos((i + 1) * angleDelta))); Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() }; polygons[sideCount + i] = new Polygon(capVertices, null, false, false); } return(polygons); }
/// <summary> /// Generates an ico-sphere of radius 2. Unlike a polar-sphere this has a more even distribution of vertices. /// </summary> /// <returns>Polygons to be supplied to a brush.</returns> /// <param name="iterationCount">Number of times the surface is subdivided, values of 1 or 2 are recommended.</param> public static Polygon[] GenerateIcoSphere(int iterationCount) { // Derived from http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html float longestDimension = (1 + Mathf.Sqrt(5f)) / 2f; Vector3 sourceVector = new Vector3(0, 1, longestDimension); // Make the longest dimension 1, so the icosphere fits in a 2,2,2 cube sourceVector.Normalize(); Vertex[] vertices = new Vertex[] { new Vertex(new Vector3(-sourceVector.y, +sourceVector.z, sourceVector.x), Vector3.zero, Vector2.zero), new Vertex(new Vector3(sourceVector.y, +sourceVector.z, sourceVector.x), Vector3.zero, Vector2.zero), new Vertex(new Vector3(-sourceVector.y, -sourceVector.z, sourceVector.x), Vector3.zero, Vector2.zero), new Vertex(new Vector3(sourceVector.y, -sourceVector.z, sourceVector.x), Vector3.zero, Vector2.zero), new Vertex(new Vector3(sourceVector.x, -sourceVector.y, +sourceVector.z), Vector3.zero, Vector2.zero), new Vertex(new Vector3(sourceVector.x, +sourceVector.y, +sourceVector.z), Vector3.zero, Vector2.zero), new Vertex(new Vector3(sourceVector.x, -sourceVector.y, -sourceVector.z), Vector3.zero, Vector2.zero), new Vertex(new Vector3(sourceVector.x, +sourceVector.y, -sourceVector.z), Vector3.zero, Vector2.zero), new Vertex(new Vector3(+sourceVector.z, sourceVector.x, -sourceVector.y), Vector3.zero, Vector2.zero), new Vertex(new Vector3(+sourceVector.z, sourceVector.x, +sourceVector.y), Vector3.zero, Vector2.zero), new Vertex(new Vector3(-sourceVector.z, sourceVector.x, -sourceVector.y), Vector3.zero, Vector2.zero), new Vertex(new Vector3(-sourceVector.z, sourceVector.x, +sourceVector.y), Vector3.zero, Vector2.zero), }; Polygon[] polygons = new Polygon[] { new Polygon(new Vertex[] { vertices[0].DeepCopy(), vertices[1].DeepCopy(), vertices[7].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[0].DeepCopy(), vertices[5].DeepCopy(), vertices[1].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[0].DeepCopy(), vertices[7].DeepCopy(), vertices[10].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[0].DeepCopy(), vertices[10].DeepCopy(), vertices[11].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[0].DeepCopy(), vertices[11].DeepCopy(), vertices[5].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[7].DeepCopy(), vertices[1].DeepCopy(), vertices[8].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[1].DeepCopy(), vertices[5].DeepCopy(), vertices[9].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[10].DeepCopy(), vertices[7].DeepCopy(), vertices[6].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[11].DeepCopy(), vertices[10].DeepCopy(), vertices[2].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[5].DeepCopy(), vertices[11].DeepCopy(), vertices[4].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[3].DeepCopy(), vertices[2].DeepCopy(), vertices[6].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[3].DeepCopy(), vertices[4].DeepCopy(), vertices[2].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[3].DeepCopy(), vertices[6].DeepCopy(), vertices[8].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[3].DeepCopy(), vertices[8].DeepCopy(), vertices[9].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[3].DeepCopy(), vertices[9].DeepCopy(), vertices[4].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[6].DeepCopy(), vertices[2].DeepCopy(), vertices[10].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[2].DeepCopy(), vertices[4].DeepCopy(), vertices[11].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[8].DeepCopy(), vertices[6].DeepCopy(), vertices[7].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[9].DeepCopy(), vertices[8].DeepCopy(), vertices[1].DeepCopy() }, null, false, false), new Polygon(new Vertex[] { vertices[4].DeepCopy(), vertices[9].DeepCopy(), vertices[5].DeepCopy() }, null, false, false), }; // Refine for (int i = 0; i < iterationCount; i++) { Polygon[] newPolygons = new Polygon[polygons.Length * 4]; for (int j = 0; j < polygons.Length; j++) { Vertex a = Vertex.Lerp(polygons[j].Vertices[0], polygons[j].Vertices[1], 0.5f); Vertex b = Vertex.Lerp(polygons[j].Vertices[1], polygons[j].Vertices[2], 0.5f); Vertex c = Vertex.Lerp(polygons[j].Vertices[2], polygons[j].Vertices[0], 0.5f); a.Position = a.Position.normalized; b.Position = b.Position.normalized; c.Position = c.Position.normalized; newPolygons[j * 4 + 0] = new Polygon(new Vertex[] { polygons[j].Vertices[0].DeepCopy(), a.DeepCopy(), c.DeepCopy() }, null, false, false); newPolygons[j * 4 + 1] = new Polygon(new Vertex[] { polygons[j].Vertices[1].DeepCopy(), b.DeepCopy(), a.DeepCopy() }, null, false, false); newPolygons[j * 4 + 2] = new Polygon(new Vertex[] { polygons[j].Vertices[2].DeepCopy(), c.DeepCopy(), b.DeepCopy() }, null, false, false); newPolygons[j * 4 + 3] = new Polygon(new Vertex[] { a.DeepCopy(), b.DeepCopy(), c.DeepCopy() }, null, false, false); } polygons = newPolygons; } for (int i = 0; i < polygons.Length; i++) { bool anyAboveHalf = false; for (int j = 0; j < polygons[i].Vertices.Length; j++) { Vector3 normal = polygons[i].Vertices[j].Position.normalized; polygons[i].Vertices[j].Normal = normal; float piReciprocal = 1f / Mathf.PI; float u = 0.5f - 0.5f * Mathf.Atan2(normal.x, -normal.z) * piReciprocal; float v = 1f - Mathf.Acos(normal.y) * piReciprocal; if (Mathf.Abs(u) < 0.01f || Mathf.Abs(1 - Mathf.Abs(u)) < 0.01f) { if (polygons[i].Plane.normal.x > 0) { u = 0; } else { u = 1; } } if (u > 0.75f) { anyAboveHalf = true; } //Debug.Log(u); polygons[i].Vertices[j].UV = new Vector2(u, v); //const float kOneOverPi = 1.0 / 3.14159265; //float u = 0.5 - 0.5 * atan(N.x, -N.z) * kOneOverPi; //float v = 1.0 - acos(N.y) * kOneOverPi; } if (anyAboveHalf) { for (int j = 0; j < polygons[i].Vertices.Length; j++) { Vector2 uv = polygons[i].Vertices[j].UV; if (uv.x < 0.5f) { uv.x += 1; } polygons[i].Vertices[j].UV = uv; } } } return(polygons); }
/// <summary> /// Generates a sphere of radius 2 /// </summary> /// <returns>Polygons to be supplied to a brush.</returns> /// <param name="lateralCount">Vertex count up from the south pole to the north pole.</param> /// <param name="longitudinalCount">Vertex count around the sphere equator.</param> public static Polygon[] GeneratePolarSphere(int lateralCount = 6, int longitudinalCount = 12) { Polygon[] polygons = new Polygon[lateralCount * longitudinalCount]; float angleDelta = 1f / lateralCount; float longitudinalDelta = 1f / longitudinalCount; // Generate tris for the top and bottom, then quads for the rest for (int i = 0; i < lateralCount; i++) { for (int j = 0; j < longitudinalCount; j++) { Vertex[] vertices; if (i == lateralCount - 1) { vertices = new Vertex[] { new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j + 1) * longitudinalDelta), Mathf.Cos(Mathf.PI * i * angleDelta), Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j + 1) * longitudinalDelta) ), new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j + 1) * longitudinalDelta), Mathf.Cos(Mathf.PI * i * angleDelta), Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j + 1) * longitudinalDelta) ), new Vector2(i * (1f / lateralCount), (j + 1) * (1f / longitudinalCount))), new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j + 1) * longitudinalDelta), Mathf.Cos(Mathf.PI * (i + 1) * angleDelta), Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j + 1) * longitudinalDelta) ), new Vector3(Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j + 1) * longitudinalDelta), Mathf.Cos(Mathf.PI * (i + 1) * angleDelta), Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j + 1) * longitudinalDelta) ), new Vector2((i + 1) * (1f / lateralCount), (j + 1) * (1f / longitudinalCount))), new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), Mathf.Cos(Mathf.PI * i * angleDelta), Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) ), new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), Mathf.Cos(Mathf.PI * i * angleDelta), Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) ), new Vector2(i * (1f / lateralCount), j * (1f / longitudinalCount))), }; } else if (i > 0) { vertices = new Vertex[] { new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j + 1) * longitudinalDelta), Mathf.Cos(Mathf.PI * i * angleDelta), Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j + 1) * longitudinalDelta) ), new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j + 1) * longitudinalDelta), Mathf.Cos(Mathf.PI * i * angleDelta), Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j + 1) * longitudinalDelta) ), new Vector2(i * (1f / lateralCount), (j + 1) * (1f / longitudinalCount))), new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j + 1) * longitudinalDelta), Mathf.Cos(Mathf.PI * (i + 1) * angleDelta), Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j + 1) * longitudinalDelta) ), new Vector3(Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j + 1) * longitudinalDelta), Mathf.Cos(Mathf.PI * (i + 1) * angleDelta), Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j + 1) * longitudinalDelta) ), new Vector2((i + 1) * (1f / lateralCount), (j + 1) * (1f / longitudinalCount))), new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), Mathf.Cos(Mathf.PI * (i + 1) * angleDelta), Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) ), new Vector3(Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), Mathf.Cos(Mathf.PI * (i + 1) * angleDelta), Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) ), new Vector2((i + 1) * (1f / lateralCount), j * (1f / longitudinalCount))), new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), Mathf.Cos(Mathf.PI * i * angleDelta), Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) ), new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), Mathf.Cos(Mathf.PI * i * angleDelta), Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) ), new Vector2(i * (1f / lateralCount), j * (1f / longitudinalCount))), }; } else // i == 0 { vertices = new Vertex[] { new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j + 1) * longitudinalDelta), Mathf.Cos(Mathf.PI * i * angleDelta), Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j + 1) * longitudinalDelta) ), new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j + 1) * longitudinalDelta), Mathf.Cos(Mathf.PI * i * angleDelta), Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j + 1) * longitudinalDelta) ), new Vector2(i * (1f / lateralCount), (j + 1) * (1f / longitudinalCount))), new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j + 1) * longitudinalDelta), Mathf.Cos(Mathf.PI * (i + 1) * angleDelta), Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j + 1) * longitudinalDelta) ), new Vector3(Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j + 1) * longitudinalDelta), Mathf.Cos(Mathf.PI * (i + 1) * angleDelta), Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j + 1) * longitudinalDelta) ), new Vector2((i + 1) * (1f / lateralCount), (j + 1) * (1f / longitudinalCount))), new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), Mathf.Cos(Mathf.PI * (i + 1) * angleDelta), Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) ), new Vector3(Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta), Mathf.Cos(Mathf.PI * (i + 1) * angleDelta), Mathf.Sin(Mathf.PI * (i + 1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta) ), new Vector2((i + 1) * (1f / lateralCount), j * (1f / longitudinalCount))), }; } for (int d = 0; d < vertices.Length; d++) { vertices[d].UV = new Vector2(vertices[d].UV.y, 1 - vertices[d].UV.x); } polygons[i + j * lateralCount] = new Polygon(vertices, null, false, false); } } return(polygons); }
/// <summary> /// Exports the supplied polygons to a OBJ format string, typically you'll use ExportToFile rather than ExportToString /// </summary> /// <returns>The OBJ file contents.</returns> /// <param name="transform">If a transform is provided, the vertex positions and normals are converted from world space to local to the transform.</param> /// <param name="polygons">Polygons to export.</param> /// <param name="defaultMaterial">Default material to use if none present on polygon.</param> public static string ExportToString(Transform transform, List <Polygon> polygons, Material defaultMaterial) { // If a transform is provided, convert the world positions and normals to local to the transform if (transform != null) { for (int polygonIndex = 0; polygonIndex < polygons.Count; polygonIndex++) { Polygon polygon = polygons[polygonIndex]; for (int vertexIndex = 0; vertexIndex < polygon.Vertices.Length; vertexIndex++) { Vertex vertex = polygon.Vertices[vertexIndex]; vertex.Position = transform.InverseTransformPoint(vertex.Position); vertex.Normal = transform.InverseTransformDirection(vertex.Normal); } } } // Create polygon subsets for each material Dictionary <Material, List <Polygon> > polygonMaterialTable = new Dictionary <Material, List <Polygon> >(); // Iterate through every polygon adding it to the appropiate material list foreach (Polygon polygon in polygons) { if (polygon.UserExcludeFromFinal) { continue; } Material material = polygon.Material; if (material == null) { material = defaultMaterial; } if (!polygonMaterialTable.ContainsKey(material)) { polygonMaterialTable.Add(material, new List <Polygon>()); } polygonMaterialTable[material].Add(polygon); } // Use a string builder as this should allow faster concatenation StringBuilder stringBuilder = new StringBuilder(); OBJVertexList vertexList = new OBJVertexList(); int positionIndexOffset = 0; int uvIndexOffset = 0; int normalIndexOffset = 0; int meshIndex = 1; // Create a separate mesh for polygons of each material so that we batch by material foreach (KeyValuePair <Material, List <Polygon> > polygonMaterialGroup in polygonMaterialTable) { List <List <OBJFaceVertex> > faces = new List <List <OBJFaceVertex> >(polygonMaterialGroup.Value.Count); // Iterate through every polygon and triangulate foreach (Polygon polygon in polygonMaterialGroup.Value) { List <OBJFaceVertex> faceVertices = new List <OBJFaceVertex>(polygon.Vertices.Length); for (int i = 0; i < polygon.Vertices.Length; i++) { OBJFaceVertex faceVertex = vertexList.AddOrGet(polygon.Vertices[i]); faceVertices.Add(faceVertex); } faces.Add(faceVertices); } List <Vector3> positions = vertexList.Positions; List <Vector2> uvs = vertexList.UVs; List <Vector3> normals = vertexList.Normals; // Start a new group for the mesh stringBuilder.AppendLine("g Mesh" + meshIndex); // Write all the positions stringBuilder.AppendLine("# Vertex Positions: " + (positions.Count)); for (int i = 0; i < positions.Count; i++) { stringBuilder.AppendLine("v " + WriteVector3(positions[i])); } // Write all the texture coordinates (UVs) stringBuilder.AppendLine("# Vertex UVs: " + (uvs.Count)); for (int i = 0; i < uvs.Count; i++) { stringBuilder.AppendLine("vt " + WriteVector2(uvs[i])); } // Write all the normals stringBuilder.AppendLine("# Vertex Normals: " + (normals.Count)); for (int i = 0; i < normals.Count; i++) { stringBuilder.AppendLine("vn " + WriteVector3(normals[i])); } // Write all the faces stringBuilder.AppendLine("# Faces: " + faces.Count); for (int i = 0; i < faces.Count; i++) { stringBuilder.Append("f "); for (int j = faces[i].Count - 1; j >= 0; j--) { stringBuilder.Append((faces[i][j].PositionIndex + positionIndexOffset) + "/" + (faces[i][j].UVIndex + uvIndexOffset) + "/" + (faces[i][j].NormalIndex + normalIndexOffset) + " "); } stringBuilder.AppendLine(); } // Add some padding between this and the next mesh stringBuilder.AppendLine(); stringBuilder.AppendLine(); meshIndex++; // Update the offsets so that the next pass only writes new vertex information positionIndexOffset += positions.Count; uvIndexOffset += uvs.Count; normalIndexOffset += normals.Count; vertexList.ResetForNext(); } return(stringBuilder.ToString()); }