177 lines
6.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Random = UnityEngine.Random;
namespace NewHorizons.Body.Geometry
{
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 List<Vector3[]> vertices = new List<Vector3[]>()
{
new Vector3[]
{
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 List<int[]> triangles = new List<int[]>()
{
new int[]
{
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)
{
Mesh mesh = new Mesh();
if (vertices.Count <= subdivisions)
RefineFaces(subdivisions);
var verticesToCopy = vertices[subdivisions];
Vector3[] newVertices = new Vector3[verticesToCopy.Length];
Vector3[] normals = new Vector3[verticesToCopy.Length];
Vector2[] uvs = new Vector2[verticesToCopy.Length];
var randomOffset = new Vector3(Random.Range(0, 10f), Random.Range(0, 10f), Random.Range(0, 10f));
for(int i = 0; i < verticesToCopy.Length; i++)
{
var v = verticesToCopy[i];
float latitude = Mathf.Repeat(Mathf.Rad2Deg * Mathf.Acos(v.z / Mathf.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z)), 180f);
float longitude = Mathf.Repeat(Mathf.Rad2Deg * (v.x > 0 ? Mathf.Atan(v.y / v.x) : Mathf.Atan(v.y / v.x) + Mathf.PI) + 90f, 360f);
float 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(int i = vertices.Count - 1; i < level; i++)
{
// Each triangle will be subdivided into 4 new ones
int[] oldTriangles = triangles[i];
int[] newTriangles = new int[oldTriangles.Length * 4];
// Making too many vertices but its fine I guess. Three per old triangle.
Vector3[] oldVertices = vertices[i];
Vector3[] newVertices = new Vector3[oldVertices.Length + oldTriangles.Length];
Array.Copy(oldVertices, newVertices, oldVertices.Length);
int v = oldVertices.Length;
int newTrianglesIndex = 0;
for(int 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)
{
return ((a + b) / 2f).normalized;
}
}
}