-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMain.gd
264 lines (210 loc) · 8.92 KB
/
Main.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
extends Node3D
var planet_scene: PackedScene = load("res://Planet/Planet.tscn")
@export_subgroup("Animation Settings")
@export var paused: bool = true
@export var use_compute_shader: bool = true
@export var planets_per_timeout: int = 1
@export var max_planets: int = 2000
@export var max_suns: int = 10
@export var initial_planets: int = 1000
@export_subgroup("Animation Parameters")
@export var planet_interaction: bool = true
@export var sun_interaction: bool = false
@export var rotation_speed: float = 1.0
# Compute shader related globals
var rd: RenderingDevice
var shader: RID
var pipeline: RID
var uniform_set: RID
var bindings: Array[RDUniform]
var buffers: Array[RID]
const INVOCATION_SIZE: int = 8
func _ready():
$Suns/Sun.set_instance_shader_parameter("color", Color(1., 0.2, 0.2))
$Suns/Sun.rotation_speed = rotation_speed
$Suns/Sun.is_sun = true
$Suns/Sun.handle_process = not use_compute_shader
$Suns/Sun.mass = 100000.
$Suns/Sun.paused = paused
for i in range(initial_planets):
spawn_planet()
if use_compute_shader:
setup_compute_shader()
func _process(delta):
var direction: Vector2 = Input.get_vector("move_left", "move_right", "move_down", "move_up")
if direction:
$Suns/Sun.position.x += direction.x
$Suns/Sun.position.y += direction.y
if paused:
return
if use_compute_shader:
update_compute_shader(delta)
func _unhandled_input(event):
if event.is_action_pressed("toggle_pause"):
paused = not paused
$Interface/Control/LabelContainer/SettingsContainer/UseComputeShader.focus_mode = Control.FOCUS_NONE
$Interface/Control/LabelContainer/SettingsContainer/PlanetInteraction.focus_mode = Control.FOCUS_NONE
$Interface.toggle_pause(paused)
for planet in $Planets.get_children():
planet.paused = paused
for sun in $Suns.get_children():
sun.paused = paused
func get_random_position() -> Vector3:
return Vector3(0, randf_range(-20, 20), randf_range(400, 800))
func get_random_mass() -> float:
return randf_range(80., 100.)
func spawn_planet() -> void:
var planet = planet_scene.instantiate()
planet.mass = get_random_mass()
planet.position = get_random_position()
planet.velocity = Vector3(planet.mass, 0, 0)
planet.radius = planet.mass / 20.
planet.is_sun = false
planet.handle_process = not use_compute_shader
planet.rotation_speed = rotation_speed
planet.paused = paused
planet.set_instance_shader_parameter("color", Color(randf(), randf(), randf()))
planet.planets = $Planets.get_children()
planet.suns = $Suns.get_children()
$Planets.add_child(planet)
func _on_timer_timeout():
if $Planets.get_child_count() >= max_planets:
$Timer.stop()
return
$Interface.change_counter($Planets.get_child_count(), max_planets)
for i in range(planets_per_timeout):
spawn_planet()
func setup_compute_shader() -> void:
rd = RenderingServer.create_local_rendering_device()
var shader_file := load("res://ComputeSpace.glsl")
var shader_spirv: RDShaderSPIRV = shader_file.get_spirv()
shader = rd.shader_create_from_spirv(shader_spirv)
# planet_data is the data that will be passed to the compute shader
# it keeps the data of all the planets in the scene.
var planet_buffer := rd.storage_buffer_create(max_planets * 10 * 4)
var planet_uniform := RDUniform.new()
planet_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
planet_uniform.binding = 0
planet_uniform.add_id(planet_buffer)
# output_data is the data that will be read back from the compute shader.
# it will keep the new data of all the planets in the scene.
var output_buffer = rd.storage_buffer_create(max_planets * 10 * 4)
var output_uniform := RDUniform.new()
output_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
output_uniform.binding = 1
output_uniform.add_id(output_buffer)
# sun_data is the data that will be passed to the compute shader
# it keeps the data of the sun(s) in the scene.
var sun_buffer = rd.storage_buffer_create(max_suns * 10 * 4)
var sun_uniform := RDUniform.new()
sun_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
sun_uniform.binding = 2
sun_uniform.add_id(sun_buffer)
# new_sun_data is the data that will be passed to the compute shader
# it keeps the data of the sun(s) in the scene.
var new_sun_buffer = rd.storage_buffer_create(max_suns * 10 * 4)
var new_sun_uniform := RDUniform.new()
new_sun_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
new_sun_uniform.binding = 3
new_sun_uniform.add_id(new_sun_buffer)
# param_data is the data that will be passed to the compute shader
# it keeps various parameters that will be used in the shader
var param_data := get_params()
var param_bytes = param_data.to_byte_array()
var param_buffer = rd.storage_buffer_create(param_bytes.size(), param_bytes)
var param_uniform := RDUniform.new()
param_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
param_uniform.binding = 4
param_uniform.add_id(param_buffer)
bindings = [planet_uniform, output_uniform, sun_uniform, new_sun_uniform, param_uniform]
buffers = [planet_buffer, output_buffer, sun_buffer, new_sun_buffer, param_buffer]
# Create a uniform set (the last parameter (the 0) needs to match the "set" in our shader file)
uniform_set = rd.uniform_set_create(bindings, shader, 0)
# Create a compute pipeline
pipeline = rd.compute_pipeline_create(shader)
func update_compute_shader(delta: float = 0) -> void:
# Update the planet_data
var planet_data := get_planet_data()
var planet_bytes := planet_data.to_byte_array()
rd.buffer_update(buffers[0], 0, planet_bytes.size(), planet_bytes)
rd.buffer_update(buffers[1], 0, planet_bytes.size(), planet_bytes)
# Update the sun_data
var sun_data := get_sun_data()
var sun_bytes := sun_data.to_byte_array()
rd.buffer_update(buffers[2], 0, sun_bytes.size(), sun_bytes)
rd.buffer_update(buffers[3], 0, sun_bytes.size(), sun_bytes)
# Update the param_data
var params := get_params(delta)
var params_bytes = params.to_byte_array()
rd.buffer_update(buffers[4], 0, params_bytes.size(), params_bytes)
# Bind the compute pipeline
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)
rd.compute_list_dispatch(compute_list, ceil(float($Planets.get_child_count() + $Suns.get_child_count()) / INVOCATION_SIZE), 1, 1)
rd.compute_list_end()
# Submit to GPU and wait for sync
rd.submit()
rd.sync()
# Read back the data from the planet buffer
var new_planet_bytes := rd.buffer_get_data(buffers[1])
var new_planet_data := new_planet_bytes.to_float32_array()
# Read back the data from the sun buffer
var new_sun_bytes := rd.buffer_get_data(buffers[3])
var new_sun_data := new_sun_bytes.to_float32_array()
set_planet_data(new_planet_data)
set_sun_data(new_sun_data)
func get_planet_data() -> PackedFloat32Array:
var planet_data: PackedFloat32Array = []
for planet in $Planets.get_children():
var data: PackedFloat32Array = planet.to_float32_array()
planet_data.append_array(data)
return planet_data
func get_sun_data() -> PackedFloat32Array:
var sun_data: PackedFloat32Array = []
for sun in $Suns.get_children():
var data: PackedFloat32Array = sun.to_float32_array()
sun_data.append_array(data)
return sun_data
func get_params(delta: float = 0) -> PackedFloat32Array:
var param_data: PackedFloat32Array = []
param_data.append_array([delta, planet_interaction, sun_interaction, rotation_speed, float($Planets.get_child_count()), float($Suns.get_child_count())])
return param_data
func set_planet_data(data: PackedFloat32Array) -> void:
for i in range($Planets.get_child_count()):
var planet = $Planets.get_child(i)
planet.mass = data[i * 10]
planet.position.x = data[i * 10 + 1]
planet.position.y = data[i * 10 + 2]
planet.position.z = data[i * 10 + 3]
planet.rotation.x = data[i * 10 + 4]
planet.rotation.y = data[i * 10 + 5]
planet.rotation.z = data[i * 10 + 6]
planet.velocity.x = data[i * 10 + 7]
planet.velocity.y = data[i * 10 + 8]
planet.velocity.z = data[i * 10 + 9]
func set_sun_data(data: PackedFloat32Array) -> void:
for i in range($Suns.get_child_count()):
var sun = $Suns.get_child(i)
sun.mass = data[i * 10]
sun.position.x = data[i * 10 + 1]
sun.position.y = data[i * 10 + 2]
sun.position.z = data[i * 10 + 3]
sun.rotation.x = data[i * 10 + 4]
sun.rotation.y = data[i * 10 + 5]
sun.rotation.z = data[i * 10 + 6]
sun.velocity.x = data[i * 10 + 7]
sun.velocity.y = data[i * 10 + 8]
sun.velocity.z = data[i * 10 + 9]
func _on_interface_use_compute_shader_changed(toggled_on: bool) -> void:
self.use_compute_shader = toggled_on
self.update_planets()
func _on_interface_planet_interaction_changed(toggled_on) -> void:
self.planet_interaction = toggled_on
self.update_planets()
func update_planets() -> void:
for planet in $Planets.get_children():
planet.handle_process = not self.use_compute_shader
planet.planet_interaction = self.planet_interaction
for planet in $Suns.get_children():
planet.handle_process = not self.use_compute_shader