diff --git a/adatonic.sln.DotSettings.user b/adatonic.sln.DotSettings.user index ceb9423..c87cd83 100644 --- a/adatonic.sln.DotSettings.user +++ b/adatonic.sln.DotSettings.user @@ -1,4 +1,5 @@  + ForceIncluded ForceIncluded ForceIncluded ForceIncluded diff --git a/project.godot b/project.godot index 23d21db..414f41a 100644 --- a/project.godot +++ b/project.godot @@ -50,5 +50,3 @@ mouse_secondary={ [rendering] rendering_device/driver.windows="d3d12" -renderer/rendering_method="gl_compatibility" -renderer/rendering_method.mobile="gl_compatibility" diff --git a/shaders/compute/PlateExpansion.glsl b/shaders/compute/PlateExpansion.glsl new file mode 100644 index 0000000..d9d7857 --- /dev/null +++ b/shaders/compute/PlateExpansion.glsl @@ -0,0 +1,40 @@ +#[compute] +#version 450 +// Invocations in the (x, y, z) dimension +layout(local_size_x = 1000, local_size_y = 1, local_size_z = 1) in; + +// A binding to the buffer we create in our script +layout(set = 0, binding = 0, std430) restrict buffer PointBuffer { + int points[]; +} +pointbuffer; +layout(set = 0, binding = 1, std430) restrict buffer NeighborBuffer { + int neighbors[]; +} +neighborbuffer; +layout(set = 0, binding = 2, std430) restrict buffer PlateBuffer { + int plateid[]; +} +platebuffer; + +// The code we want to execute in each invocation +void main() { + // Iteration is gl_GlobalInvocationID.x + // Neighbors for Point gl_GlobalInvocationID.y + for (int point = 0; point < int(gl_WorkGroupSize.x); point++) + { + int localpoint = int(gl_WorkGroupID.x)*int(gl_WorkGroupSize.x) + point; + for (int neighbor = 0; neighbor < 6; neighbor++) { + if (neighborbuffer.neighbors[localpoint*6+neighbor] == -1) + { + continue; + } + if (platebuffer.plateid[localpoint] != -1 && platebuffer.plateid[neighborbuffer.neighbors[localpoint*6+neighbor]] == -1) + { + // Neighbor unclaimed, and we can claim it. + platebuffer.plateid[neighborbuffer.neighbors[localpoint*6+neighbor]] = platebuffer.plateid[localpoint]; + } + } + } + +} diff --git a/shaders/compute/PlateExpansion.glsl.import b/shaders/compute/PlateExpansion.glsl.import new file mode 100644 index 0000000..8be2164 --- /dev/null +++ b/shaders/compute/PlateExpansion.glsl.import @@ -0,0 +1,14 @@ +[remap] + +importer="glsl" +type="RDShaderFile" +uid="uid://cypd2lshixd17" +path="res://.godot/imported/PlateExpansion.glsl-9047ef54068b6d1a50f93c2cd303b382.res" + +[deps] + +source_file="res://shaders/compute/PlateExpansion.glsl" +dest_files=["res://.godot/imported/PlateExpansion.glsl-9047ef54068b6d1a50f93c2cd303b382.res"] + +[params] + diff --git a/shaders/compute/compute_example.glsl b/shaders/compute/compute_example.glsl new file mode 100644 index 0000000..c3e3ce3 --- /dev/null +++ b/shaders/compute/compute_example.glsl @@ -0,0 +1,17 @@ +#[compute] +#version 450 + +// Invocations in the (x, y, z) dimension +layout(local_size_x = 2, local_size_y = 1, local_size_z = 1) in; + +// A binding to the buffer we create in our script +layout(set = 0, binding = 0, std430) restrict buffer MyDataBuffer { + float data[]; +} +my_data_buffer; + +// The code we want to execute in each invocation +void main() { + // gl_GlobalInvocationID.x uniquely identifies this invocation across all work groups + my_data_buffer.data[gl_GlobalInvocationID.x] *= 2.0; +} diff --git a/shaders/compute/compute_example.glsl.import b/shaders/compute/compute_example.glsl.import new file mode 100644 index 0000000..4dbf9d7 --- /dev/null +++ b/shaders/compute/compute_example.glsl.import @@ -0,0 +1,14 @@ +[remap] + +importer="glsl" +type="RDShaderFile" +uid="uid://du2kf2vmf800w" +path="res://.godot/imported/compute_example.glsl-3f04b63d0b43774004ed10776fd05117.res" + +[deps] + +source_file="res://shaders/compute/compute_example.glsl" +dest_files=["res://.godot/imported/compute_example.glsl-3f04b63d0b43774004ed10776fd05117.res"] + +[params] + diff --git a/src/Main.cs b/src/Main.cs index 7d79a6d..9932642 100644 --- a/src/Main.cs +++ b/src/Main.cs @@ -1,10 +1,13 @@ #nullable enable using Godot; using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Threading.Tasks; using Godot.Collections; +using Array = System.Array; public partial class Main : Control { @@ -39,6 +42,43 @@ public partial class Main : Control Projector.GatherPoints(_planetHelper, int.Parse(GetNode("%Resolution").Text)); AxialTiltChanged(GetNode("%AxialTilt").Text); UpdateTime(); + + _planetHelper.InitializeGeneration(); + _planetHelper.Plates[0].Color = _planetHelper.GetInitialColor(true); + //_planetHelper.Plates[1].Color = _planetHelper.GetInitialColor(true); + //_planetHelper.Plates[2].Color = _planetHelper.GetInitialColor(false); + //_planetHelper.Plates[3].Color = _planetHelper.GetInitialColor(false); + + // for (int i = 0; i < 1; i++) + // { + // for (int point = 0; point < points.Length; point++) + // { + // for (int neighbor = 0; neighbor < 6; neighbor++) + // { + // if (neighbors[point * 6 + neighbor] == -1) + // { + // continue; + // } + // + // int x = plateids[point]; + // int y = neighbors[point * 6 + neighbor]; + // int z = plateids[y]; + // if (x != -1 && z == -1) + // { + // // Neighbor unclaimed, and we can claim it. + // plateids[neighbors[point * 6 +neighbor]] = plateids[point]; + // } + // } + // } + // } + // int index = 0; + // foreach (int plate in plateids) + // { + // if (plate != -1) + // _planetHelper.Mdt.SetVertexColor(index, _planetHelper.Plates[plate].Color); + // index++; + // } + // _planetHelper.UpdateMesh(); } Vector3 _pointerPosition = Vector3.Zero; diff --git a/src/PlanetHelper.cs b/src/PlanetHelper.cs index 29efbf0..e9cfc29 100644 --- a/src/PlanetHelper.cs +++ b/src/PlanetHelper.cs @@ -56,7 +56,7 @@ public class PlanetHelper } private bool StageComplete = true; - private int _plateCount = 14; + private int _plateCount = 12; private float _landRatio = 0.4f; public List Plates = new List(); @@ -156,7 +156,6 @@ public class PlanetHelper 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.MovementAxis = GetRandomVector3(); @@ -168,7 +167,7 @@ public class PlanetHelper public IEnumerable GetNeighboringVertices(int vertexId, bool blackOnly = true) { - if (Stage != GenerationStage.Initialization) + if (Stage != GenerationStage.Initialization && Stage != GenerationStage.NotStarted) { if (blackOnly) return Vertices[vertexId].Neighbours.Where(n => Vertices[n].PlateId == -1); @@ -296,33 +295,95 @@ public class PlanetHelper 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; - } - } - } + var rd = RenderingServer.CreateLocalRenderingDevice(); + var shaderFile = GD.Load("res://shaders/compute/PlateExpansion.glsl"); + GD.Print(shaderFile.BaseError); + var shaderBytecode = shaderFile.GetSpirV(); + GD.Print(shaderBytecode.CompileErrorCompute); + GD.Print(shaderBytecode.CompileErrorFragment); + GD.Print(shaderBytecode.CompileErrorVertex); + GD.Print(shaderBytecode.CompileErrorTesselationEvaluation); + GD.Print(shaderBytecode.CompileErrorTesselationControl); + var shader = rd.ShaderCreateFromSpirV(shaderBytecode); + GD.Print(shader.IsValid); + + // Prepare our data. We use floats in the shader, so we need 32 bit. + int[] points = Enumerable.Range(0, Mdt.GetVertexCount()).ToArray(); + var sizePoints = points.Length * sizeof(int); + + int[] neighbors = Vertices.SelectMany(v => v.Neighbours.Count < 6 ? [..v.Neighbours.ToArray(),-1] : v.Neighbours.ToArray()).ToArray(); + var sizeNeighbors = neighbors.Length * sizeof(int); + int[] plateids = Vertices.Select(v => v.PlateId).ToArray(); + + var sizePlateids = plateids.Length * sizeof(int); - if (!availableVerts.Any()) + var pointBytes = new byte[sizePoints]; + byte[] neighborBytes = new byte[sizeNeighbors]; + var plateBytes = new byte[sizePlateids]; + Buffer.BlockCopy(points, 0, pointBytes, 0, pointBytes.Length); + Buffer.BlockCopy(neighbors, 0, neighborBytes, 0, neighborBytes.Length); + Buffer.BlockCopy(plateids, 0, plateBytes, 0, plateBytes.Length); + + // Create a uniform to assign the buffer to the rendering device + var pointUniform = new RDUniform { - foreach (VertexData vertexData in Vertices) - vertexData.StageComplete = false; - AssignOceanPlates(Plates); - CompleteStage(); + UniformType = RenderingDevice.UniformType.StorageBuffer, + Binding = 0 + }; + var pointBuffer = rd.StorageBufferCreate((uint)pointBytes.Length, pointBytes); + pointUniform.AddId(pointBuffer); + + var neighborUniform = new RDUniform + { + UniformType = RenderingDevice.UniformType.StorageBuffer, + Binding = 1 + }; + var neighborBuffer = rd.StorageBufferCreate((uint)neighborBytes.Length, neighborBytes); + neighborUniform.AddId(neighborBuffer); + + var plateUniform = new RDUniform + { + UniformType = RenderingDevice.UniformType.StorageBuffer, + Binding = 2 + }; + var plateBuffer = rd.StorageBufferCreate((uint)plateBytes.Length, plateBytes); + plateUniform.AddId(plateBuffer); + + var uniformSet = rd.UniformSetCreate([pointUniform, neighborUniform, plateUniform], shader, 0); + + // Create a compute pipeline + var pipeline = rd.ComputePipelineCreate(shader); + var computeList = rd.ComputeListBegin(); + rd.ComputeListBindComputePipeline(computeList, pipeline); + rd.ComputeListBindUniformSet(computeList, uniformSet, 0); + uint xgroups = (uint)(Mathf.Ceil((double)points.Length / 1000.0)); + rd.ComputeListDispatch(computeList, xGroups: xgroups, yGroups: 50, zGroups: 1); + rd.ComputeListEnd(); + rd.Submit(); + rd.Sync(); + // Read back the data from the buffers + var outputBytes = rd.BufferGetData(plateBuffer); + var output = new int[plateids.Length]; + Buffer.BlockCopy(outputBytes, 0, output, 0, plateBytes.Length); + + int index = 0; + foreach (int plate in output) + { + if (plate != -1) + { + Mdt.SetVertexColor(index, Plates[plate].Color); + Vertices[index].PlateId = plate; + } + index++; } + rd.FreeRid(pointBuffer); + rd.FreeRid(plateBuffer); + rd.FreeRid(neighborBuffer); + rd.FreeRid(pipeline); + rd.FreeRid(uniformSet); + + AssignOceanPlates(Plates); + CompleteStage(); } public void BorderSearch() {