Skip to content

Commit

Permalink
Planet Procedural Mesh Generator
Browse files Browse the repository at this point in the history
  • Loading branch information
Xlinka committed Jun 25, 2024
1 parent 95ace26 commit bb283a0
Show file tree
Hide file tree
Showing 3 changed files with 324 additions and 15 deletions.
169 changes: 169 additions & 0 deletions ProjectObsidian/Components/Mesh/Planet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
using Elements.Assets;
using Elements.Core;
using System;
using System.Collections.Generic;

namespace Obsidian
{
public class Planet : MeshXShape
{
public int Subdivisions;
public float Radius;
public float NoiseScale;
public float NoiseStrength;

public Planet(MeshX mesh, int subdivisions, float radius, float noiseScale, float noiseStrength) : base(mesh)
{
Subdivisions = subdivisions;
Radius = radius;
NoiseScale = noiseScale;
NoiseStrength = noiseStrength;
mesh.Clear();
GeneratePlanet(mesh, Subdivisions, Radius, NoiseScale, NoiseStrength);
}

private void GeneratePlanet(MeshX mesh, int subdivisions, float radius, float noiseScale, float noiseStrength)
{
List<float3> vertices = new List<float3>();
List<int> triangles = new List<int>();

// Generate initial icosahedron
CreateIcosahedron(vertices, triangles);

// Subdivide icosahedron
for (int i = 0; i < subdivisions; i++)
{
Subdivide(vertices, triangles);
}

// Apply noise to vertices
ApplyPerlinNoise(vertices, radius, noiseScale, noiseStrength);

// Assign vertices and triangles to the mesh
mesh.SetVertexCount(vertices.Count);
for (int i = 0; i < vertices.Count; i++)
{
mesh.SetVertex(i, vertices[i]);
}

for (int i = 0; i < triangles.Count; i += 3)
{
mesh.AddTriangle(triangles[i], triangles[i + 1], triangles[i + 2]);
}

mesh.RecalculateNormals(AllTriangles);
}

private void CreateIcosahedron(List<float3> vertices, List<int> triangles)
{
float t = (1.0f + MathX.Sqrt(5.0f)) / 2.0f;

vertices.Add(new float3(-1, t, 0));
vertices.Add(new float3(1, t, 0));
vertices.Add(new float3(-1, -t, 0));
vertices.Add(new float3(1, -t, 0));

vertices.Add(new float3(0, -1, t));
vertices.Add(new float3(0, 1, t));
vertices.Add(new float3(0, -1, -t));
vertices.Add(new float3(0, 1, -t));

vertices.Add(new float3(t, 0, -1));
vertices.Add(new float3(t, 0, 1));
vertices.Add(new float3(-t, 0, -1));
vertices.Add(new float3(-t, 0, 1));

triangles.AddRange(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
});
}

private void Subdivide(List<float3> vertices, List<int> triangles)
{
Dictionary<long, int> midpointCache = new Dictionary<long, int>();
List<int> newTriangles = new List<int>();

for (int i = 0; i < triangles.Count; i += 3)
{
int v1 = triangles[i];
int v2 = triangles[i + 1];
int v3 = triangles[i + 2];

int a = GetMidpoint(midpointCache, vertices, v1, v2);
int b = GetMidpoint(midpointCache, vertices, v2, v3);
int c = GetMidpoint(midpointCache, vertices, v3, v1);

newTriangles.AddRange(new int[] { v1, a, c });
newTriangles.AddRange(new int[] { v2, b, a });
newTriangles.AddRange(new int[] { v3, c, b });
newTriangles.AddRange(new int[] { a, b, c });
}

triangles.Clear();
triangles.AddRange(newTriangles);
}

private int GetMidpoint(Dictionary<long, int> midpointCache, List<float3> vertices, int v1, int v2)
{
long key = ((long)Math.Min(v1, v2) << 32) + Math.Max(v1, v2);

if (midpointCache.TryGetValue(key, out int midpoint))
{
return midpoint;
}

float3 p1 = vertices[v1];
float3 p2 = vertices[v2];
float3 middle = new float3(
(p1.x + p2.x) / 2.0f,
(p1.y + p2.y) / 2.0f,
(p1.z + p2.z) / 2.0f
);

midpoint = vertices.Count;
vertices.Add(middle);

midpointCache[key] = midpoint;
return midpoint;
}

private void ApplyPerlinNoise(List<float3> vertices, float radius, float noiseScale, float noiseStrength)
{
for (int i = 0; i < vertices.Count; i++)
{
float3 vertex = vertices[i];
float noise = NodeExtensions.PerlinNoise(vertex.x * noiseScale, vertex.y * noiseScale, vertex.z * noiseScale);
float displacement = radius + noise * noiseStrength;
vertices[i] = vertex.Normalized * displacement;
}
}

public override void Update()
{
Mesh.RecalculateNormals(AllTriangles);
Mesh.RecalculateTangents(AllTriangles);
}
}
}
62 changes: 62 additions & 0 deletions ProjectObsidian/Components/Mesh/PlanetMesh.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using Elements.Assets;
using Elements.Core;
using FrooxEngine;
using System;

namespace Obsidian
{
[Category(new string[] { "Obsidian/Assets/Procedural Meshes" })]
public class PlanetMesh : ProceduralMesh
{
[Range(1, 8)] public readonly Sync<int> Subdivisions;
[Range(0.5f, 10.0f)] public readonly Sync<float> Radius;
[Range(0.1f, 10.0f)] public readonly Sync<float> NoiseScale;
[Range(0.0f, 2.0f)] public readonly Sync<float> NoiseStrength;

private Planet planet;
private int _subdivisions;
private float _radius;
private float _noiseScale;
private float _noiseStrength;

protected override void OnAwake()
{
base.OnAwake();
Subdivisions.Value = 4;
Radius.Value = 1.0f;
NoiseScale.Value = 1.0f;
NoiseStrength.Value = 0.5f;
}

protected override void PrepareAssetUpdateData()
{
_subdivisions = Subdivisions.Value;
_radius = Radius.Value;
_noiseScale = NoiseScale.Value;
_noiseStrength = NoiseStrength.Value;
}

protected override void ClearMeshData()
{
planet = null;
}

protected override void UpdateMeshData(MeshX meshx)
{
bool value = false;
if (planet == null || planet.Subdivisions != _subdivisions || planet.Radius != _radius || planet.NoiseScale != _noiseScale || planet.NoiseStrength != _noiseStrength)
{
planet?.Remove();
planet = new Planet(meshx, _subdivisions, _radius, _noiseScale, _noiseStrength);
value = true;
}

planet.Subdivisions = Subdivisions.Value;
planet.Radius = Radius.Value;
planet.NoiseScale = NoiseScale.Value;
planet.NoiseStrength = NoiseStrength.Value;
planet.Update();
uploadHint[MeshUploadHint.Flag.Geometry] = value;
}
}
}
108 changes: 93 additions & 15 deletions ProjectObsidian/ProtoFlux/NodeExstensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,98 @@
using FrooxEngine.ProtoFlux;
using FrooxEngine.UIX;

namespace Obsidian;
public static class NodeExtensions
namespace Obsidian
{
public static readonly Dictionary<char, string> CharToMorse = new()
public static class NodeExtensions
{
{'A', ".-"}, {'B', "-..."}, {'C', "-.-."}, {'D', "-.."}, {'E', "."},
{'F', "..-."}, {'G', "--."}, {'H', "...."}, {'I', ".."}, {'J', ".---"},
{'K', "-.-"}, {'L', ".-.."}, {'M', "--"}, {'N', "-."}, {'O', "---"},
{'P', ".--."}, {'Q', "--.-"}, {'R', ".-."}, {'S', "..."}, {'T', "-"},
{'U', "..-"}, {'V', "...-"}, {'W', ".--"}, {'X', "-..-"}, {'Y', "-.--"},
{'Z', "--.."}, {'1', ".----"}, {'2', "..---"}, {'3', "...--"}, {'4', "....-"},
{'5', "....."}, {'6', "-...."}, {'7', "--..."}, {'8', "---.."}, {'9', "----."},
{'0', "-----"}
};

public static readonly Dictionary<string, char> MorseToChar = CharToMorse.ToDictionary(i => i.Value, j => j.Key);
}
public static readonly Dictionary<char, string> CharToMorse = new()
{
{'A', ".-"}, {'B', "-..."}, {'C', "-.-."}, {'D', "-.."}, {'E', "."},
{'F', "..-."}, {'G', "--."}, {'H', "...."}, {'I', ".."}, {'J', ".---"},
{'K', "-.-"}, {'L', ".-.."}, {'M', "--"}, {'N', "-."}, {'O', "---"},
{'P', ".--."}, {'Q', "--.-"}, {'R', ".-."}, {'S', "..."}, {'T', "-"},
{'U', "..-"}, {'V', "...-"}, {'W', ".--"}, {'X', "-..-"}, {'Y', "-.--"},
{'Z', "--.."}, {'1', ".----"}, {'2', "..---"}, {'3', "...--"}, {'4', "....-"},
{'5', "....."}, {'6', "-...."}, {'7', "--..."}, {'8', "---.."}, {'9', "----."},
{'0', "-----"}
};

public static readonly Dictionary<string, char> MorseToChar = CharToMorse.ToDictionary(i => i.Value, j => j.Key);

private static readonly int[] permutation = { 151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,
8,99,37,240,21,10,23,190, 6,148,247,120,234,75,0,26,
197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,
149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,
139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,
230,220,105,92,41,55,46,245,40,244,102,143,54, 65,25,
63,161, 1,216,80,73,209,76,132,187,208,89,18,169,200,
196,135,130,116,188,159,86,164,100,109,198,173,186,3,
64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,
85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,
183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,
155,167, 43,172,9,129,22,39,253,19,98,108,110,79,113,
224,232,178,185, 112,104,218,246,97,228,251,34,242,193,
238,210,144,12,191,179,162,241, 81,51,145,235,249,14,
239,107,49,192,214,31,181,199,106,157,184, 84,204,176,
115,121,50,45,127, 4,150,254,138,236,205,93,222,114,
67,29,24,72,243,141,128,195,78,66,215,61,156,180
};

private static readonly int[] p;

static NodeExtensions()
{
p = new int[512];
for (int x = 0; x < 512; x++)
{
p[x] = permutation[x % 256];
}
}

public static float PerlinNoise(float x, float y, float z)
{
int X = (int)MathX.Floor(x) & 255;
int Y = (int)MathX.Floor(y) & 255;
int Z = (int)MathX.Floor(z) & 255;

x -= (float)MathX.Floor(x);
y -= (float)MathX.Floor(y);
z -= (float)MathX.Floor(z);

float u = Fade(x);
float v = Fade(y);
float w = Fade(z);

int A = p[X] + Y;
int AA = p[A] + Z;
int AB = p[A + 1] + Z;
int B = p[X + 1] + Y;
int BA = p[B] + Z;
int BB = p[B + 1] + Z;

return Lerp(w, Lerp(v, Lerp(u, Grad(p[AA], x, y, z), Grad(p[BA], x - 1, y, z)),
Lerp(u, Grad(p[AB], x, y - 1, z), Grad(p[BB], x - 1, y - 1, z))),
Lerp(v, Lerp(u, Grad(p[AA + 1], x, y, z - 1), Grad(p[BA + 1], x - 1, y, z - 1)),
Lerp(u, Grad(p[AB + 1], x, y - 1, z - 1), Grad(p[BB + 1], x - 1, y - 1, z - 1))));
}

private static float Fade(float t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
}

private static float Lerp(float t, float a, float b)
{
return a + t * (b - a);
}

private static float Grad(int hash, float x, float y, float z)
{
int h = hash & 15;
float u = h < 8 ? x : y;
float v = h < 4 ? y : h == 12 || h == 14 ? x : z;
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
}
}

0 comments on commit bb283a0

Please sign in to comment.