/// <summary> /// Generates a sphere by progressively refining a tetrahedral mesh. /// </summary> /// <param name="subdivisionCount">Number of recursive splits to perform on the triangles.</param> /// <param name="vertices">Vertex buffer of the sphere.</param> /// <param name="triangles">Index buffer of the sphere. Each group of three integers represents one triangle.</param> public static void GenerateSphere(int subdivisionCount, out System.Numerics.Vector3[] vertices, out int[] triangles) { //int finalSize = 3 * (4 << (subdivisionCount << 1)); //int secondToLastSize = finalSize >> 2; int finalSize = 3 * (20 << (subdivisionCount * 2)); int secondToLastSize = finalSize >> 2; triangles = new int[finalSize]; //Grabbing a RawList<int> for this purpose is a little gross. Really, just an int[] is desired. var swapTriangles = new int[secondToLastSize]; int[] currentTriangles, nextTriangles; if ((subdivisionCount & 1) == 0) { //Subdivision count is even. //The "currentTriangles" reference will end up being the final container. currentTriangles = triangles; nextTriangles = swapTriangles; } else { //Subdivision count is odd. //The "nextTriangles" reference will end up being the final container. currentTriangles = swapTriangles; nextTriangles = triangles; } //The number of vertices that we'll end up with is known analytically. vertices = new System.Numerics.Vector3[GetExpectedVertexCount(subdivisionCount)]; ////Create the regular tetrahedron vertices. //float x = (float)(1 / Math.Sqrt(3)); //float z = (float)(-1 / (2 * Math.Sqrt(6))); //vertices[0] = System.Numerics.Vector3.Normalize(new System.Numerics.Vector3(0, 0, (float)(Math.Sqrt(2.0 / 3.0) + z))); //vertices[1] = System.Numerics.Vector3.Normalize(new System.Numerics.Vector3(-0.5f * x, -0.5f, z)); //vertices[2] = System.Numerics.Vector3.Normalize(new System.Numerics.Vector3(-0.5f * x, 0.5f, z)); //vertices[3] = System.Numerics.Vector3.Normalize(new System.Numerics.Vector3(x, 0, z)); ////Just treat this array as a list. //int vertexCount = 4; ////Create the regular tetrahedron triangles. ////The winding matters. They should be consistent so that the end result is consistent. //currentTriangles[0] = 0; //currentTriangles[1] = 1; //currentTriangles[2] = 2; //currentTriangles[3] = 0; //currentTriangles[4] = 2; //currentTriangles[5] = 3; //currentTriangles[6] = 1; //currentTriangles[7] = 3; //currentTriangles[8] = 2; //currentTriangles[9] = 0; //currentTriangles[10] = 3; //currentTriangles[11] = 1; //int currentTriangleIndexCount = 12; //int nextTriangleIndexCount = 0; //Create the regular icosahedron vertices. //System.Numerics.Vector3[] vertices = new System.Numerics.Vector3[12]; var goldenRatio = (1 + (float)Math.Sqrt(5)) / 2; float length = (float)Math.Sqrt(1 + goldenRatio * goldenRatio); float x = 1 / length; float y = goldenRatio / length; vertices[0] = new System.Numerics.Vector3(0, x, y); vertices[1] = new System.Numerics.Vector3(0, -x, y); vertices[2] = new System.Numerics.Vector3(0, x, -y); vertices[3] = new System.Numerics.Vector3(0, -x, -y); vertices[4] = new System.Numerics.Vector3(x, y, 0); vertices[5] = new System.Numerics.Vector3(-x, y, 0); vertices[6] = new System.Numerics.Vector3(x, -y, 0); vertices[7] = new System.Numerics.Vector3(-x, -y, 0); vertices[8] = new System.Numerics.Vector3(y, 0, x); vertices[9] = new System.Numerics.Vector3(-y, 0, x); vertices[10] = new System.Numerics.Vector3(y, 0, -x); vertices[11] = new System.Numerics.Vector3(-y, 0, -x); //Just treat this array as a list. int vertexCount = 12; //The winding matters. They should be consistent so that the end result is consistent. //[This was generated using GetConvexHull on the above vertices, so it's known to be consistent with GetConvexHull!] currentTriangles[0] = 8; currentTriangles[1] = 10; currentTriangles[2] = 6; currentTriangles[3] = 2; currentTriangles[4] = 3; currentTriangles[5] = 10; currentTriangles[6] = 8; currentTriangles[7] = 6; currentTriangles[8] = 1; currentTriangles[9] = 9; currentTriangles[10] = 11; currentTriangles[11] = 5; currentTriangles[12] = 6; currentTriangles[13] = 10; currentTriangles[14] = 3; currentTriangles[15] = 9; currentTriangles[16] = 5; currentTriangles[17] = 0; currentTriangles[18] = 2; currentTriangles[19] = 5; currentTriangles[20] = 11; currentTriangles[21] = 8; currentTriangles[22] = 4; currentTriangles[23] = 10; currentTriangles[24] = 8; currentTriangles[25] = 0; currentTriangles[26] = 4; currentTriangles[27] = 2; currentTriangles[28] = 10; currentTriangles[29] = 4; currentTriangles[30] = 2; currentTriangles[31] = 11; currentTriangles[32] = 3; currentTriangles[33] = 8; currentTriangles[34] = 1; currentTriangles[35] = 0; currentTriangles[36] = 9; currentTriangles[37] = 0; currentTriangles[38] = 1; currentTriangles[39] = 5; currentTriangles[40] = 2; currentTriangles[41] = 4; currentTriangles[42] = 5; currentTriangles[43] = 4; currentTriangles[44] = 0; currentTriangles[45] = 9; currentTriangles[46] = 1; currentTriangles[47] = 7; currentTriangles[48] = 6; currentTriangles[49] = 7; currentTriangles[50] = 1; currentTriangles[51] = 9; currentTriangles[52] = 7; currentTriangles[53] = 11; currentTriangles[54] = 11; currentTriangles[55] = 7; currentTriangles[56] = 3; currentTriangles[57] = 6; currentTriangles[58] = 3; currentTriangles[59] = 7; int currentTriangleIndexCount = 60; int nextTriangleIndexCount = 0; var edges = new Dictionary <TriangleMeshConvexContactManifold.Edge, int>(); for (int i = 0; i < subdivisionCount; ++i) { for (int triangleIndex = 0; triangleIndex < currentTriangleIndexCount; triangleIndex += 3) { //For each edge of this triangle, insert a new vertex if the edge hasn't already been taken care of. var aIndex = currentTriangles[triangleIndex]; var bIndex = currentTriangles[triangleIndex + 1]; var cIndex = currentTriangles[triangleIndex + 2]; var a = vertices[aIndex]; var b = vertices[bIndex]; var c = vertices[cIndex]; int abMidIndex; var edge = new TriangleMeshConvexContactManifold.Edge(aIndex, bIndex); if (!edges.TryGetValue(edge, out abMidIndex)) { //This edge hasn't yet been handled by another triangle. //Create a vertex. System.Numerics.Vector3 mid; Vector3Ex.Add(ref a, ref b, out mid); mid.Normalize(); abMidIndex = vertexCount; vertices.Add(ref mid, ref vertexCount); //Mark this edge as handled. edges.Add(edge, abMidIndex); } int bcMidIndex; edge = new TriangleMeshConvexContactManifold.Edge(bIndex, cIndex); if (!edges.TryGetValue(edge, out bcMidIndex)) { //This edge hasn't yet been handled by another triangle. //Create a vertex. System.Numerics.Vector3 mid; Vector3Ex.Add(ref b, ref c, out mid); mid.Normalize(); bcMidIndex = vertexCount; vertices.Add(ref mid, ref vertexCount); //Mark this edge as handled. edges.Add(edge, bcMidIndex); } int acMidIndex; edge = new TriangleMeshConvexContactManifold.Edge(aIndex, cIndex); if (!edges.TryGetValue(edge, out acMidIndex)) { //This edge hasn't yet been handled by another triangle. System.Numerics.Vector3 mid; Vector3Ex.Add(ref a, ref c, out mid); mid.Normalize(); acMidIndex = vertexCount; vertices.Add(ref mid, ref vertexCount); //Mark this edge as handled. edges.Add(edge, acMidIndex); } //Create the new triangles with consistent winding. nextTriangles.Add(aIndex, ref nextTriangleIndexCount); nextTriangles.Add(abMidIndex, ref nextTriangleIndexCount); nextTriangles.Add(acMidIndex, ref nextTriangleIndexCount); nextTriangles.Add(abMidIndex, ref nextTriangleIndexCount); nextTriangles.Add(bIndex, ref nextTriangleIndexCount); nextTriangles.Add(bcMidIndex, ref nextTriangleIndexCount); nextTriangles.Add(abMidIndex, ref nextTriangleIndexCount); nextTriangles.Add(bcMidIndex, ref nextTriangleIndexCount); nextTriangles.Add(acMidIndex, ref nextTriangleIndexCount); nextTriangles.Add(acMidIndex, ref nextTriangleIndexCount); nextTriangles.Add(bcMidIndex, ref nextTriangleIndexCount); nextTriangles.Add(cIndex, ref nextTriangleIndexCount); } //Clear out the edges collection. It will get reused when we iterate through the next deeper set of triangles. edges.Clear(); //Swap the triangle buffer references. currentTriangleIndexCount = nextTriangleIndexCount; nextTriangleIndexCount = 0; var swap = currentTriangles; currentTriangles = nextTriangles; nextTriangles = swap; } //RawList<int> indices = new RawList<int>(SampleTriangleIndices.Length); //RawList<System.Numerics.Vector3> surfacePoints = new RawList<System.Numerics.Vector3>(); //ConvexHullHelper.GetConvexHull(vertices, indices, surfacePoints); //indices.CopyTo(SampleTriangleIndices, 0); }