704 lines
24 KiB
C#
704 lines
24 KiB
C#
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using Godot;
|
||
|
|
using System.Diagnostics;
|
||
|
|
using System.Linq;
|
||
|
|
using System.Threading;
|
||
|
|
using adatonic;
|
||
|
|
using Node = adatonic.Node;
|
||
|
|
using Timer = System.Timers.Timer;
|
||
|
|
|
||
|
|
public class PlanetHelper
|
||
|
|
{
|
||
|
|
public static float RandF(float min, float max)
|
||
|
|
{
|
||
|
|
return min + (max - min) * Random.Shared.NextSingle();
|
||
|
|
}
|
||
|
|
public class PlateData(int Id = 0, Color Color = new(), bool IsLandform = false, List<int> Vertices = null)
|
||
|
|
{
|
||
|
|
public int Id { get; set; } = Id;
|
||
|
|
public Color Color { get; set; } = Color;
|
||
|
|
public bool IsLandform { get; set; } = IsLandform;
|
||
|
|
public List<int> Vertices { get; set; } = Vertices;
|
||
|
|
|
||
|
|
public int CenterVertexId = -1;
|
||
|
|
public float PlateExpansion { get; set; } = RandF(0.5f, 2f);
|
||
|
|
public Vector3 Dir { get; set; } = Vector3.Zero;
|
||
|
|
}
|
||
|
|
|
||
|
|
public class VertexData(int Id = 0, int PlateId = 0, List<int> Neighbours = null, bool StageComplete = false)
|
||
|
|
{
|
||
|
|
public int Id { get; set; } = Id;
|
||
|
|
public int PlateId { get; set; } = PlateId;
|
||
|
|
|
||
|
|
public List<StrainAnalysis> StrainSamples { get; set; } = new();
|
||
|
|
public List<int> Neighbours { get; set; } = Neighbours;
|
||
|
|
public bool StageComplete { get; set; } = StageComplete;
|
||
|
|
public bool IsEdge = false;
|
||
|
|
public bool IsTypeEdge = false;
|
||
|
|
public float EdgeDistance = -1f;
|
||
|
|
public float Height = 0f;
|
||
|
|
}
|
||
|
|
public enum StrainType
|
||
|
|
{
|
||
|
|
Tension,
|
||
|
|
Compression,
|
||
|
|
Shear
|
||
|
|
}
|
||
|
|
public class StrainAnalysis
|
||
|
|
{
|
||
|
|
public float Magnitude;
|
||
|
|
public StrainType Type;
|
||
|
|
public float NormalRate;
|
||
|
|
public float ShearRate;
|
||
|
|
}
|
||
|
|
|
||
|
|
private bool StageComplete = true;
|
||
|
|
private int _plateCount = 14;
|
||
|
|
private float _landRatio = 0.4f;
|
||
|
|
|
||
|
|
public List<PlateData> Plates = new List<PlateData>();
|
||
|
|
public List<VertexData> Vertices = new List<VertexData>();
|
||
|
|
|
||
|
|
public double StageHangTime = 1.0;
|
||
|
|
public bool AutoRun = false;
|
||
|
|
public bool Advance = false;
|
||
|
|
|
||
|
|
public int TesselationLevel = 4;
|
||
|
|
|
||
|
|
Stopwatch _generationStopwatch = new Stopwatch();
|
||
|
|
|
||
|
|
private FastNoiseLite _continentalNoise;
|
||
|
|
private FastNoiseLite _mountainNoise;
|
||
|
|
private FastNoiseLite _hfNoise;
|
||
|
|
|
||
|
|
public enum GenerationStage
|
||
|
|
{
|
||
|
|
NotStarted,
|
||
|
|
Initialization,
|
||
|
|
PlateGeneration,
|
||
|
|
BorderSearch,
|
||
|
|
EdgeDistanceCalculation,
|
||
|
|
EdgeStressCalculation,
|
||
|
|
SpreadStress,
|
||
|
|
HeightCalculation,
|
||
|
|
|
||
|
|
Completed,
|
||
|
|
}
|
||
|
|
|
||
|
|
private bool _waiting = false;
|
||
|
|
public GenerationStage Stage = GenerationStage.NotStarted;
|
||
|
|
public GenerationStage StopStage = GenerationStage.Completed;
|
||
|
|
|
||
|
|
private MeshInstance3D _meshInstance;
|
||
|
|
private TextureRect _textureRect;
|
||
|
|
private ArrayMesh _arrayMesh;
|
||
|
|
|
||
|
|
public MeshDataTool Mdt;
|
||
|
|
|
||
|
|
public Oct Octree = new Oct();
|
||
|
|
|
||
|
|
public PlanetHelper(MeshInstance3D meshInstance, TextureRect textureRect)
|
||
|
|
{
|
||
|
|
_meshInstance = meshInstance;
|
||
|
|
_arrayMesh = meshInstance.Mesh as ArrayMesh;
|
||
|
|
_textureRect = textureRect;
|
||
|
|
|
||
|
|
_continentalNoise = new FastNoiseLite();
|
||
|
|
_mountainNoise = new FastNoiseLite();
|
||
|
|
_hfNoise = new FastNoiseLite();
|
||
|
|
|
||
|
|
Mdt = new MeshDataTool();
|
||
|
|
Mdt.CreateFromSurface(_arrayMesh, 0);
|
||
|
|
|
||
|
|
for (int i = 0; i < Mdt.GetVertexCount(); i++)
|
||
|
|
{
|
||
|
|
Octree.Insert(new Node(i, Mdt.GetVertex(i) * 0.001f));
|
||
|
|
Mdt.SetVertexColor(i, Colors.Black);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void InitializeGeneration()
|
||
|
|
{
|
||
|
|
Plates = new();
|
||
|
|
Vertices = new();
|
||
|
|
|
||
|
|
Mdt.CreateFromSurface(_arrayMesh, 0);
|
||
|
|
|
||
|
|
for (int i = 0; i < Mdt.GetVertexCount(); i++)
|
||
|
|
{
|
||
|
|
// Init to black
|
||
|
|
Mdt.SetVertexColor(i, Colors.Black);
|
||
|
|
|
||
|
|
Vertices.Add(new VertexData(i, -1,GetNeighboringVertices(i, false).OrderBy(v => Guid.NewGuid()).ToList()));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Initialize Plates
|
||
|
|
for (int i = 0; i < _plateCount; i++)
|
||
|
|
{
|
||
|
|
// Get a random un-assigned vertex.
|
||
|
|
VertexData vertex = Vertices.Where(v => v.PlateId == -1).OrderBy(v => Guid.NewGuid()).First();
|
||
|
|
vertex.PlateId = i;
|
||
|
|
var color = new Color(RandF(0f, 1f), RandF(0f, 1f), RandF(0f, 1f));
|
||
|
|
ColorVertex(vertex.Id, color);
|
||
|
|
PlateData plate = new PlateData(i, color, false, [vertex.Id]);
|
||
|
|
|
||
|
|
plate.Dir = GetRandomTangentialVelocity(Mdt.GetVertex(vertex.Id), RandF(0f, 1f));
|
||
|
|
Plates.Add(plate);
|
||
|
|
}
|
||
|
|
|
||
|
|
CompleteStage();
|
||
|
|
}
|
||
|
|
|
||
|
|
public IEnumerable<int> GetNeighboringVertices(int vertexId, bool blackOnly = true)
|
||
|
|
{
|
||
|
|
if (Stage != GenerationStage.Initialization)
|
||
|
|
{
|
||
|
|
if (blackOnly)
|
||
|
|
return Vertices[vertexId].Neighbours.Where(n => Vertices[n].PlateId == -1);
|
||
|
|
return Vertices[vertexId].Neighbours;
|
||
|
|
}
|
||
|
|
var verts = Mdt.GetVertexEdges(vertexId).AsEnumerable().SelectMany<int, int>(edge => [Mdt.GetEdgeVertex(edge, 0), Mdt.GetEdgeVertex(edge, 1)]).Distinct().Where(v => v != vertexId);
|
||
|
|
if (!blackOnly)
|
||
|
|
return verts.Except([vertexId]);
|
||
|
|
return verts.Where(v => Mdt.GetVertexColor(v) == Colors.Black).Except([vertexId]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public Vector3 GetRandomTangentialVelocity(Vector3 pointOnSphere, float speed)
|
||
|
|
{
|
||
|
|
Vector3 normal = pointOnSphere.Normalized();
|
||
|
|
|
||
|
|
Random rand = new Random();
|
||
|
|
Vector3 randomVec = new Vector3(
|
||
|
|
(float)(rand.NextDouble() - 0.5), // Range -0.5 to 0.5
|
||
|
|
(float)(rand.NextDouble() - 0.5),
|
||
|
|
(float)(rand.NextDouble() - 0.5)
|
||
|
|
);
|
||
|
|
|
||
|
|
Vector3 tangent = randomVec.Cross(normal);
|
||
|
|
|
||
|
|
if (tangent.Dot(tangent) < 1e-6f)
|
||
|
|
{
|
||
|
|
randomVec = new Vector3(0, 1, 0);
|
||
|
|
tangent = randomVec.Cross(normal);
|
||
|
|
}
|
||
|
|
|
||
|
|
Vector3 normalizedTangent = tangent.Normalized();
|
||
|
|
|
||
|
|
return normalizedTangent * speed;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void ToggleAutoRun()
|
||
|
|
{
|
||
|
|
AutoRun = !AutoRun;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void ToggleAdvance()
|
||
|
|
{
|
||
|
|
Advance = !Advance;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void AdvanceStage()
|
||
|
|
{
|
||
|
|
Advance = false;
|
||
|
|
if (_waiting)
|
||
|
|
return;
|
||
|
|
|
||
|
|
|
||
|
|
Timer timer = new(Mathf.Clamp(StageHangTime, 0.1, 10.0));
|
||
|
|
timer.Elapsed += (o, e) =>
|
||
|
|
{
|
||
|
|
GenerationStage stage = Stage + 1;
|
||
|
|
Stage = Stage == StopStage ? GenerationStage.Completed : stage;
|
||
|
|
if (stage == GenerationStage.Completed)
|
||
|
|
_generationStopwatch.Stop();
|
||
|
|
else
|
||
|
|
_generationStopwatch.Restart();
|
||
|
|
GD.Print($"Stage Started: '{Stage.ToString()}'");
|
||
|
|
_waiting = false;
|
||
|
|
StageComplete = false;
|
||
|
|
};
|
||
|
|
timer.AutoReset = false;
|
||
|
|
timer.Start();
|
||
|
|
_waiting = true;
|
||
|
|
}
|
||
|
|
public void Process()
|
||
|
|
{
|
||
|
|
if (!StageComplete)
|
||
|
|
{
|
||
|
|
switch (Stage)
|
||
|
|
{
|
||
|
|
default:
|
||
|
|
case GenerationStage.NotStarted:
|
||
|
|
break;
|
||
|
|
case GenerationStage.Completed:
|
||
|
|
break;
|
||
|
|
case GenerationStage.Initialization:
|
||
|
|
InitializeGeneration();
|
||
|
|
break;
|
||
|
|
case GenerationStage.PlateGeneration:
|
||
|
|
PlateGeneration();
|
||
|
|
break;
|
||
|
|
case GenerationStage.BorderSearch:
|
||
|
|
BorderSearch();
|
||
|
|
break;
|
||
|
|
case GenerationStage.EdgeDistanceCalculation:
|
||
|
|
EdgeDistanceCalculation();
|
||
|
|
break;
|
||
|
|
case GenerationStage.EdgeStressCalculation:
|
||
|
|
EdgeStressCalculation();
|
||
|
|
break;
|
||
|
|
case GenerationStage.SpreadStress:
|
||
|
|
SpreadStress();
|
||
|
|
break;
|
||
|
|
case GenerationStage.HeightCalculation:
|
||
|
|
HeightCalculation();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
UpdateMesh();
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
if (AutoRun || Advance)
|
||
|
|
AdvanceStage();
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
public void PlateGeneration()
|
||
|
|
{
|
||
|
|
var availableVerts = Vertices.Where(d => d.StageComplete == false && d.PlateId != -1).OrderBy(v => Guid.NewGuid()).ToList();
|
||
|
|
foreach (PlateData plateData in Plates)
|
||
|
|
{
|
||
|
|
var plateVerts = availableVerts.Where(d => d.PlateId == plateData.Id);
|
||
|
|
foreach (VertexData vertexData in plateVerts.Take((int)((5 + plateVerts.Count() / 4) * plateData.PlateExpansion)))
|
||
|
|
{
|
||
|
|
int expandTo = GetFreeNeighbourIndex(vertexData);
|
||
|
|
if (expandTo != -1)
|
||
|
|
{
|
||
|
|
Vertices[expandTo].PlateId = plateData.Id;
|
||
|
|
plateData.Vertices.Add(expandTo);
|
||
|
|
ColorVertex(expandTo, plateData.Color);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
vertexData.StageComplete = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!availableVerts.Any())
|
||
|
|
{
|
||
|
|
foreach (VertexData vertexData in Vertices)
|
||
|
|
vertexData.StageComplete = false;
|
||
|
|
AssignOceanPlates(Plates);
|
||
|
|
CompleteStage();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
public void BorderSearch()
|
||
|
|
{
|
||
|
|
var availableVerts = Vertices.Where(d => d.StageComplete == false).Take(2500).ToList();
|
||
|
|
foreach (VertexData vertexData in availableVerts)
|
||
|
|
{
|
||
|
|
// Do we have any neighbours of another plate?
|
||
|
|
var neighbours = GetNeighboringVertices(vertexData.Id, false).ToList();
|
||
|
|
if (neighbours
|
||
|
|
.Any(v => Vertices[v].PlateId != vertexData.PlateId))
|
||
|
|
{
|
||
|
|
vertexData.IsEdge = true;
|
||
|
|
vertexData.IsTypeEdge = neighbours.Any(n => Plates[Vertices[n].PlateId].IsLandform != Plates[vertexData.PlateId].IsLandform);
|
||
|
|
if (vertexData.IsTypeEdge)
|
||
|
|
vertexData.EdgeDistance = 1f;
|
||
|
|
ColorVertex(vertexData.Id, vertexData.IsTypeEdge ? Colors.White : Colors.Black);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
ColorVertex(vertexData.Id, Plates[vertexData.PlateId].Color);
|
||
|
|
}
|
||
|
|
vertexData.StageComplete = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!availableVerts.Any())
|
||
|
|
{
|
||
|
|
foreach (VertexData vertexData in Vertices)
|
||
|
|
vertexData.StageComplete = false;
|
||
|
|
CompleteStage();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
public void EdgeDistanceCalculation()
|
||
|
|
{
|
||
|
|
var availableVerts = Vertices.Where(d => d.StageComplete == false && d.EdgeDistance > 0f).OrderBy(v => v.EdgeDistance).Take(2500).ToList();
|
||
|
|
foreach (VertexData vertexData in availableVerts)
|
||
|
|
{
|
||
|
|
var neighbours = GetNeighboringVertices(vertexData.Id, false).ToList();
|
||
|
|
foreach (int neighbour in neighbours)
|
||
|
|
{
|
||
|
|
if (Vertices[neighbour].EdgeDistance > 0f && Vertices[neighbour].EdgeDistance < vertexData.EdgeDistance + 1f)
|
||
|
|
continue;
|
||
|
|
VertexData neighbourVert = Vertices[neighbour];
|
||
|
|
neighbourVert.EdgeDistance = vertexData.EdgeDistance + 1f;
|
||
|
|
ColorVertex(neighbourVert.Id, Plates[vertexData.PlateId].Color * 0.8f);
|
||
|
|
}
|
||
|
|
vertexData.StageComplete = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!availableVerts.Any())
|
||
|
|
{
|
||
|
|
float maxDistance = Vertices.Max(v => v.EdgeDistance);
|
||
|
|
foreach (VertexData vertexData in Vertices)
|
||
|
|
{
|
||
|
|
vertexData.EdgeDistance /= maxDistance;
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (PlateData plateData in Plates)
|
||
|
|
{
|
||
|
|
plateData.CenterVertexId =
|
||
|
|
Vertices.Where(v => v.PlateId == plateData.Id).MaxBy(v => v.EdgeDistance).Id;
|
||
|
|
}
|
||
|
|
foreach (VertexData vertexData in Vertices)
|
||
|
|
vertexData.StageComplete = false;
|
||
|
|
CompleteStage();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
public void EdgeStressCalculation()
|
||
|
|
{
|
||
|
|
var availableVerts = Vertices.Where(d => d.StageComplete == false && d.IsEdge).Take(2500).ToList();
|
||
|
|
foreach (VertexData vertexData in availableVerts)
|
||
|
|
{
|
||
|
|
var neighbours = GetNeighboringVertices(vertexData.Id, false).ToList();
|
||
|
|
foreach (int neighbour in neighbours)
|
||
|
|
{
|
||
|
|
if (!Vertices[neighbour].IsEdge)
|
||
|
|
continue;
|
||
|
|
if (Vertices[neighbour].PlateId == vertexData.PlateId)
|
||
|
|
continue;
|
||
|
|
PlateData plateA = Plates[vertexData.PlateId];
|
||
|
|
PlateData plateB = Plates[Vertices[neighbour].PlateId];
|
||
|
|
VertexData centerA = Vertices[plateA.CenterVertexId];
|
||
|
|
VertexData centerB = Vertices[plateB.CenterVertexId];
|
||
|
|
Vector3 p1, p2;
|
||
|
|
p1 = Mdt.GetVertex(vertexData.Id).Cross(Mdt.GetVertex(centerA.Id));
|
||
|
|
p2 = Mdt.GetVertex(neighbour).Cross(Mdt.GetVertex(centerB.Id));
|
||
|
|
vertexData.StrainSamples.Add(CalculateStrainMagnitude(p1, p2, plateA.Dir, plateB.Dir));
|
||
|
|
}
|
||
|
|
vertexData.StageComplete = true;
|
||
|
|
var majorStrain = AverageStrainList(vertexData.StrainSamples);
|
||
|
|
switch (majorStrain.Type)
|
||
|
|
{
|
||
|
|
case StrainType.Compression:
|
||
|
|
ColorVertex(vertexData.Id, Colors.Red * majorStrain.Magnitude);
|
||
|
|
break;
|
||
|
|
case StrainType.Shear:
|
||
|
|
ColorVertex(vertexData.Id, Colors.Yellow * majorStrain.Magnitude);
|
||
|
|
break;
|
||
|
|
case StrainType.Tension:
|
||
|
|
ColorVertex(vertexData.Id, Colors.Blue * majorStrain.Magnitude);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!availableVerts.Any())
|
||
|
|
{
|
||
|
|
foreach (VertexData vertexData in Vertices)
|
||
|
|
vertexData.StageComplete = false;
|
||
|
|
CompleteStage();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
public void SpreadStress()
|
||
|
|
{
|
||
|
|
var availableVerts = Vertices.Where(d => d.StageComplete == false && d.IsEdge && d.StrainSamples.Any()).OrderBy(d => Mathf.Abs(d.StrainSamples.Max(s => s.Magnitude))).Take(2500).ToList();
|
||
|
|
foreach (VertexData vertexData in availableVerts)
|
||
|
|
{
|
||
|
|
var neighbours = GetNeighboringVertices(vertexData.Id, false).ToList();
|
||
|
|
var majorStrain = AverageStrainList(vertexData.StrainSamples);
|
||
|
|
foreach (int neighbour in neighbours)
|
||
|
|
{
|
||
|
|
VertexData neighbourVert = Vertices[neighbour];
|
||
|
|
neighbourVert.IsEdge = true;
|
||
|
|
|
||
|
|
var newStrain = new StrainAnalysis();
|
||
|
|
newStrain.Magnitude = majorStrain.Magnitude * 0.9f;
|
||
|
|
newStrain.Type = majorStrain.Type;
|
||
|
|
newStrain.NormalRate = majorStrain.NormalRate * 0.9f;
|
||
|
|
newStrain.ShearRate = majorStrain.ShearRate * 0.9f;
|
||
|
|
neighbourVert.StrainSamples.Add(newStrain);
|
||
|
|
var newAverage = AverageStrainList(neighbourVert.StrainSamples);;
|
||
|
|
switch (majorStrain.Type)
|
||
|
|
{
|
||
|
|
case StrainType.Compression:
|
||
|
|
ColorVertex(neighbourVert.Id, Colors.Red * newAverage.Magnitude);
|
||
|
|
break;
|
||
|
|
case StrainType.Shear:
|
||
|
|
ColorVertex(neighbourVert.Id, Colors.Yellow * newAverage.Magnitude);
|
||
|
|
break;
|
||
|
|
case StrainType.Tension:
|
||
|
|
ColorVertex(neighbourVert.Id, Colors.Blue * newAverage.Magnitude);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (neighbours.All(n => Vertices[n].IsEdge))
|
||
|
|
{
|
||
|
|
vertexData.StageComplete = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!availableVerts.Any())
|
||
|
|
{
|
||
|
|
foreach (VertexData vertexData in Vertices)
|
||
|
|
{
|
||
|
|
vertexData.StageComplete = false;
|
||
|
|
}
|
||
|
|
CompleteStage();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void HeightCalculation()
|
||
|
|
{
|
||
|
|
var availableVerts = Vertices.Where(d => d.StageComplete == false).Take(2500).ToList();
|
||
|
|
foreach (VertexData vertexData in availableVerts)
|
||
|
|
{
|
||
|
|
PlateData plate = Plates[vertexData.PlateId];
|
||
|
|
float continentalNoise = _continentalNoise.GetNoise3Dv(GetVertexPosition(vertexData.Id));
|
||
|
|
float mountainNoise = (1.0f + _mountainNoise.GetNoise3Dv(GetVertexPosition(vertexData.Id))) * 0.5f;
|
||
|
|
float hfNoise = _hfNoise.GetNoise3Dv(GetVertexPosition(vertexData.Id));
|
||
|
|
var majorStrain = AverageStrainList(vertexData.StrainSamples);
|
||
|
|
var normalRate = -majorStrain.NormalRate * majorStrain.Magnitude * (plate.IsLandform ? 1f : 0.5f);
|
||
|
|
var edgeDistance = vertexData.EdgeDistance * (plate.IsLandform ? 1f : -1f);
|
||
|
|
float height = 0.5f;
|
||
|
|
//height *= plate.PlateExpansion;
|
||
|
|
float mult = 2f;
|
||
|
|
height += hfNoise;
|
||
|
|
height = (height + 0.5f * mult) / (1f + mult);
|
||
|
|
height += continentalNoise;
|
||
|
|
height = (height + 0.5f * mult) / (1f + mult);
|
||
|
|
height += edgeDistance * 0.25f;
|
||
|
|
height = (height + 0.5f * mult) / (1f + mult);
|
||
|
|
height += normalRate * 0.35f;
|
||
|
|
height = Mathf.Clamp(height, 0.01f, 0.99f);
|
||
|
|
ColorVertex(vertexData.Id, Colors.White * height);
|
||
|
|
vertexData.StageComplete = true;
|
||
|
|
vertexData.Height = height;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!availableVerts.Any())
|
||
|
|
{
|
||
|
|
GD.Print($"Heights - min:'{Vertices.Min(v => v.Height)}' - max:'{Vertices.Max(v => v.Height)}' - average:'{Vertices.Average(v => v.Height)}'");
|
||
|
|
ScaleValues(Vertices);
|
||
|
|
foreach (VertexData vertexData in Vertices)
|
||
|
|
ColorVertex(vertexData.Id, Colors.White * vertexData.Height);
|
||
|
|
float oceanPercentage = Vertices.Count(v => v.Height < 0.5f) / (float)Vertices.Count;
|
||
|
|
GD.Print($"Ocean Percentage:'{oceanPercentage}'");
|
||
|
|
CompleteStage();
|
||
|
|
if (_meshInstance.GetSurfaceOverrideMaterial(0) is ShaderMaterial shaderMaterial)
|
||
|
|
{
|
||
|
|
shaderMaterial.SetShaderParameter("mode", 2);
|
||
|
|
}
|
||
|
|
if (_textureRect.Material is ShaderMaterial textureShaderMaterial)
|
||
|
|
{
|
||
|
|
textureShaderMaterial.SetShaderParameter("mode", 2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
public void ScaleValues(List<VertexData> values)
|
||
|
|
{
|
||
|
|
float maxDistance = Vertices.Max(s => Mathf.Abs(s.Height - 0.5f));
|
||
|
|
float scale = 0.5f/maxDistance;
|
||
|
|
|
||
|
|
values.ForEach(v => v.Height = Mathf.Clamp(0.5f + (v.Height - 0.5f) * scale, 0.01f, 0.99f));
|
||
|
|
GD.Print($"Heights Post Scaling - min:'{Vertices.Min(v => v.Height)}' - max:'{Vertices.Max(v => v.Height)}' - average:'{Vertices.Average(v => v.Height)}'");
|
||
|
|
}
|
||
|
|
public StrainAnalysis CalculateStrainMagnitude(Vector3 p1, Vector3 p2, Vector3 v1, Vector3 v2)
|
||
|
|
{
|
||
|
|
StrainAnalysis result = new StrainAnalysis();
|
||
|
|
|
||
|
|
Vector3 edge = p2 - p1;
|
||
|
|
float edgeLength = edge.Length();
|
||
|
|
|
||
|
|
if (edgeLength < float.Epsilon)
|
||
|
|
{
|
||
|
|
result.Magnitude = 0;
|
||
|
|
result.Type = StrainType.Shear; // Default
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
Vector3 relVelocity = v2 - v1;
|
||
|
|
float relVelMag = relVelocity.Length();
|
||
|
|
|
||
|
|
float dot = relVelocity.Dot(edge);
|
||
|
|
result.NormalRate = dot / edgeLength;
|
||
|
|
|
||
|
|
float normalRateSq = result.NormalRate * result.NormalRate;
|
||
|
|
float shearRateSq = relVelMag * relVelMag - normalRateSq;
|
||
|
|
result.ShearRate = (shearRateSq > 0) ? (float)Math.Sqrt(shearRateSq) : 0;
|
||
|
|
|
||
|
|
result.Magnitude = (float)Math.Sqrt(normalRateSq + shearRateSq);
|
||
|
|
|
||
|
|
float absNormal = Math.Abs(result.NormalRate);
|
||
|
|
float absShear = Math.Abs(result.ShearRate);
|
||
|
|
|
||
|
|
if (absNormal > absShear)
|
||
|
|
{
|
||
|
|
result.Type = result.NormalRate > 0
|
||
|
|
? StrainType.Tension
|
||
|
|
: StrainType.Compression;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
result.Type = StrainType.Shear;
|
||
|
|
}
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static StrainAnalysis AverageStrainList(List<StrainAnalysis> strains)
|
||
|
|
{
|
||
|
|
if (strains == null || strains.Count == 0)
|
||
|
|
{
|
||
|
|
return new StrainAnalysis();
|
||
|
|
}
|
||
|
|
|
||
|
|
int count = strains.Count;
|
||
|
|
|
||
|
|
float sumMagnitude = 0;
|
||
|
|
float sumNormalRate = 0;
|
||
|
|
float sumShearRate = 0;
|
||
|
|
|
||
|
|
int tensionCount = 0;
|
||
|
|
int compressionCount = 0;
|
||
|
|
int shearCount = 0;
|
||
|
|
|
||
|
|
foreach (var s in strains)
|
||
|
|
{
|
||
|
|
sumMagnitude += s.Magnitude;
|
||
|
|
sumNormalRate += s.NormalRate;
|
||
|
|
sumShearRate += s.ShearRate;
|
||
|
|
|
||
|
|
switch (s.Type)
|
||
|
|
{
|
||
|
|
case StrainType.Tension:
|
||
|
|
tensionCount++;
|
||
|
|
break;
|
||
|
|
case StrainType.Compression:
|
||
|
|
compressionCount++;
|
||
|
|
break;
|
||
|
|
case StrainType.Shear:
|
||
|
|
shearCount++;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
float avgMagnitude = sumMagnitude / count;
|
||
|
|
float avgNormalRate = sumNormalRate / count;
|
||
|
|
float avgShearRate = sumShearRate / count;
|
||
|
|
|
||
|
|
StrainType averageType = StrainType.Shear;
|
||
|
|
int maxCount = 0;
|
||
|
|
|
||
|
|
if (tensionCount > maxCount) { maxCount = tensionCount; averageType = StrainType.Tension; }
|
||
|
|
if (compressionCount > maxCount) { maxCount = compressionCount; averageType = StrainType.Compression; }
|
||
|
|
if (shearCount > maxCount) { maxCount = shearCount; averageType = StrainType.Shear; }
|
||
|
|
|
||
|
|
return new StrainAnalysis
|
||
|
|
{
|
||
|
|
Magnitude = avgMagnitude,
|
||
|
|
Type = averageType,
|
||
|
|
NormalRate = avgNormalRate,
|
||
|
|
ShearRate = avgShearRate
|
||
|
|
};
|
||
|
|
}
|
||
|
|
public void AssignOceanPlates(List<PlateData> areas)
|
||
|
|
{
|
||
|
|
int n = areas.Count;
|
||
|
|
double totalArea = areas.Sum(a => a.Vertices.Count * a.PlateExpansion);
|
||
|
|
double targetOcean = totalArea * _landRatio;
|
||
|
|
|
||
|
|
double bestDiff = double.MaxValue;
|
||
|
|
int bestMask = 0;
|
||
|
|
|
||
|
|
int combinations = 1 << n;
|
||
|
|
|
||
|
|
for (int mask = 0; mask < combinations; mask++)
|
||
|
|
{
|
||
|
|
int oceanArea = 0;
|
||
|
|
|
||
|
|
for (int i = 0; i < n; i++)
|
||
|
|
{
|
||
|
|
if ((mask & (1 << i)) != 0)
|
||
|
|
oceanArea += (int)(areas[i].Vertices.Count * areas[i].PlateExpansion);
|
||
|
|
}
|
||
|
|
|
||
|
|
double diff = Math.Abs(oceanArea - targetOcean);
|
||
|
|
|
||
|
|
if (diff < bestDiff)
|
||
|
|
{
|
||
|
|
bestDiff = diff;
|
||
|
|
bestMask = mask;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (int i = 0; i < n; i++)
|
||
|
|
{
|
||
|
|
areas[i].IsLandform = (bestMask & (1 << i)) != 0;
|
||
|
|
Color color = GetInitialColor(areas[i].IsLandform);
|
||
|
|
areas[i].Color = color;
|
||
|
|
foreach (int v in areas[i].Vertices)
|
||
|
|
{
|
||
|
|
ColorVertex(v, color);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
public int GetFreeNeighbourIndex(VertexData vertexData)
|
||
|
|
{
|
||
|
|
foreach (int neighbour in vertexData.Neighbours)
|
||
|
|
{
|
||
|
|
if (Vertices[neighbour].PlateId == -1)
|
||
|
|
return neighbour;
|
||
|
|
}
|
||
|
|
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
public Color GetInitialColor(bool isLand)
|
||
|
|
{
|
||
|
|
var color = isLand ? new Color(
|
||
|
|
0.2f,
|
||
|
|
1f,
|
||
|
|
0.2f
|
||
|
|
) : new Color(
|
||
|
|
0.2f,
|
||
|
|
0.2f,
|
||
|
|
1f
|
||
|
|
);
|
||
|
|
color.ToHsv(out float h, out float s, out float v);
|
||
|
|
h += RandF(-0.05f, 0.05f);
|
||
|
|
s += RandF(-0.2f, 0.2f);
|
||
|
|
v += RandF(-0.3f, 0.3f);
|
||
|
|
color = Color.FromHsv(h, s, v);
|
||
|
|
return color;
|
||
|
|
}
|
||
|
|
public Vector3 GetVertexPosition(int vertexId)
|
||
|
|
{
|
||
|
|
return Mdt.GetVertex(vertexId);
|
||
|
|
}
|
||
|
|
public void CompleteStage()
|
||
|
|
{
|
||
|
|
StageComplete = true;
|
||
|
|
_generationStopwatch.Stop();
|
||
|
|
if (Stage != GenerationStage.NotStarted)
|
||
|
|
GD.Print($"'{Stage.ToString()}' took '{_generationStopwatch.Elapsed}'");
|
||
|
|
}
|
||
|
|
|
||
|
|
public void ColorVertex(int id, Color color)
|
||
|
|
{
|
||
|
|
Mdt.SetVertexColor(id, color);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void UpdateMesh()
|
||
|
|
{
|
||
|
|
_arrayMesh.ClearSurfaces();
|
||
|
|
Mdt.CommitToSurface(_arrayMesh);
|
||
|
|
}
|
||
|
|
}
|