diff --git a/SurfaceNetsWorld/chunk.gd b/SurfaceNetsWorld/chunk.gd new file mode 100644 index 0000000..f183bb0 --- /dev/null +++ b/SurfaceNetsWorld/chunk.gd @@ -0,0 +1,63 @@ +extends Node3D +const CENTER := Vector3.ZERO +@export var threshold: float = 0.5 + +var generate_mesh_shader = preload("res://SurfaceNetsWorld/generate_mesh.tres") + +@export var regenerate_mesh = false + + +@export var chunk_size = 16 +@export var show_sample_points = false +@export var show_surface_points = false +@export var show_surface = true + +var mesh + +var color = Color.RED + +var meshinstance = MeshInstance3D.new() +var material = ShaderMaterial.new() + +var gpu_sdf + +func generate_chunk(pgpu_sdf) -> void: + gpu_sdf = pgpu_sdf + material.shader = generate_mesh_shader + meshinstance.material_override = material + add_child(meshinstance) + meshinstance.mesh = gpu_sdf.compute_mesh(chunk_size, threshold, self.position) + +func _input(event): + if event is InputEventKey and event.is_action_released("RegenerateMesh"): + if regenerate_mesh: + regenerate_mesh = false + else: + regenerate_mesh = true + +func _process(_delta: float) -> void: + if show_surface && regenerate_mesh: + regenerate_mesh = false + clear() + meshinstance.mesh = gpu_sdf.compute_mesh(chunk_size, threshold, self.position) + if show_surface_points: + var idx = 0 + var text_id = 0 + while idx < gpu_sdf.iout_surface_points.size()/ 2: + text_id += 1 + var value = Vector3(gpu_sdf.iout_surface_points.get(idx), gpu_sdf.iout_surface_points.get(idx+1), gpu_sdf.iout_surface_points.get(idx+2)) - Vector3(chunk_size / 2, chunk_size / 2, chunk_size / 2) + if gpu_sdf.iout_surface_points.get(idx) != -1.0: + DebugDraw3D.draw_square(value, 0.2, color.from_rgba8(255, 128, 128, 255)) + if text_id % 1 == 0: + DebugDraw3D.draw_text(value, str(value), 35) + idx += 3 + else: + idx += 1 + + +func clear(): + mesh = ArrayMesh.new() + + +func get_index_from_coords(coords: Vector3i): + return coords.x + coords.y * chunk_size + coords.z * chunk_size * chunk_size diff --git a/smooth_world.gd.uid b/SurfaceNetsWorld/chunk.gd.uid similarity index 100% rename from smooth_world.gd.uid rename to SurfaceNetsWorld/chunk.gd.uid diff --git a/smooth_world.tscn b/SurfaceNetsWorld/chunk.tscn similarity index 63% rename from smooth_world.tscn rename to SurfaceNetsWorld/chunk.tscn index b8c5d5f..c906466 100644 --- a/smooth_world.tscn +++ b/SurfaceNetsWorld/chunk.tscn @@ -1,16 +1,14 @@ [gd_scene format=3 uid="uid://llggsd0qmn4p"] -[ext_resource type="Script" uid="uid://bdfq22we54eul" path="res://smooth_world.gd" id="1_4h467"] -[ext_resource type="Shader" uid="uid://bose286qacwdl" path="res://generate_mesh.tres" id="2_an62b"] +[ext_resource type="Script" uid="uid://bdfq22we54eul" path="res://SurfaceNetsWorld/chunk.gd" id="1_oab2n"] +[ext_resource type="Shader" uid="uid://bose286qacwdl" path="res://SurfaceNetsWorld/generate_mesh.tres" id="2_uq73c"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_iutkt"] render_priority = 0 -shader = ExtResource("2_an62b") +shader = ExtResource("2_uq73c") -[node name="SmoothWorld" type="Node3D" unique_id=1592820568] -script = ExtResource("1_4h467") -RADIUS = 18.524 -world_size = 53 +[node name="Chunk" type="Node3D" unique_id=1592820568] +script = ExtResource("1_oab2n") [node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1739626442] material_override = SubResource("ShaderMaterial_iutkt") diff --git a/compute_samples.gd b/SurfaceNetsWorld/compute_samples.gd similarity index 63% rename from compute_samples.gd rename to SurfaceNetsWorld/compute_samples.gd index 0277e1f..63658fb 100644 --- a/compute_samples.gd +++ b/SurfaceNetsWorld/compute_samples.gd @@ -10,25 +10,72 @@ var start_time := Time.get_ticks_msec() / 1000.0 var color = Color.CORAL @export var iout_surface_points = [] -func create_device(): +var pipeline1 +var pipeline2 +var buffer +var surface_buffer +var normal_buffer +var uv_buffer +var idx_buffer +var counter_buffer +var chunk_position_buffer +var params_buffer + +func create_device(world_size: int): + rd = RenderingServer.create_local_rendering_device() - shader_file1 = load("res://sdf_shader.glsl") - shader_spirv1 = shader_file1.get_spirv() - shader_pass1 = rd.shader_create_from_spirv(shader_spirv1) - shader_file2 = load("res://sdf_mesh_generation.glsl") - shader_spirv2 = shader_file2.get_spirv() - shader_pass2 = rd.shader_create_from_spirv(shader_spirv2) -func compute_mesh(world_size: int, threshold: float) -> ArrayMesh: + # 1. Load Shaders + shader_file1 = load("res://SurfaceNetsWorld/compute_surface_points.glsl") + shader_pass1 = rd.shader_create_from_spirv(shader_file1.get_spirv()) + shader_file2 = load("res://SurfaceNetsWorld/sdf_mesh_generation.glsl") + shader_pass2 = rd.shader_create_from_spirv(shader_file2.get_spirv()) - # Prepare our data. We use floats in the shader, so we need 32 bit. - var total = world_size * world_size * world_size - var input := PackedFloat32Array() - input.resize(total) - var input_bytes := input.to_byte_array() + # 2. Create Pipelines + pipeline1 = rd.compute_pipeline_create(shader_pass1) + pipeline2 = rd.compute_pipeline_create(shader_pass2) - # Create a storage buffer that can hold our float values. - var buffer := rd.storage_buffer_create(input_bytes.size(), input_bytes) + # 3. Pre-allocate Buffers (assuming world_size is constant) + var total = world_size ** 3 + + # We create them once with empty/zero data of the correct size + buffer = rd.storage_buffer_create(total * 4) + surface_buffer = rd.storage_buffer_create(total * 3 * 4) + normal_buffer = rd.storage_buffer_create(total * 3 * 4) + uv_buffer = rd.storage_buffer_create(total * 2 * 4) + idx_buffer = rd.storage_buffer_create(total * 6 * 4) + counter_buffer = rd.storage_buffer_create(4) + chunk_position_buffer = rd.storage_buffer_create(16) # vec4 + params_buffer = rd.uniform_buffer_create(16) # world_size, threshold, time, etc + +func compute_mesh(world_size: int, threshold: float, chunk_pos: Vector3) -> ArrayMesh: + + # 1. Update existing buffers with NEW data for THIS chunk + var chunk_pos_data = PackedFloat32Array([chunk_pos.x, chunk_pos.y, chunk_pos.z, 0.0]).to_byte_array() + rd.buffer_update(chunk_position_buffer, 0, chunk_pos_data.size(), chunk_pos_data) + + # Reset the counter to 0 for the new chunk + var counter_reset = PackedInt32Array([0]).to_byte_array() + rd.buffer_update(counter_buffer, 0, 4, counter_reset) + + # Chunk position (offset) + var chunk_position_peer := PackedFloat32Array() + chunk_position_peer.resize(4) + chunk_position_peer.set(0, chunk_pos.x) + chunk_position_peer.set(1, chunk_pos.y) + chunk_position_peer.set(2, chunk_pos.z) + chunk_position_peer.set(3, 0.0) + var chunk_position_bytes := chunk_position_peer.to_byte_array() + rd.buffer_update(chunk_position_buffer, 0, chunk_position_bytes.size(), chunk_position_bytes) + + var u_time := Time.get_ticks_msec() / 1000.0 - start_time + var peer := StreamPeerBuffer.new() + peer.put_32(world_size) + peer.put_float(threshold) + peer.put_float(u_time) + peer.put_32(0) + var uniform_params_bytes := peer.data_array + rd.buffer_update(params_buffer, 0, uniform_params_bytes.size(), uniform_params_bytes) # Create a uniform to assign the buffer to the rendering device var uniform_buf := RDUniform.new() @@ -36,81 +83,45 @@ func compute_mesh(world_size: int, threshold: float) -> ArrayMesh: uniform_buf.binding = 0 # this needs to match the "binding" in our shader file uniform_buf.add_id(buffer) - # Prepare our data. We use floats in the shader, so we need 32 bit. - var surface_total = world_size * world_size * world_size * 3 - var surface_input := PackedFloat32Array() - surface_input.resize(surface_total) - var surface_input_bytes := surface_input.to_byte_array() - - # Create a storage buffer that can hold our float values. - var surface_buffer := rd.storage_buffer_create(surface_input_bytes.size(), surface_input_bytes) - # Create a uniform to assign the buffer to the rendering device var surface_uniform_buf := RDUniform.new() surface_uniform_buf.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER surface_uniform_buf.binding = 2 surface_uniform_buf.add_id(surface_buffer) - # Binding 3: Normals ( 3 floats per voxel) - var normal_total = total * 3 - var normal_input := PackedFloat32Array() - normal_input.resize(normal_total) - # Ensure we pass the byte size correctly: total_elements * 4 bytes per float - var normal_buffer = rd.storage_buffer_create(normal_input.size() * 4, normal_input.to_byte_array()) - var normal_uniform = RDUniform.new() normal_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER normal_uniform.binding = 3 normal_uniform.add_id(normal_buffer) - # Binding 4: UVs (vec2 = 2 floats per voxel) - var uv_bytes = PackedFloat32Array() - uv_bytes.resize(total * 2) - var uv_buffer = rd.storage_buffer_create(uv_bytes.size() * 4, uv_bytes.to_byte_array()) var uv_uniform = RDUniform.new() uv_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER uv_uniform.binding = 4 uv_uniform.add_id(uv_buffer) - # Index buffer - # 1. Use a safer max_indices for testing (18 is the absolute max, 6 is usually enough) - var safe_max = world_size * world_size * world_size * 6 - # 2. Pre-calculate byte array to avoid double-allocation spikes - var index_bytes := PackedInt32Array() - index_bytes.resize(safe_max) - var index_raw_bytes = index_bytes.to_byte_array() - # 3. Explicitly create the buffer - var idx_buffer = rd.storage_buffer_create(index_raw_bytes.size(), index_raw_bytes) var idx_uniform := RDUniform.new() idx_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER idx_uniform.binding = 5 idx_uniform.add_id(idx_buffer) - # Atomic counter - var counter_bytes = PackedInt32Array([0]).to_byte_array() - var counter_buffer = rd.storage_buffer_create(4, counter_bytes) var counter_uniform := RDUniform.new() counter_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER counter_uniform.binding = 6 counter_uniform.add_id(counter_buffer) - var u_time := Time.get_ticks_msec() / 1000.0 - start_time + var chunk_position_uniform := RDUniform.new() + chunk_position_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER + chunk_position_uniform.binding = 7 + chunk_position_uniform.add_id(chunk_position_buffer) + - var peer := StreamPeerBuffer.new() - peer.put_32(world_size) - peer.put_float(threshold) - peer.put_float(u_time) - peer.put_32(0) - var bytes := peer.data_array - - var params_buffer := rd.uniform_buffer_create(bytes.size(), bytes) var uniform_params := RDUniform.new() uniform_params.uniform_type = RenderingDevice.UNIFORM_TYPE_UNIFORM_BUFFER uniform_params.binding = 1 uniform_params.add_id(params_buffer) - var uniform_set1 := rd.uniform_set_create([uniform_buf, uniform_params, surface_uniform_buf, normal_uniform, uv_uniform, idx_uniform, counter_uniform], shader_pass1, 0) # the last parameter (the 0) needs to match the "set" in our shader file + var uniform_set1 := rd.uniform_set_create([uniform_buf, uniform_params, surface_uniform_buf, normal_uniform, uv_uniform, idx_uniform, counter_uniform, chunk_position_uniform], shader_pass1, 0) # the last parameter (the 0) needs to match the "set" in our shader file var uniform_set2 := rd.uniform_set_create([uniform_buf, uniform_params, surface_uniform_buf, normal_uniform, uv_uniform, idx_uniform, counter_uniform], shader_pass2, 0) var dispatch_count = int(ceil(world_size / 4.0)) @@ -123,11 +134,6 @@ func compute_mesh(world_size: int, threshold: float) -> ArrayMesh: rd.compute_list_dispatch(compute_list, dispatch_count, dispatch_count, dispatch_count) rd.compute_list_end() - # This is the "Magic" command that fixes the stripes/holes - # It forces the GPU to finish all writes to the surface_buffer - # before any other shader starts reading it. - rd.barrier(RenderingDevice.BARRIER_MASK_ALL_BARRIERS) - # 2. Dispatch PASS 2 (Generate Indices) var pipeline2 := rd.compute_pipeline_create(shader_pass2) # Indices only compute_list = rd.compute_list_begin() @@ -145,14 +151,6 @@ func compute_mesh(world_size: int, threshold: float) -> ArrayMesh: var out_indices = rd.buffer_get_data(idx_buffer).to_int32_array() var final_count = rd.buffer_get_data(counter_buffer).to_int32_array()[0] var out_surface_points = rd.buffer_get_data(surface_buffer).to_float32_array() - - rd.free_rid(buffer) - rd.free_rid(params_buffer) - rd.free_rid(surface_buffer) - rd.free_rid(normal_buffer) - rd.free_rid(uv_buffer) - rd.free_rid(idx_buffer) - rd.free_rid(counter_buffer) # 5. Build the Mesh var mesh = ArrayMesh.new() @@ -233,3 +231,42 @@ func build_surface_dict(world_size: int, flat_buffer: PackedFloat32Array) -> Dic dict[voxel_id] = surface_pos return dict + +func _exit_tree(): + # If the rendering device wasn't initialized, we have nothing to free + if not rd: + return + + # 1. Free Shader and Pipeline RIDs + # Pipelines depend on shaders, so free them first + if pipeline1.is_valid(): + rd.free_rid(pipeline1) + if pipeline2.is_valid(): + rd.free_rid(pipeline2) + + if shader_pass1.is_valid(): + rd.free_rid(shader_pass1) + if shader_pass2.is_valid(): + rd.free_rid(shader_pass2) + + # 2. Free Buffer RIDs + # These are the actual memory allocations on the VRAM + var buffers_to_free = [ + buffer, + surface_buffer, + normal_buffer, + uv_buffer, + idx_buffer, + counter_buffer, + chunk_position_buffer, + params_buffer + ] + + for b_rid in buffers_to_free: + if b_rid.is_valid(): + rd.free_rid(b_rid) + + # 3. Finalize the Rendering Device + # This tells Godot we are done with this local device entirely + rd.free() + rd = null diff --git a/compute_samples.gd.uid b/SurfaceNetsWorld/compute_samples.gd.uid similarity index 100% rename from compute_samples.gd.uid rename to SurfaceNetsWorld/compute_samples.gd.uid diff --git a/sdf_shader.glsl b/SurfaceNetsWorld/compute_surface_points.glsl similarity index 94% rename from sdf_shader.glsl rename to SurfaceNetsWorld/compute_surface_points.glsl index c2f8ce8..ba2a243 100644 --- a/sdf_shader.glsl +++ b/SurfaceNetsWorld/compute_surface_points.glsl @@ -35,6 +35,10 @@ layout(set = 0, binding = 6, std430) buffer Counter { uint count; } index_count; +layout(set = 0, binding = 7, std430) buffer ChunkPos { + float position_array[]; +} chunk; + uint index3(uint x, uint y, uint z) { return x + y * params.world_size + z * params.world_size * params.world_size; } @@ -147,7 +151,7 @@ float snoise(vec3 v) vec4 m = max(0.5 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); m = m * m; return 105.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), - dot(p2,x2), dot(p3,x3) ) ); + dot(p2,x2), dot(p3,x3) ) ) + params.threshold; } const ivec3 AXIS[3] = ivec3[]( @@ -181,7 +185,13 @@ const ivec3 CORNERS[8] = ivec3[]( ); float get_noise_at(vec3 p) { - return snoise((p / 20.0) + vec3(params.u_time * .5)); + + float chunk_x = chunk.position_array[0] * -.5; + float chunk_y = chunk.position_array[1] * -.5; + float chunk_z = chunk.position_array[2] * -.5; + p = p - vec3(chunk_x, chunk_y, chunk_z); + p = p / 20.0; + return snoise(p); } // Calculate normal using central difference diff --git a/SurfaceNetsWorld/compute_surface_points.glsl.import b/SurfaceNetsWorld/compute_surface_points.glsl.import new file mode 100644 index 0000000..4eb5f52 --- /dev/null +++ b/SurfaceNetsWorld/compute_surface_points.glsl.import @@ -0,0 +1,14 @@ +[remap] + +importer="glsl" +type="RDShaderFile" +uid="uid://dv4s7mmqwnsqr" +path="res://.godot/imported/compute_surface_points.glsl-11430003a7d9b84dfd57d5dcf11b574d.res" + +[deps] + +source_file="res://SurfaceNetsWorld/compute_surface_points.glsl" +dest_files=["res://.godot/imported/compute_surface_points.glsl-11430003a7d9b84dfd57d5dcf11b574d.res"] + +[params] + diff --git a/generate_mesh.tres b/SurfaceNetsWorld/generate_mesh.tres similarity index 97% rename from generate_mesh.tres rename to SurfaceNetsWorld/generate_mesh.tres index f6047a2..80663c1 100644 --- a/generate_mesh.tres +++ b/SurfaceNetsWorld/generate_mesh.tres @@ -4,7 +4,6 @@ constant = Color(0.30202293, 0.6060653, 0.9109474, 1) [resource] -modes/cull = 2 nodes/vertex/0/position = Vector2(360, 220) nodes/fragment/0/position = Vector2(660, 140) nodes/fragment/2/node = SubResource("VisualShaderNodeColorConstant_sxi40") diff --git a/sdf_mesh_generation.glsl b/SurfaceNetsWorld/sdf_mesh_generation.glsl similarity index 79% rename from sdf_mesh_generation.glsl rename to SurfaceNetsWorld/sdf_mesh_generation.glsl index 87f9755..2cf38f8 100644 --- a/sdf_mesh_generation.glsl +++ b/SurfaceNetsWorld/sdf_mesh_generation.glsl @@ -17,7 +17,8 @@ bool has_vertex(ivec3 p) { // Boundary check for the voxel itself if (any(lessThan(p, ivec3(0))) || any(greaterThanEqual(p, ivec3(params.world_size)))) return false; uint idx = index3(uint(p.x), uint(p.y), uint(p.z)); - return surface.surface_points[idx * 3u] > - 0.5; + float val = surface.surface_points[idx * 3u]; + return (val >= 0.0 && val <= float(params.world_size)); } // Translated QUAD_POINTS from your GDScript @@ -30,25 +31,26 @@ const ivec3 QUAD_OFFSETS[3][4] = ivec3[3][4]( ivec3[](ivec3(0,0,0), ivec3(0,-1,0), ivec3(-1,-1,0), ivec3(-1,0,0)) ); +const ivec3 AXIS[3] = ivec3[](ivec3(1,0,0), ivec3(0,1,0), ivec3(0,0,1)); + + void main() { memoryBarrierBuffer(); ivec3 id = ivec3(gl_GlobalInvocationID); - if (id.x >= params.world_size || id.y >= params.world_size || id.z >= params.world_size) return; + if (any(greaterThanEqual(id, ivec3(params.world_size)))) return; uint idx = index3(id.x, id.y, id.z); - // Check the 3 primary axes (forward edges) - const ivec3 AXIS[3] = ivec3[](ivec3(1,0,0), ivec3(0,1,0), ivec3(0,0,1)); for (int i = 0; i < 3; i++) { ivec3 neighbor_id = id + AXIS[i]; - if (any(greaterThan(neighbor_id, ivec3(params.world_size)))) continue; + if (any(greaterThanEqual(neighbor_id, ivec3(params.world_size)))) continue; float d1 = voxels.sample_points[idx]; float d2 = voxels.sample_points[index3(neighbor_id.x, neighbor_id.y, neighbor_id.z)]; - if ((d1 < 0.0) != (d2 < 0.0)) { + if ((d1 < 0.0) != (d2 < 0.0) && d1 != -d2) { ivec3 p0 = id + QUAD_OFFSETS[i][0]; ivec3 p1 = id + QUAD_OFFSETS[i][1]; ivec3 p2 = id + QUAD_OFFSETS[i][2]; @@ -70,12 +72,13 @@ void main() { mesh_indices.indices[start + 4] = v2; mesh_indices.indices[start + 5] = v3; } else { + // Reverse the order for the other side of the surface mesh_indices.indices[start + 0] = v0; - mesh_indices.indices[start + 1] = v2; - mesh_indices.indices[start + 2] = v1; + mesh_indices.indices[start + 1] = v3; + mesh_indices.indices[start + 2] = v2; mesh_indices.indices[start + 3] = v0; - mesh_indices.indices[start + 4] = v3; - mesh_indices.indices[start + 5] = v2; + mesh_indices.indices[start + 4] = v2; + mesh_indices.indices[start + 5] = v1; } } } diff --git a/SurfaceNetsWorld/sdf_mesh_generation.glsl.import b/SurfaceNetsWorld/sdf_mesh_generation.glsl.import new file mode 100644 index 0000000..e393bf4 --- /dev/null +++ b/SurfaceNetsWorld/sdf_mesh_generation.glsl.import @@ -0,0 +1,14 @@ +[remap] + +importer="glsl" +type="RDShaderFile" +uid="uid://d348vk1vnsbps" +path="res://.godot/imported/sdf_mesh_generation.glsl-d7c76c8683be743d5aa10359d91ec159.res" + +[deps] + +source_file="res://SurfaceNetsWorld/sdf_mesh_generation.glsl" +dest_files=["res://.godot/imported/sdf_mesh_generation.glsl-d7c76c8683be743d5aa10359d91ec159.res"] + +[params] + diff --git a/SurfaceNetsWorld/smooth_world.gd b/SurfaceNetsWorld/smooth_world.gd new file mode 100644 index 0000000..a414eed --- /dev/null +++ b/SurfaceNetsWorld/smooth_world.gd @@ -0,0 +1,31 @@ +extends Node3D + +@export var chunk_size = 16 +@export var world_size = 6 +@export var threshold = 0.2 + +var chunk_scene = preload("res://SurfaceNetsWorld/chunk.tscn") + +var ComputeSdf = preload("res://SurfaceNetsWorld/compute_samples.gd") +var gpu_sdf + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + gpu_sdf = ComputeSdf.new() + gpu_sdf.create_device(chunk_size) + for x in range(world_size): + for y in range(world_size): + for z in range(world_size): + var chunk: Node = chunk_scene.instantiate() + chunk.chunk_size = chunk_size + chunk.threshold = threshold + chunk.position = Vector3(x * chunk_size, y * chunk_size, z * chunk_size) + chunk.generate_chunk(gpu_sdf) + await Engine.get_main_loop().process_frame + add_child(chunk) + + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass diff --git a/SurfaceNetsWorld/smooth_world.gd.uid b/SurfaceNetsWorld/smooth_world.gd.uid new file mode 100644 index 0000000..c3a27c3 --- /dev/null +++ b/SurfaceNetsWorld/smooth_world.gd.uid @@ -0,0 +1 @@ +uid://cgf3kpllu4cv7 diff --git a/SurfaceNetsWorld/smooth_world.tscn b/SurfaceNetsWorld/smooth_world.tscn new file mode 100644 index 0000000..b9371ab --- /dev/null +++ b/SurfaceNetsWorld/smooth_world.tscn @@ -0,0 +1,9 @@ +[gd_scene format=3 uid="uid://d13vfr2vhyq17"] + +[ext_resource type="Script" uid="uid://cgf3kpllu4cv7" path="res://SurfaceNetsWorld/smooth_world.gd" id="1_4h467"] + +[node name="SmoothWorld" type="Node3D" unique_id=113243680] +script = ExtResource("1_4h467") +chunk_size = 64 +world_size = 4 +threshold = 0.045 diff --git a/activation.gd b/activation.gd index 6a8b4af..908da37 100644 --- a/activation.gd +++ b/activation.gd @@ -1,4 +1,3 @@ -@tool extends Node3D enum EnumWorld {CUBIC_WORLD, SMOOTH_WORLD} @@ -7,7 +6,7 @@ enum EnumWorld {CUBIC_WORLD, SMOOTH_WORLD} var enabled_world: EnumWorld = EnumWorld.CUBIC_WORLD -var smooth_world = preload("res://smooth_world.tscn") +var smooth_world = preload("res://SurfaceNetsWorld/smooth_world.tscn") var cubic_world = preload("res://cubic_world.tscn") signal change_world diff --git a/sdf_mesh_generation.glsl.import b/sdf_mesh_generation.glsl.import deleted file mode 100644 index 7c8e2d7..0000000 --- a/sdf_mesh_generation.glsl.import +++ /dev/null @@ -1,14 +0,0 @@ -[remap] - -importer="glsl" -type="RDShaderFile" -uid="uid://d348vk1vnsbps" -path="res://.godot/imported/sdf_mesh_generation.glsl-debc521de09c2e8ab62d3825224df785.res" - -[deps] - -source_file="res://sdf_mesh_generation.glsl" -dest_files=["res://.godot/imported/sdf_mesh_generation.glsl-debc521de09c2e8ab62d3825224df785.res"] - -[params] - diff --git a/sdf_shader.glsl.import b/sdf_shader.glsl.import deleted file mode 100644 index e33881b..0000000 --- a/sdf_shader.glsl.import +++ /dev/null @@ -1,14 +0,0 @@ -[remap] - -importer="glsl" -type="RDShaderFile" -uid="uid://dv4s7mmqwnsqr" -path="res://.godot/imported/sdf_shader.glsl-8a6992e3be4c1416763f59c511add1b8.res" - -[deps] - -source_file="res://sdf_shader.glsl" -dest_files=["res://.godot/imported/sdf_shader.glsl-8a6992e3be4c1416763f59c511add1b8.res"] - -[params] - diff --git a/smooth_world.gd b/smooth_world.gd deleted file mode 100644 index 00c978c..0000000 --- a/smooth_world.gd +++ /dev/null @@ -1,94 +0,0 @@ -#@tool -extends Node3D -const CENTER := Vector3.ZERO -@export var RADIUS: float = 5.0 - -var sample_points = {} -var SamplePoint = preload("res://sample_point.gd") -var surface_points = {} -var SurfacePoint = preload("res://surface_point.gd") -var ComputeSdf = preload("res://compute_samples.gd") -var gpu_sdf - -var generate_mesh_shader = preload("res://generate_mesh.tres") - -@export var regenerate_mesh = false - - -@export var world_size = 16 -@export var show_sample_points = false -@export var show_surface_points = false -@export var show_surface = true - -@export var do_the_thing: bool = false -@export var clear_the_thing: bool = false - -var color = Color.CORAL -var surface_point_color = Color.CRIMSON - -var mesh -var surface_tool: SurfaceTool = SurfaceTool.new() - -var meshinstance = MeshInstance3D.new() -var material = ShaderMaterial.new() - -func _ready() -> void: - material.shader = generate_mesh_shader - meshinstance.material_override = material - add_child(meshinstance) - gpu_sdf = ComputeSdf.new() - gpu_sdf.create_device() - meshinstance.mesh = gpu_sdf.compute_mesh(world_size, RADIUS) - -func _input(event): - if event is InputEventKey and event.is_action_released("RegenerateMesh"): - if regenerate_mesh: - regenerate_mesh = false - else: - regenerate_mesh = true - -func _process(_delta: float) -> void: - - if show_surface && regenerate_mesh: - #regenerate_mesh = false - clear() - meshinstance.mesh = gpu_sdf.compute_mesh(world_size, RADIUS) - if show_surface_points: - var idx = 0 - var text_id = 0 - while idx < gpu_sdf.iout_surface_points.size(): - text_id += 1 - var value = Vector3(gpu_sdf.iout_surface_points.get(idx), gpu_sdf.iout_surface_points.get(idx+1), gpu_sdf.iout_surface_points.get(idx+2)) - Vector3(world_size / 2, world_size / 2, world_size / 2) - if gpu_sdf.iout_surface_points.get(idx) != -1.0: - DebugDraw3D.draw_square(value, 0.2, color.from_rgba8(255, 128, 128, 255)) - if text_id % 3 == 0: - DebugDraw3D.draw_text(value, str(value), 35) - idx += 3 - else: - idx += 1 - if show_sample_points: - draw_sample_points() - - -func clear(): - surface_points = {} - sample_points = {} - mesh = ArrayMesh.new() - - -func get_index_from_coords(coords: Vector3i): - return coords.x + coords.y * world_size + coords.z * world_size * world_size - -func draw_sample_points(): - for x in range(world_size): - for y in range(world_size): - for z in range(world_size): - if sample_points.has(Vector3i(x, y, z)): - DebugDraw3D.draw_square(Vector3i(x, y, z), 0.2, color.from_rgba8(sample_points.get(Vector3i(x, y, z)), 128, 128, 255)) - #DebugDraw3D.draw_text(Vector3i(x, y, z), str(Vector3i(x, y, z))) - -func draw_surface_points(): - for surface_point in surface_points: - if surface_points[surface_point] != Vector3(0, 0, 0): - DebugDraw3D.draw_square(surface_points[surface_point] , 0.3, surface_point_color) - #DebugDraw3D.draw_text(surface_points[surface_point], str(surface_points[surface_point]))