Added footsteps, new tree, various other tweaks
This commit is contained in:
191
addons/proton_scatter/src/modifiers/relax.gd
Normal file
191
addons/proton_scatter/src/modifiers/relax.gd
Normal file
@@ -0,0 +1,191 @@
|
||||
@tool
|
||||
extends "base_modifier.gd"
|
||||
|
||||
|
||||
static var shader_file: RDShaderFile
|
||||
|
||||
|
||||
@export var iterations : int = 3
|
||||
@export var offset_step : float = 0.01
|
||||
@export var consecutive_step_multiplier : float = 0.5
|
||||
@export var use_computeshader : bool = true
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
display_name = "Relax Position"
|
||||
category = "Edit"
|
||||
global_reference_frame_available = false
|
||||
local_reference_frame_available = false
|
||||
individual_instances_reference_frame_available = false
|
||||
can_restrict_height = true
|
||||
restrict_height = true
|
||||
|
||||
documentation.add_warning(
|
||||
"This modifier is has an O(n²) complexity and will be slow with
|
||||
large amounts of points, unless your device supports compute shaders.",
|
||||
1)
|
||||
|
||||
var p := documentation.add_parameter("iterations")
|
||||
p.set_type("int")
|
||||
p.set_cost(2)
|
||||
p.set_description(
|
||||
"How many times the relax algorithm will run. Increasing this value will
|
||||
generally improves the result, at the cost of execution speed."
|
||||
)
|
||||
|
||||
p = documentation.add_parameter("Offset step")
|
||||
p.set_type("float")
|
||||
p.set_cost(0)
|
||||
p.set_description("How far the transform will be pushed away each iteration.")
|
||||
|
||||
p = documentation.add_parameter("Consecutive step multiplier")
|
||||
p.set_type("float")
|
||||
p.set_cost(0)
|
||||
p.set_description(
|
||||
"On each iteration, multiply the offset step by this value. This value
|
||||
is usually set between 0 and 1, to make the effect less pronounced on
|
||||
successive iterations.")
|
||||
|
||||
p = documentation.add_parameter("Use compute shader")
|
||||
p.set_cost(0)
|
||||
p.set_type("bool")
|
||||
p.set_description(
|
||||
"Run the calculations on the GPU instead of the CPU. This provides
|
||||
a significant speed boost and should be enabled when possible.")
|
||||
p.add_warning(
|
||||
"This parameter can't be enabled when using the OpenGL backend or running
|
||||
in headless mode.", 2)
|
||||
|
||||
|
||||
func _process_transforms(transforms, _domain, _seed) -> void:
|
||||
var offset := offset_step
|
||||
if transforms.size() < 2:
|
||||
return
|
||||
|
||||
# Disable the use of compute shader, if we cannot create a RenderingDevice
|
||||
if use_computeshader:
|
||||
var rd := RenderingServer.create_local_rendering_device()
|
||||
if rd == null:
|
||||
use_computeshader = false
|
||||
else:
|
||||
rd.free()
|
||||
rd = null
|
||||
|
||||
if use_computeshader:
|
||||
for iteration in iterations:
|
||||
if interrupt_update:
|
||||
return
|
||||
var movedir: PackedVector3Array = compute_closest(transforms)
|
||||
for i in transforms.size():
|
||||
var dir = movedir[i]
|
||||
if restrict_height:
|
||||
dir.y = 0.0
|
||||
# move away from closest point
|
||||
transforms.list[i].origin += dir.normalized() * offset
|
||||
|
||||
offset *= consecutive_step_multiplier
|
||||
|
||||
else:
|
||||
# calculate the relax transforms on the cpu
|
||||
for iteration in iterations:
|
||||
for i in transforms.size():
|
||||
if interrupt_update:
|
||||
return
|
||||
var min_vector = Vector3.ONE * 99999.0
|
||||
var threshold := 99999.0
|
||||
var distance := 0.0
|
||||
var diff: Vector3
|
||||
|
||||
# Find the closest point
|
||||
for j in transforms.size():
|
||||
if i == j:
|
||||
continue
|
||||
|
||||
diff = transforms.list[i].origin - transforms.list[j].origin
|
||||
distance = diff.length_squared()
|
||||
|
||||
if distance < threshold:
|
||||
min_vector = diff
|
||||
threshold = distance
|
||||
|
||||
if restrict_height:
|
||||
min_vector.y = 0.0
|
||||
|
||||
# move away from closest point
|
||||
transforms.list[i].origin += min_vector.normalized() * offset
|
||||
|
||||
offset *= consecutive_step_multiplier
|
||||
|
||||
|
||||
# compute the closest points to each other using a compute shader
|
||||
# return a vector for each point that points away from the closest neighbour
|
||||
func compute_closest(transforms) -> PackedVector3Array:
|
||||
var padded_num_vecs = ceil(float(transforms.size()) / 64.0) * 64
|
||||
var padded_num_floats = padded_num_vecs * 4
|
||||
var rd := RenderingServer.create_local_rendering_device()
|
||||
var shader_spirv: RDShaderSPIRV = get_shader_file().get_spirv()
|
||||
var shader := rd.shader_create_from_spirv(shader_spirv)
|
||||
# Prepare our data. We use vec4 floats in the shader, so we need 32 bit.
|
||||
var input := PackedFloat32Array()
|
||||
for i in transforms.size():
|
||||
input.append(transforms.list[i].origin.x)
|
||||
input.append(transforms.list[i].origin.y)
|
||||
input.append(transforms.list[i].origin.z)
|
||||
input.append(0) # needed to use vec4, necessary for byte alignment in the shader code
|
||||
# buffer size, number of vectors sent to the gpu
|
||||
input.resize(padded_num_floats) # indexing in the compute shader requires padding
|
||||
var input_bytes := input.to_byte_array()
|
||||
var output_bytes := input_bytes.duplicate()
|
||||
# Create a storage buffer that can hold our float values.
|
||||
var buffer_in := rd.storage_buffer_create(input_bytes.size(), input_bytes)
|
||||
var buffer_out := rd.storage_buffer_create(output_bytes.size(), output_bytes)
|
||||
|
||||
# Create a uniform to assign the buffer to the rendering device
|
||||
var uniform_in := RDUniform.new()
|
||||
uniform_in.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
|
||||
uniform_in.binding = 0 # this needs to match the "binding" in our shader file
|
||||
uniform_in.add_id(buffer_in)
|
||||
# Create a uniform to assign the buffer to the rendering device
|
||||
var uniform_out := RDUniform.new()
|
||||
uniform_out.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
|
||||
uniform_out.binding = 1 # this needs to match the "binding" in our shader file
|
||||
uniform_out.add_id(buffer_out)
|
||||
# the last parameter (the 0) needs to match the "set" in our shader file
|
||||
var uniform_set := rd.uniform_set_create([uniform_in, uniform_out], shader, 0)
|
||||
|
||||
# Create a compute pipeline
|
||||
var pipeline := rd.compute_pipeline_create(shader)
|
||||
var compute_list := rd.compute_list_begin()
|
||||
rd.compute_list_bind_compute_pipeline(compute_list, pipeline)
|
||||
rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0)
|
||||
# each workgroup computes 64 vectors
|
||||
# print("Dispatching workgroups: ", padded_num_vecs/64)
|
||||
rd.compute_list_dispatch(compute_list, padded_num_vecs/64, 1, 1)
|
||||
rd.compute_list_end()
|
||||
# Submit to GPU and wait for sync
|
||||
rd.submit()
|
||||
rd.sync()
|
||||
# Read back the data from the buffer
|
||||
var result_bytes := rd.buffer_get_data(buffer_out)
|
||||
var result := result_bytes.to_float32_array()
|
||||
var retval = PackedVector3Array()
|
||||
for i in transforms.size():
|
||||
retval.append(Vector3(result[i*4], result[i*4+1], result[i*4+2]))
|
||||
|
||||
# Free the allocated objects.
|
||||
# All resources must be freed after use to avoid memory leaks.
|
||||
if rd != null:
|
||||
rd.free_rid(pipeline)
|
||||
rd.free_rid(uniform_set)
|
||||
rd.free_rid(shader)
|
||||
rd.free_rid(buffer_in)
|
||||
rd.free_rid(buffer_out)
|
||||
rd.free()
|
||||
rd = null
|
||||
return retval
|
||||
|
||||
func get_shader_file() -> RDShaderFile:
|
||||
if shader_file == null:
|
||||
shader_file = load(get_script().resource_path.get_base_dir() + "/compute_shaders/compute_relax.glsl")
|
||||
|
||||
return shader_file
|
||||
Reference in New Issue
Block a user