using System; using System.Collections.Generic; using UnityEngine; using Random = UnityEngine.Random; namespace NewHorizons.Builder.Body.Geometry { public static class Icosphere { private static readonly float t = (1f + Mathf.Sqrt(5f)) / 2f; // By subdivisions, will add to this to memoize computation of icospheres private static readonly List vertices = new List { new[] { new Vector3(-1, t, 0).normalized, new Vector3(1, t, 0).normalized, new Vector3(-1, -t, 0).normalized, new Vector3(1, -t, 0).normalized, new Vector3(0, -1, t).normalized, new Vector3(0, 1, t).normalized, new Vector3(0, -1, -t).normalized, new Vector3(0, 1, -t).normalized, new Vector3(t, 0, -1).normalized, new Vector3(t, 0, 1).normalized, new Vector3(-t, 0, -1).normalized, new Vector3(-t, 0, 1).normalized } }; private static readonly List triangles = new List { new[] { 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 } }; public static Mesh Build(int subdivisions, float minHeight, float maxHeight) { var mesh = new Mesh(); if (vertices.Count <= subdivisions) RefineFaces(subdivisions); var verticesToCopy = vertices[subdivisions]; var newVertices = new Vector3[verticesToCopy.Length]; var normals = new Vector3[verticesToCopy.Length]; var uvs = new Vector2[verticesToCopy.Length]; var randomOffset = new Vector3(Random.Range(0, 10f), Random.Range(0, 10f), Random.Range(0, 10f)); for (var i = 0; i < verticesToCopy.Length; i++) { var v = verticesToCopy[i]; var latitude = Mathf.Repeat(Mathf.Rad2Deg * Mathf.Acos(v.z / Mathf.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z)), 180f); var longitude = Mathf.Repeat( Mathf.Rad2Deg * (v.x > 0 ? Mathf.Atan(v.y / v.x) : Mathf.Atan(v.y / v.x) + Mathf.PI) + 90f, 360f); var height = Perlin.Noise(v + randomOffset) * (maxHeight - minHeight) + minHeight; newVertices[i] = verticesToCopy[i] * height; normals[i] = v.normalized; var x = longitude / 360f; var y = latitude / 180f; uvs[i] = new Vector2(x, y); } mesh.vertices = newVertices; mesh.triangles = triangles[subdivisions]; mesh.normals = normals; mesh.uv = uvs; return mesh; } private static void RefineFaces(int level) { if (level < vertices.Count) return; for (var i = vertices.Count - 1; i < level; i++) { // Each triangle will be subdivided into 4 new ones var oldTriangles = triangles[i]; var newTriangles = new int[oldTriangles.Length * 4]; // Making too many vertices but its fine I guess. Three per old triangle. var oldVertices = vertices[i]; var newVertices = new Vector3[oldVertices.Length + oldTriangles.Length]; Array.Copy(oldVertices, newVertices, oldVertices.Length); var v = oldVertices.Length; var newTrianglesIndex = 0; for (var j = 0; j < oldTriangles.Length; j += 3, v += 3) { // Old vertex indices var v0Ind = oldTriangles[j]; var v1Ind = oldTriangles[j + 1]; var v2Ind = oldTriangles[j + 2]; // Old vertices var v0 = oldVertices[v0Ind]; var v1 = oldVertices[v1Ind]; var v2 = oldVertices[v2Ind]; // New vertex indices var aInd = v; var bInd = v + 1; var cInd = v + 2; // New vertices var a = GetMidPoint(v0, v1); var b = GetMidPoint(v1, v2); var c = GetMidPoint(v2, v0); // Add the three new vertices to the vertex list newVertices[aInd] = a; newVertices[bInd] = b; newVertices[cInd] = c; // Add the four triangles newTriangles[newTrianglesIndex++] = v0Ind; newTriangles[newTrianglesIndex++] = aInd; newTriangles[newTrianglesIndex++] = cInd; newTriangles[newTrianglesIndex++] = v1Ind; newTriangles[newTrianglesIndex++] = bInd; newTriangles[newTrianglesIndex++] = aInd; newTriangles[newTrianglesIndex++] = v2Ind; newTriangles[newTrianglesIndex++] = cInd; newTriangles[newTrianglesIndex++] = bInd; newTriangles[newTrianglesIndex++] = aInd; newTriangles[newTrianglesIndex++] = bInd; newTriangles[newTrianglesIndex++] = cInd; } vertices.Add(newVertices); triangles.Add(newTriangles); } } private static Vector3 GetMidPoint(Vector3 a, Vector3 b) => ((a + b) / 2f).normalized; } }