world can be edited
This commit is contained in:
@@ -4,7 +4,7 @@ const CENTER := Vector3.ZERO
|
||||
|
||||
var generate_mesh_shader = preload("res://SurfaceNetsWorld/generate_mesh.tres")
|
||||
|
||||
@export var regenerate_mesh = false
|
||||
var regenerate_mesh = true
|
||||
|
||||
|
||||
@export var chunk_size = 16
|
||||
@@ -12,7 +12,7 @@ var generate_mesh_shader = preload("res://SurfaceNetsWorld/generate_mesh.tres")
|
||||
@export var show_surface_points = false
|
||||
@export var show_surface = true
|
||||
|
||||
var mesh
|
||||
var mesh: ArrayMesh
|
||||
|
||||
var color = Color.RED
|
||||
|
||||
@@ -21,25 +21,33 @@ var material = ShaderMaterial.new()
|
||||
|
||||
var gpu_sdf
|
||||
|
||||
var staticBody: StaticBody3D = StaticBody3D.new()
|
||||
var collisionShape3D: CollisionShape3D = CollisionShape3D.new()
|
||||
|
||||
|
||||
func generate_chunk(pgpu_sdf) -> void:
|
||||
signals_service.regenerate_mesh_sig.connect(_on_regenerate_mesh_sig)
|
||||
signals_service.add_matter.connect(_on_player_add_matter)
|
||||
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:
|
||||
if regenerate_mesh:
|
||||
regenerate_mesh = false
|
||||
clear()
|
||||
meshinstance.mesh = gpu_sdf.compute_mesh(chunk_size, threshold, self.position)
|
||||
var array_mesh = gpu_sdf.compute_mesh(chunk_size, threshold, self.position)
|
||||
var trimesh_collision = array_mesh.create_trimesh_shape()
|
||||
collisionShape3D.set_shape(trimesh_collision)
|
||||
staticBody = StaticBody3D.new()
|
||||
staticBody.add_child(collisionShape3D)
|
||||
add_child(staticBody)
|
||||
meshinstance.mesh = array_mesh
|
||||
if show_surface_points:
|
||||
var idx = 0
|
||||
var text_id = 0
|
||||
@@ -56,8 +64,50 @@ func _process(_delta: float) -> void:
|
||||
|
||||
|
||||
func clear():
|
||||
if staticBody.get_children().has(collisionShape3D):
|
||||
staticBody.remove_child(collisionShape3D)
|
||||
mesh = ArrayMesh.new()
|
||||
|
||||
|
||||
func get_index_from_coords(coords: Vector3i):
|
||||
return coords.x + coords.y * chunk_size + coords.z * chunk_size * chunk_size
|
||||
|
||||
var chunks_to_regenerate = [
|
||||
Vector3i(0,0,0),
|
||||
Vector3i(0,1,0),
|
||||
Vector3i(0,0,1),
|
||||
Vector3i(0,1,1),
|
||||
Vector3i(0,-1,0),
|
||||
Vector3i(0,0,-1),
|
||||
Vector3i(0,-1,-1),
|
||||
Vector3i(0,-1,1),
|
||||
Vector3i(0,1,-1),
|
||||
|
||||
Vector3i(1,0,0),
|
||||
Vector3i(1,0,1),
|
||||
Vector3i(-1,0,0),
|
||||
Vector3i(-1,0,-1),
|
||||
Vector3i(-1,0,1),
|
||||
Vector3i(1,0,-1),
|
||||
|
||||
Vector3i(1,1,0),
|
||||
Vector3i(-1,-1,0),
|
||||
Vector3i(-1,1,0),
|
||||
Vector3i(1,-1,0),
|
||||
Vector3i(1,1,1),
|
||||
Vector3i(-1,-1,-1),
|
||||
]
|
||||
|
||||
func _on_regenerate_mesh_sig(pposition: Vector3i):
|
||||
if Vector3i(self.global_position) == pposition:
|
||||
regenerate_mesh = true
|
||||
|
||||
func _on_player_add_matter(pposition: Vector3, size: float) -> void:
|
||||
var intpposition = Vector3i(pposition.x - int(pposition.x) % chunk_size, pposition.y - int(pposition.y) % chunk_size, pposition.z - int(pposition.z) % chunk_size)
|
||||
var intposition = Vector3i(self.global_position)
|
||||
if intpposition.x == intposition.x && intpposition.y == intposition.y && intpposition.z == intposition.z:
|
||||
for chunk_pos_to_regenerate in chunks_to_regenerate:
|
||||
chunk_pos_to_regenerate = Vector3i(intpposition.x + chunk_pos_to_regenerate.x * chunk_size, intpposition.y + chunk_pos_to_regenerate.y * chunk_size, intpposition.z + chunk_pos_to_regenerate.z * chunk_size)
|
||||
signals_service.regenerate_mesh_sig.emit(chunk_pos_to_regenerate)
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ var shader_pass2: RID
|
||||
var start_time := Time.get_ticks_msec() / 1000.0
|
||||
var color = Color.CORAL
|
||||
@export var iout_surface_points = []
|
||||
var player_edits = PackedFloat32Array()
|
||||
|
||||
var pipeline1
|
||||
var pipeline2
|
||||
@@ -20,9 +21,11 @@ var idx_buffer
|
||||
var counter_buffer
|
||||
var chunk_position_buffer
|
||||
var params_buffer
|
||||
var player_edits_buffer
|
||||
|
||||
|
||||
func create_device(world_size: int):
|
||||
|
||||
signals_service.add_matter.connect(_on_player_add_matter)
|
||||
rd = RenderingServer.create_local_rendering_device()
|
||||
|
||||
# 1. Load Shaders
|
||||
@@ -36,7 +39,7 @@ func create_device(world_size: int):
|
||||
pipeline2 = rd.compute_pipeline_create(shader_pass2)
|
||||
|
||||
# 3. Pre-allocate Buffers (assuming world_size is constant)
|
||||
# add 1 to look at adjacent voxels for chunk boundaries
|
||||
# add 2 to look at adjacent voxels for chunk boundaries
|
||||
var total = (world_size + 2) ** 3
|
||||
|
||||
# We create them once with empty/zero data of the correct size
|
||||
@@ -48,6 +51,7 @@ func create_device(world_size: int):
|
||||
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
|
||||
player_edits_buffer = rd.storage_buffer_create(256)
|
||||
|
||||
func compute_mesh(world_size: int, threshold: float, chunk_pos: Vector3) -> ArrayMesh:
|
||||
world_size += 2
|
||||
@@ -70,6 +74,11 @@ func compute_mesh(world_size: int, threshold: float, chunk_pos: Vector3) -> Arra
|
||||
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 player_edits_bytes = player_edits.to_byte_array()
|
||||
if player_edits_bytes.size() > 0:
|
||||
player_edits_buffer = rd.storage_buffer_create(player_edits_bytes.size())
|
||||
rd.buffer_update(player_edits_buffer, 0, player_edits_bytes.size(), player_edits_bytes)
|
||||
|
||||
var u_time := Time.get_ticks_msec() / 1000.0 - start_time
|
||||
var peer := StreamPeerBuffer.new()
|
||||
peer.put_32(world_size)
|
||||
@@ -116,20 +125,23 @@ func compute_mesh(world_size: int, threshold: float, chunk_pos: Vector3) -> Arra
|
||||
chunk_position_uniform.binding = 7
|
||||
chunk_position_uniform.add_id(chunk_position_buffer)
|
||||
|
||||
|
||||
var player_edits_uniform := RDUniform.new()
|
||||
player_edits_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
|
||||
player_edits_uniform.binding = 8
|
||||
player_edits_uniform.add_id(player_edits_buffer)
|
||||
|
||||
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, chunk_position_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, player_edits_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))
|
||||
|
||||
# 1. Dispatch PASS 1 (Calculate Points)
|
||||
var pipeline1 := rd.compute_pipeline_create(shader_pass1) # Points only
|
||||
pipeline1 = rd.compute_pipeline_create(shader_pass1) # Points only
|
||||
var compute_list = rd.compute_list_begin()
|
||||
rd.compute_list_bind_compute_pipeline(compute_list, pipeline1)
|
||||
rd.compute_list_bind_uniform_set(compute_list, uniform_set1, 0)
|
||||
@@ -137,7 +149,7 @@ func compute_mesh(world_size: int, threshold: float, chunk_pos: Vector3) -> Arra
|
||||
rd.compute_list_end()
|
||||
|
||||
# 2. Dispatch PASS 2 (Generate Indices)
|
||||
var pipeline2 := rd.compute_pipeline_create(shader_pass2) # Indices only
|
||||
pipeline2 = rd.compute_pipeline_create(shader_pass2) # Indices only
|
||||
compute_list = rd.compute_list_begin()
|
||||
rd.compute_list_bind_compute_pipeline(compute_list, pipeline2)
|
||||
rd.compute_list_bind_uniform_set(compute_list, uniform_set2, 0)
|
||||
@@ -159,9 +171,6 @@ func compute_mesh(world_size: int, threshold: float, chunk_pos: Vector3) -> Arra
|
||||
var arrays = []
|
||||
arrays.resize(Mesh.ARRAY_MAX)
|
||||
|
||||
# We need to reshape the flat float array into Vector3s
|
||||
var verts := PackedVector3Array()
|
||||
var normals := PackedVector3Array()
|
||||
# Instead of blindly appending every voxel, we check if the voxel was "active"
|
||||
# Or, even better, map the original voxel indices to new packed indices
|
||||
var active_map = {}
|
||||
@@ -234,6 +243,12 @@ func build_surface_dict(world_size: int, flat_buffer: PackedFloat32Array) -> Dic
|
||||
|
||||
return dict
|
||||
|
||||
func _on_player_add_matter(pposition: Vector3, size: float) -> void:
|
||||
player_edits.append(pposition.x)
|
||||
player_edits.append(pposition.y)
|
||||
player_edits.append(pposition.z)
|
||||
player_edits.append(size)
|
||||
|
||||
func _exit_tree():
|
||||
# If the rendering device wasn't initialized, we have nothing to free
|
||||
if not rd:
|
||||
|
||||
@@ -39,6 +39,11 @@ layout(set = 0, binding = 7, std430) buffer ChunkPos {
|
||||
float position_array[];
|
||||
} chunk;
|
||||
|
||||
layout(set = 0, binding = 8, std430) buffer PlayerEdits {
|
||||
float position_size_array[];
|
||||
} edits;
|
||||
|
||||
|
||||
uint index3(uint x, uint y, uint z) {
|
||||
return x + y * params.world_size + z * params.world_size * params.world_size;
|
||||
}
|
||||
@@ -79,6 +84,10 @@ vec4 taylorInvSqrt(vec4 r)
|
||||
return 1.79284291400159 - 0.85373472095314 * r;
|
||||
}
|
||||
|
||||
float sdsphere(vec3 p, float radius) {
|
||||
return length(p) - radius;
|
||||
}
|
||||
|
||||
float snoise(vec3 v)
|
||||
{
|
||||
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
|
||||
@@ -185,13 +194,21 @@ const ivec3 CORNERS[8] = ivec3[](
|
||||
);
|
||||
|
||||
float get_noise_at(vec3 p) {
|
||||
|
||||
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);
|
||||
|
||||
float return_value = 10000.0;
|
||||
|
||||
for (int i = 0; i < edits.position_size_array.length(); i += 4){
|
||||
return_value = min(return_value, sdsphere(p + vec3(edits.position_size_array[i] * -0.025, edits.position_size_array[i + 1] * -0.025, edits.position_size_array[i + 2] * -0.025), edits.position_size_array[i + 3]));
|
||||
}
|
||||
|
||||
return_value = min(return_value, snoise(p));
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
// Calculate normal using central difference
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -4,6 +4,5 @@
|
||||
|
||||
[node name="SmoothWorld" type="Node3D" unique_id=113243680]
|
||||
script = ExtResource("1_4h467")
|
||||
chunk_size = 64
|
||||
world_size = 4
|
||||
threshold = 0.045
|
||||
world_size = 8
|
||||
threshold = 0.46
|
||||
|
||||
Reference in New Issue
Block a user