1 | /**************************************************************************/ |
2 | /* particles_storage.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #ifdef GLES3_ENABLED |
32 | |
33 | #include "particles_storage.h" |
34 | #include "material_storage.h" |
35 | #include "mesh_storage.h" |
36 | #include "texture_storage.h" |
37 | #include "utilities.h" |
38 | |
39 | #include "servers/rendering/rendering_server_default.h" |
40 | |
41 | using namespace GLES3; |
42 | |
43 | ParticlesStorage *ParticlesStorage::singleton = nullptr; |
44 | |
45 | ParticlesStorage *ParticlesStorage::get_singleton() { |
46 | return singleton; |
47 | } |
48 | |
49 | ParticlesStorage::ParticlesStorage() { |
50 | singleton = this; |
51 | GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); |
52 | |
53 | { |
54 | String global_defines; |
55 | global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n" ; // TODO: this is arbitrary for now |
56 | material_storage->shaders.particles_process_shader.initialize(global_defines, 1); |
57 | } |
58 | { |
59 | // default material and shader for particles shader |
60 | particles_shader.default_shader = material_storage->shader_allocate(); |
61 | material_storage->shader_initialize(particles_shader.default_shader); |
62 | material_storage->shader_set_code(particles_shader.default_shader, R"( |
63 | // Default particles shader. |
64 | |
65 | shader_type particles; |
66 | |
67 | void process() { |
68 | COLOR = vec4(1.0); |
69 | } |
70 | )" ); |
71 | particles_shader.default_material = material_storage->material_allocate(); |
72 | material_storage->material_initialize(particles_shader.default_material); |
73 | material_storage->material_set_shader(particles_shader.default_material, particles_shader.default_shader); |
74 | } |
75 | { |
76 | particles_shader.copy_shader.initialize(); |
77 | particles_shader.copy_shader_version = particles_shader.copy_shader.version_create(); |
78 | } |
79 | } |
80 | |
81 | ParticlesStorage::~ParticlesStorage() { |
82 | singleton = nullptr; |
83 | GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); |
84 | |
85 | material_storage->material_free(particles_shader.default_material); |
86 | material_storage->shader_free(particles_shader.default_shader); |
87 | particles_shader.copy_shader.version_free(particles_shader.copy_shader_version); |
88 | } |
89 | |
90 | /* PARTICLES */ |
91 | |
92 | RID ParticlesStorage::particles_allocate() { |
93 | return particles_owner.allocate_rid(); |
94 | } |
95 | |
96 | void ParticlesStorage::particles_initialize(RID p_rid) { |
97 | particles_owner.initialize_rid(p_rid, Particles()); |
98 | } |
99 | |
100 | void ParticlesStorage::particles_free(RID p_rid) { |
101 | update_particles(); |
102 | Particles *particles = particles_owner.get_or_null(p_rid); |
103 | particles->dependency.deleted_notify(p_rid); |
104 | _particles_free_data(particles); |
105 | particles_owner.free(p_rid); |
106 | } |
107 | |
108 | void ParticlesStorage::particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) { |
109 | Particles *particles = particles_owner.get_or_null(p_particles); |
110 | ERR_FAIL_NULL(particles); |
111 | if (particles->mode == p_mode) { |
112 | return; |
113 | } |
114 | |
115 | _particles_free_data(particles); |
116 | |
117 | particles->mode = p_mode; |
118 | } |
119 | |
120 | void ParticlesStorage::particles_set_emitting(RID p_particles, bool p_emitting) { |
121 | Particles *particles = particles_owner.get_or_null(p_particles); |
122 | ERR_FAIL_NULL(particles); |
123 | |
124 | particles->emitting = p_emitting; |
125 | } |
126 | |
127 | bool ParticlesStorage::particles_get_emitting(RID p_particles) { |
128 | ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer." ); |
129 | Particles *particles = particles_owner.get_or_null(p_particles); |
130 | ERR_FAIL_NULL_V(particles, false); |
131 | |
132 | return particles->emitting; |
133 | } |
134 | |
135 | void ParticlesStorage::_particles_free_data(Particles *particles) { |
136 | particles->userdata_count = 0; |
137 | particles->instance_buffer_size_cache = 0; |
138 | particles->instance_buffer_stride_cache = 0; |
139 | particles->num_attrib_arrays_cache = 0; |
140 | particles->process_buffer_stride_cache = 0; |
141 | |
142 | if (particles->front_process_buffer != 0) { |
143 | glDeleteVertexArrays(1, &particles->front_vertex_array); |
144 | GLES3::Utilities::get_singleton()->buffer_free_data(particles->front_process_buffer); |
145 | GLES3::Utilities::get_singleton()->buffer_free_data(particles->front_instance_buffer); |
146 | particles->front_vertex_array = 0; |
147 | particles->front_process_buffer = 0; |
148 | particles->front_instance_buffer = 0; |
149 | |
150 | glDeleteVertexArrays(1, &particles->back_vertex_array); |
151 | GLES3::Utilities::get_singleton()->buffer_free_data(particles->back_process_buffer); |
152 | GLES3::Utilities::get_singleton()->buffer_free_data(particles->back_instance_buffer); |
153 | particles->back_vertex_array = 0; |
154 | particles->back_process_buffer = 0; |
155 | particles->back_instance_buffer = 0; |
156 | } |
157 | |
158 | if (particles->sort_buffer != 0) { |
159 | GLES3::Utilities::get_singleton()->buffer_free_data(particles->last_frame_buffer); |
160 | GLES3::Utilities::get_singleton()->buffer_free_data(particles->sort_buffer); |
161 | particles->last_frame_buffer = 0; |
162 | particles->sort_buffer = 0; |
163 | particles->sort_buffer_filled = false; |
164 | particles->last_frame_buffer_filled = false; |
165 | } |
166 | |
167 | if (particles->frame_params_ubo != 0) { |
168 | GLES3::Utilities::get_singleton()->buffer_free_data(particles->frame_params_ubo); |
169 | particles->frame_params_ubo = 0; |
170 | } |
171 | } |
172 | |
173 | void ParticlesStorage::particles_set_amount(RID p_particles, int p_amount) { |
174 | Particles *particles = particles_owner.get_or_null(p_particles); |
175 | ERR_FAIL_NULL(particles); |
176 | |
177 | if (particles->amount == p_amount) { |
178 | return; |
179 | } |
180 | |
181 | _particles_free_data(particles); |
182 | |
183 | particles->amount = p_amount; |
184 | |
185 | particles->prev_ticks = 0; |
186 | particles->phase = 0; |
187 | particles->prev_phase = 0; |
188 | particles->clear = true; |
189 | |
190 | particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); |
191 | } |
192 | |
193 | void ParticlesStorage::particles_set_lifetime(RID p_particles, double p_lifetime) { |
194 | Particles *particles = particles_owner.get_or_null(p_particles); |
195 | ERR_FAIL_NULL(particles); |
196 | particles->lifetime = p_lifetime; |
197 | } |
198 | |
199 | void ParticlesStorage::particles_set_one_shot(RID p_particles, bool p_one_shot) { |
200 | Particles *particles = particles_owner.get_or_null(p_particles); |
201 | ERR_FAIL_NULL(particles); |
202 | particles->one_shot = p_one_shot; |
203 | } |
204 | |
205 | void ParticlesStorage::particles_set_pre_process_time(RID p_particles, double p_time) { |
206 | Particles *particles = particles_owner.get_or_null(p_particles); |
207 | ERR_FAIL_NULL(particles); |
208 | particles->pre_process_time = p_time; |
209 | } |
210 | void ParticlesStorage::particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) { |
211 | Particles *particles = particles_owner.get_or_null(p_particles); |
212 | ERR_FAIL_NULL(particles); |
213 | particles->explosiveness = p_ratio; |
214 | } |
215 | void ParticlesStorage::particles_set_randomness_ratio(RID p_particles, real_t p_ratio) { |
216 | Particles *particles = particles_owner.get_or_null(p_particles); |
217 | ERR_FAIL_NULL(particles); |
218 | particles->randomness = p_ratio; |
219 | } |
220 | |
221 | void ParticlesStorage::particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) { |
222 | Particles *particles = particles_owner.get_or_null(p_particles); |
223 | ERR_FAIL_NULL(particles); |
224 | particles->custom_aabb = p_aabb; |
225 | particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); |
226 | } |
227 | |
228 | void ParticlesStorage::particles_set_speed_scale(RID p_particles, double p_scale) { |
229 | Particles *particles = particles_owner.get_or_null(p_particles); |
230 | ERR_FAIL_NULL(particles); |
231 | |
232 | particles->speed_scale = p_scale; |
233 | } |
234 | void ParticlesStorage::particles_set_use_local_coordinates(RID p_particles, bool p_enable) { |
235 | Particles *particles = particles_owner.get_or_null(p_particles); |
236 | ERR_FAIL_NULL(particles); |
237 | |
238 | particles->use_local_coords = p_enable; |
239 | particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); |
240 | } |
241 | |
242 | void ParticlesStorage::particles_set_fixed_fps(RID p_particles, int p_fps) { |
243 | Particles *particles = particles_owner.get_or_null(p_particles); |
244 | ERR_FAIL_NULL(particles); |
245 | |
246 | particles->fixed_fps = p_fps; |
247 | |
248 | _particles_free_data(particles); |
249 | |
250 | particles->prev_ticks = 0; |
251 | particles->phase = 0; |
252 | particles->prev_phase = 0; |
253 | particles->clear = true; |
254 | |
255 | particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); |
256 | } |
257 | |
258 | void ParticlesStorage::particles_set_interpolate(RID p_particles, bool p_enable) { |
259 | Particles *particles = particles_owner.get_or_null(p_particles); |
260 | ERR_FAIL_NULL(particles); |
261 | |
262 | particles->interpolate = p_enable; |
263 | } |
264 | |
265 | void ParticlesStorage::particles_set_fractional_delta(RID p_particles, bool p_enable) { |
266 | Particles *particles = particles_owner.get_or_null(p_particles); |
267 | ERR_FAIL_NULL(particles); |
268 | |
269 | particles->fractional_delta = p_enable; |
270 | } |
271 | |
272 | void ParticlesStorage::particles_set_trails(RID p_particles, bool p_enable, double p_length) { |
273 | if (p_enable) { |
274 | WARN_PRINT_ONCE_ED("The GL Compatibility rendering backend does not support particle trails." ); |
275 | } |
276 | } |
277 | |
278 | void ParticlesStorage::particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) { |
279 | if (p_bind_poses.size() != 0) { |
280 | WARN_PRINT_ONCE_ED("The GL Compatibility rendering backend does not support particle trails." ); |
281 | } |
282 | } |
283 | |
284 | void ParticlesStorage::particles_set_collision_base_size(RID p_particles, real_t p_size) { |
285 | Particles *particles = particles_owner.get_or_null(p_particles); |
286 | ERR_FAIL_NULL(particles); |
287 | |
288 | particles->collision_base_size = p_size; |
289 | } |
290 | |
291 | void ParticlesStorage::particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) { |
292 | Particles *particles = particles_owner.get_or_null(p_particles); |
293 | ERR_FAIL_NULL(particles); |
294 | |
295 | particles->transform_align = p_transform_align; |
296 | } |
297 | |
298 | void ParticlesStorage::particles_set_process_material(RID p_particles, RID p_material) { |
299 | Particles *particles = particles_owner.get_or_null(p_particles); |
300 | ERR_FAIL_NULL(particles); |
301 | |
302 | particles->process_material = p_material; |
303 | particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); //the instance buffer may have changed |
304 | } |
305 | |
306 | RID ParticlesStorage::particles_get_process_material(RID p_particles) const { |
307 | Particles *particles = particles_owner.get_or_null(p_particles); |
308 | ERR_FAIL_NULL_V(particles, RID()); |
309 | |
310 | return particles->process_material; |
311 | } |
312 | |
313 | void ParticlesStorage::particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) { |
314 | Particles *particles = particles_owner.get_or_null(p_particles); |
315 | ERR_FAIL_NULL(particles); |
316 | |
317 | particles->draw_order = p_order; |
318 | } |
319 | |
320 | void ParticlesStorage::particles_set_draw_passes(RID p_particles, int p_passes) { |
321 | Particles *particles = particles_owner.get_or_null(p_particles); |
322 | ERR_FAIL_NULL(particles); |
323 | |
324 | particles->draw_passes.resize(p_passes); |
325 | } |
326 | |
327 | void ParticlesStorage::particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) { |
328 | Particles *particles = particles_owner.get_or_null(p_particles); |
329 | ERR_FAIL_NULL(particles); |
330 | ERR_FAIL_INDEX(p_pass, particles->draw_passes.size()); |
331 | particles->draw_passes.write[p_pass] = p_mesh; |
332 | particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_PARTICLES); |
333 | } |
334 | |
335 | void ParticlesStorage::particles_restart(RID p_particles) { |
336 | Particles *particles = particles_owner.get_or_null(p_particles); |
337 | ERR_FAIL_NULL(particles); |
338 | |
339 | particles->restart_request = true; |
340 | } |
341 | |
342 | void ParticlesStorage::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) { |
343 | if (p_subemitter_particles.is_valid()) { |
344 | WARN_PRINT_ONCE_ED("The GL Compatibility rendering backend does not support particle sub-emitters." ); |
345 | } |
346 | } |
347 | |
348 | void ParticlesStorage::particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { |
349 | WARN_PRINT_ONCE_ED("The GL Compatibility rendering backend does not support manually emitting particles." ); |
350 | } |
351 | |
352 | void ParticlesStorage::particles_request_process(RID p_particles) { |
353 | Particles *particles = particles_owner.get_or_null(p_particles); |
354 | ERR_FAIL_NULL(particles); |
355 | |
356 | if (!particles->dirty) { |
357 | particles->dirty = true; |
358 | particles->update_list = particle_update_list; |
359 | particle_update_list = particles; |
360 | } |
361 | } |
362 | |
363 | AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) { |
364 | if (RSG::threaded) { |
365 | WARN_PRINT_ONCE("Calling this function with threaded rendering enabled stalls the renderer, use with care." ); |
366 | } |
367 | |
368 | const Particles *particles = particles_owner.get_or_null(p_particles); |
369 | ERR_FAIL_NULL_V(particles, AABB()); |
370 | |
371 | int total_amount = particles->amount; |
372 | |
373 | // If available, read from the sort buffer which should be 2 frames out of date. |
374 | // This will help alleviate GPU stalls. |
375 | GLuint read_buffer = particles->sort_buffer_filled ? particles->sort_buffer : particles->back_instance_buffer; |
376 | |
377 | Vector<uint8_t> buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, read_buffer, total_amount * sizeof(ParticleInstanceData3D)); |
378 | ERR_FAIL_COND_V(buffer.size() != (int)(total_amount * sizeof(ParticleInstanceData3D)), AABB()); |
379 | |
380 | Transform3D inv = particles->emission_transform.affine_inverse(); |
381 | |
382 | AABB aabb; |
383 | if (buffer.size()) { |
384 | bool first = true; |
385 | |
386 | const uint8_t *data_ptr = (const uint8_t *)buffer.ptr(); |
387 | uint32_t particle_data_size = sizeof(ParticleInstanceData3D) + sizeof(float) * particles->userdata_count; |
388 | |
389 | for (int i = 0; i < total_amount; i++) { |
390 | const ParticleInstanceData3D &particle_data = *(const ParticleInstanceData3D *)&data_ptr[particle_data_size * i]; |
391 | // If scale is 0.0, we assume the particle is inactive. |
392 | if (particle_data.xform[0] > 0.0) { |
393 | Vector3 pos = Vector3(particle_data.xform[3], particle_data.xform[7], particle_data.xform[11]); |
394 | if (!particles->use_local_coords) { |
395 | pos = inv.xform(pos); |
396 | } |
397 | if (first) { |
398 | aabb.position = pos; |
399 | first = false; |
400 | } else { |
401 | aabb.expand_to(pos); |
402 | } |
403 | } |
404 | } |
405 | } |
406 | |
407 | float longest_axis_size = 0; |
408 | for (int i = 0; i < particles->draw_passes.size(); i++) { |
409 | if (particles->draw_passes[i].is_valid()) { |
410 | AABB maabb = MeshStorage::get_singleton()->mesh_get_aabb(particles->draw_passes[i], RID()); |
411 | longest_axis_size = MAX(maabb.get_longest_axis_size(), longest_axis_size); |
412 | } |
413 | } |
414 | |
415 | aabb.grow_by(longest_axis_size); |
416 | |
417 | return aabb; |
418 | } |
419 | |
420 | AABB ParticlesStorage::particles_get_aabb(RID p_particles) const { |
421 | const Particles *particles = particles_owner.get_or_null(p_particles); |
422 | ERR_FAIL_NULL_V(particles, AABB()); |
423 | |
424 | return particles->custom_aabb; |
425 | } |
426 | |
427 | void ParticlesStorage::particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) { |
428 | Particles *particles = particles_owner.get_or_null(p_particles); |
429 | ERR_FAIL_NULL(particles); |
430 | |
431 | particles->emission_transform = p_transform; |
432 | } |
433 | |
434 | int ParticlesStorage::particles_get_draw_passes(RID p_particles) const { |
435 | const Particles *particles = particles_owner.get_or_null(p_particles); |
436 | ERR_FAIL_NULL_V(particles, 0); |
437 | |
438 | return particles->draw_passes.size(); |
439 | } |
440 | |
441 | RID ParticlesStorage::particles_get_draw_pass_mesh(RID p_particles, int p_pass) const { |
442 | const Particles *particles = particles_owner.get_or_null(p_particles); |
443 | ERR_FAIL_NULL_V(particles, RID()); |
444 | ERR_FAIL_INDEX_V(p_pass, particles->draw_passes.size(), RID()); |
445 | |
446 | return particles->draw_passes[p_pass]; |
447 | } |
448 | |
449 | void ParticlesStorage::particles_add_collision(RID p_particles, RID p_particles_collision_instance) { |
450 | Particles *particles = particles_owner.get_or_null(p_particles); |
451 | ERR_FAIL_NULL(particles); |
452 | particles->collisions.insert(p_particles_collision_instance); |
453 | } |
454 | |
455 | void ParticlesStorage::particles_remove_collision(RID p_particles, RID p_particles_collision_instance) { |
456 | Particles *particles = particles_owner.get_or_null(p_particles); |
457 | ERR_FAIL_NULL(particles); |
458 | particles->collisions.erase(p_particles_collision_instance); |
459 | } |
460 | |
461 | void ParticlesStorage::particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, GLuint p_texture) { |
462 | Particles *particles = particles_owner.get_or_null(p_particles); |
463 | ERR_FAIL_NULL(particles); |
464 | particles->has_sdf_collision = p_enable; |
465 | particles->sdf_collision_transform = p_xform; |
466 | particles->sdf_collision_to_screen = p_to_screen; |
467 | particles->sdf_collision_texture = p_texture; |
468 | } |
469 | |
470 | // Does one step of processing particles by reading from back_process_buffer and writing to front_process_buffer. |
471 | void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta) { |
472 | GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); |
473 | GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); |
474 | |
475 | double new_phase = Math::fmod(p_particles->phase + (p_delta / p_particles->lifetime) * p_particles->speed_scale, 1.0); |
476 | |
477 | //update current frame |
478 | ParticlesFrameParams frame_params; |
479 | |
480 | if (p_particles->clear) { |
481 | p_particles->cycle_number = 0; |
482 | p_particles->random_seed = Math::rand(); |
483 | } else if (new_phase < p_particles->phase) { |
484 | if (p_particles->one_shot) { |
485 | p_particles->emitting = false; |
486 | } |
487 | p_particles->cycle_number++; |
488 | } |
489 | |
490 | frame_params.emitting = p_particles->emitting; |
491 | frame_params.system_phase = new_phase; |
492 | frame_params.prev_system_phase = p_particles->phase; |
493 | |
494 | p_particles->phase = new_phase; |
495 | |
496 | frame_params.time = RSG::rasterizer->get_total_time(); |
497 | frame_params.delta = p_delta * p_particles->speed_scale; |
498 | frame_params.random_seed = p_particles->random_seed; |
499 | frame_params.explosiveness = p_particles->explosiveness; |
500 | frame_params.randomness = p_particles->randomness; |
501 | |
502 | if (p_particles->use_local_coords) { |
503 | GLES3::MaterialStorage::store_transform(Transform3D(), frame_params.emission_transform); |
504 | } else { |
505 | GLES3::MaterialStorage::store_transform(p_particles->emission_transform, frame_params.emission_transform); |
506 | } |
507 | |
508 | frame_params.cycle = p_particles->cycle_number; |
509 | frame_params.frame = p_particles->frame_counter++; |
510 | frame_params.pad0 = 0; |
511 | frame_params.pad1 = 0; |
512 | frame_params.pad2 = 0; |
513 | |
514 | { //collision and attractors |
515 | |
516 | frame_params.collider_count = 0; |
517 | frame_params.attractor_count = 0; |
518 | frame_params.particle_size = p_particles->collision_base_size; |
519 | |
520 | GLuint collision_heightmap_texture = 0; |
521 | |
522 | Transform3D to_particles; |
523 | if (p_particles->use_local_coords) { |
524 | to_particles = p_particles->emission_transform.affine_inverse(); |
525 | } |
526 | |
527 | if (p_particles->has_sdf_collision && p_particles->sdf_collision_texture != 0) { |
528 | //2D collision |
529 | |
530 | Transform2D xform = p_particles->sdf_collision_transform; //will use dotproduct manually so invert beforehand |
531 | |
532 | if (!p_particles->use_local_coords) { |
533 | Transform2D emission; |
534 | emission.columns[0] = Vector2(p_particles->emission_transform.basis.get_column(0).x, p_particles->emission_transform.basis.get_column(0).y); |
535 | emission.columns[1] = Vector2(p_particles->emission_transform.basis.get_column(1).x, p_particles->emission_transform.basis.get_column(1).y); |
536 | emission.set_origin(Vector2(p_particles->emission_transform.origin.x, p_particles->emission_transform.origin.y)); |
537 | xform = xform * emission.affine_inverse(); |
538 | } |
539 | |
540 | Transform2D revert = xform.affine_inverse(); |
541 | frame_params.collider_count = 1; |
542 | frame_params.colliders[0].transform[0] = xform.columns[0][0]; |
543 | frame_params.colliders[0].transform[1] = xform.columns[0][1]; |
544 | frame_params.colliders[0].transform[2] = 0; |
545 | frame_params.colliders[0].transform[3] = xform.columns[2][0]; |
546 | |
547 | frame_params.colliders[0].transform[4] = xform.columns[1][0]; |
548 | frame_params.colliders[0].transform[5] = xform.columns[1][1]; |
549 | frame_params.colliders[0].transform[6] = 0; |
550 | frame_params.colliders[0].transform[7] = xform.columns[2][1]; |
551 | |
552 | frame_params.colliders[0].transform[8] = revert.columns[0][0]; |
553 | frame_params.colliders[0].transform[9] = revert.columns[0][1]; |
554 | frame_params.colliders[0].transform[10] = 0; |
555 | frame_params.colliders[0].transform[11] = revert.columns[2][0]; |
556 | |
557 | frame_params.colliders[0].transform[12] = revert.columns[1][0]; |
558 | frame_params.colliders[0].transform[13] = revert.columns[1][1]; |
559 | frame_params.colliders[0].transform[14] = 0; |
560 | frame_params.colliders[0].transform[15] = revert.columns[2][1]; |
561 | |
562 | frame_params.colliders[0].extents[0] = p_particles->sdf_collision_to_screen.size.x; |
563 | frame_params.colliders[0].extents[1] = p_particles->sdf_collision_to_screen.size.y; |
564 | frame_params.colliders[0].extents[2] = p_particles->sdf_collision_to_screen.position.x; |
565 | frame_params.colliders[0].scale = p_particles->sdf_collision_to_screen.position.y; |
566 | frame_params.colliders[0].type = ParticlesFrameParams::COLLISION_TYPE_2D_SDF; |
567 | |
568 | collision_heightmap_texture = p_particles->sdf_collision_texture; |
569 | } |
570 | |
571 | for (const RID &E : p_particles->collisions) { |
572 | ParticlesCollisionInstance *pci = particles_collision_instance_owner.get_or_null(E); |
573 | if (!pci || !pci->active) { |
574 | continue; |
575 | } |
576 | ParticlesCollision *pc = particles_collision_owner.get_or_null(pci->collision); |
577 | ERR_CONTINUE(!pc); |
578 | |
579 | Transform3D to_collider = pci->transform; |
580 | if (p_particles->use_local_coords) { |
581 | to_collider = to_particles * to_collider; |
582 | } |
583 | Vector3 scale = to_collider.basis.get_scale(); |
584 | to_collider.basis.orthonormalize(); |
585 | |
586 | if (pc->type <= RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT) { |
587 | //attractor |
588 | if (frame_params.attractor_count >= ParticlesFrameParams::MAX_ATTRACTORS) { |
589 | continue; |
590 | } |
591 | |
592 | ParticlesFrameParams::Attractor &attr = frame_params.attractors[frame_params.attractor_count]; |
593 | |
594 | GLES3::MaterialStorage::store_transform(to_collider, attr.transform); |
595 | attr.strength = pc->attractor_strength; |
596 | attr.attenuation = pc->attractor_attenuation; |
597 | attr.directionality = pc->attractor_directionality; |
598 | |
599 | switch (pc->type) { |
600 | case RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT: { |
601 | attr.type = ParticlesFrameParams::ATTRACTOR_TYPE_SPHERE; |
602 | float radius = pc->radius; |
603 | radius *= (scale.x + scale.y + scale.z) / 3.0; |
604 | attr.extents[0] = radius; |
605 | attr.extents[1] = radius; |
606 | attr.extents[2] = radius; |
607 | } break; |
608 | case RS::PARTICLES_COLLISION_TYPE_BOX_ATTRACT: { |
609 | attr.type = ParticlesFrameParams::ATTRACTOR_TYPE_BOX; |
610 | Vector3 extents = pc->extents * scale; |
611 | attr.extents[0] = extents.x; |
612 | attr.extents[1] = extents.y; |
613 | attr.extents[2] = extents.z; |
614 | } break; |
615 | case RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT: { |
616 | WARN_PRINT_ONCE_ED("Vector field particle attractors are not available in the GL Compatibility rendering backend." ); |
617 | } break; |
618 | default: { |
619 | } |
620 | } |
621 | |
622 | frame_params.attractor_count++; |
623 | } else { |
624 | //collider |
625 | if (frame_params.collider_count >= ParticlesFrameParams::MAX_COLLIDERS) { |
626 | continue; |
627 | } |
628 | |
629 | ParticlesFrameParams::Collider &col = frame_params.colliders[frame_params.collider_count]; |
630 | |
631 | GLES3::MaterialStorage::store_transform(to_collider, col.transform); |
632 | switch (pc->type) { |
633 | case RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE: { |
634 | col.type = ParticlesFrameParams::COLLISION_TYPE_SPHERE; |
635 | float radius = pc->radius; |
636 | radius *= (scale.x + scale.y + scale.z) / 3.0; |
637 | col.extents[0] = radius; |
638 | col.extents[1] = radius; |
639 | col.extents[2] = radius; |
640 | } break; |
641 | case RS::PARTICLES_COLLISION_TYPE_BOX_COLLIDE: { |
642 | col.type = ParticlesFrameParams::COLLISION_TYPE_BOX; |
643 | Vector3 extents = pc->extents * scale; |
644 | col.extents[0] = extents.x; |
645 | col.extents[1] = extents.y; |
646 | col.extents[2] = extents.z; |
647 | } break; |
648 | case RS::PARTICLES_COLLISION_TYPE_SDF_COLLIDE: { |
649 | WARN_PRINT_ONCE_ED("SDF Particle Colliders are not available in the GL Compatibility rendering backend." ); |
650 | } break; |
651 | case RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE: { |
652 | if (collision_heightmap_texture != 0) { //already taken |
653 | continue; |
654 | } |
655 | |
656 | col.type = ParticlesFrameParams::COLLISION_TYPE_HEIGHT_FIELD; |
657 | Vector3 extents = pc->extents * scale; |
658 | col.extents[0] = extents.x; |
659 | col.extents[1] = extents.y; |
660 | col.extents[2] = extents.z; |
661 | collision_heightmap_texture = pc->heightfield_texture; |
662 | } break; |
663 | default: { |
664 | } |
665 | } |
666 | |
667 | frame_params.collider_count++; |
668 | } |
669 | } |
670 | |
671 | // Bind heightmap or SDF texture. |
672 | GLuint heightmap = collision_heightmap_texture; |
673 | if (heightmap == 0) { |
674 | GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_BLACK)); |
675 | heightmap = tex->tex_id; |
676 | } |
677 | glActiveTexture(GL_TEXTURE0); |
678 | glBindTexture(GL_TEXTURE_2D, heightmap); |
679 | } |
680 | |
681 | if (p_particles->frame_params_ubo == 0) { |
682 | glGenBuffers(1, &p_particles->frame_params_ubo); |
683 | glBindBufferBase(GL_UNIFORM_BUFFER, PARTICLES_FRAME_UNIFORM_LOCATION, p_particles->frame_params_ubo); |
684 | GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_UNIFORM_BUFFER, p_particles->frame_params_ubo, sizeof(ParticlesFrameParams), &frame_params, GL_STREAM_DRAW, "Particle Frame UBO" ); |
685 | } else { |
686 | // Update per-frame UBO. |
687 | glBindBufferBase(GL_UNIFORM_BUFFER, PARTICLES_FRAME_UNIFORM_LOCATION, p_particles->frame_params_ubo); |
688 | glBufferData(GL_UNIFORM_BUFFER, sizeof(ParticlesFrameParams), &frame_params, GL_STREAM_DRAW); |
689 | } |
690 | |
691 | // Get shader and set shader uniforms; |
692 | ParticleProcessMaterialData *m = static_cast<ParticleProcessMaterialData *>(material_storage->material_get_data(p_particles->process_material, RS::SHADER_PARTICLES)); |
693 | if (!m) { |
694 | m = static_cast<ParticleProcessMaterialData *>(material_storage->material_get_data(particles_shader.default_material, RS::SHADER_PARTICLES)); |
695 | } |
696 | |
697 | ERR_FAIL_NULL(m); |
698 | |
699 | ParticlesShaderGLES3::ShaderVariant variant = ParticlesShaderGLES3::MODE_DEFAULT; |
700 | |
701 | uint32_t specialization = 0; |
702 | for (uint32_t i = 0; i < p_particles->userdata_count; i++) { |
703 | specialization |= (1 << i); |
704 | } |
705 | |
706 | if (p_particles->mode == RS::ParticlesMode::PARTICLES_MODE_3D) { |
707 | specialization |= ParticlesShaderGLES3::MODE_3D; |
708 | } |
709 | |
710 | RID version = particles_shader.default_shader_version; |
711 | if (m->shader_data->version.is_valid() && m->shader_data->valid) { |
712 | // Bind material uniform buffer and textures. |
713 | m->bind_uniforms(); |
714 | version = m->shader_data->version; |
715 | } |
716 | |
717 | bool success = material_storage->shaders.particles_process_shader.version_bind_shader(version, variant, specialization); |
718 | if (!success) { |
719 | return; |
720 | } |
721 | |
722 | material_storage->shaders.particles_process_shader.version_set_uniform(ParticlesShaderGLES3::LIFETIME, p_particles->lifetime, version, variant, specialization); |
723 | material_storage->shaders.particles_process_shader.version_set_uniform(ParticlesShaderGLES3::CLEAR, p_particles->clear, version, variant, specialization); |
724 | material_storage->shaders.particles_process_shader.version_set_uniform(ParticlesShaderGLES3::TOTAL_PARTICLES, uint32_t(p_particles->amount), version, variant, specialization); |
725 | material_storage->shaders.particles_process_shader.version_set_uniform(ParticlesShaderGLES3::USE_FRACTIONAL_DELTA, p_particles->fractional_delta, version, variant, specialization); |
726 | |
727 | p_particles->clear = false; |
728 | |
729 | p_particles->has_collision_cache = m->shader_data->uses_collision; |
730 | |
731 | glBindVertexArray(p_particles->back_vertex_array); |
732 | |
733 | glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, p_particles->front_process_buffer); |
734 | |
735 | glBeginTransformFeedback(GL_POINTS); |
736 | glDrawArrays(GL_POINTS, 0, p_particles->amount); |
737 | glEndTransformFeedback(); |
738 | |
739 | glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0); |
740 | glBindVertexArray(0); |
741 | |
742 | SWAP(p_particles->front_process_buffer, p_particles->back_process_buffer); |
743 | SWAP(p_particles->front_vertex_array, p_particles->back_vertex_array); |
744 | } |
745 | |
746 | void ParticlesStorage::particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) { |
747 | Particles *particles = particles_owner.get_or_null(p_particles); |
748 | ERR_FAIL_NULL(particles); |
749 | |
750 | if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) { |
751 | return; |
752 | } |
753 | |
754 | if (particles->front_process_buffer == 0) { |
755 | return; //particles have not processed yet |
756 | } |
757 | |
758 | Vector3 axis = -p_axis; // cameras look to z negative |
759 | |
760 | if (particles->use_local_coords) { |
761 | axis = particles->emission_transform.basis.xform_inv(axis).normalized(); |
762 | } |
763 | |
764 | // Sort will be done on CPU since we don't have compute shaders. |
765 | // If the sort_buffer has valid data |
766 | // Use a buffer that is 2 frames out of date to avoid stalls. |
767 | if (particles->draw_order == RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->sort_buffer_filled) { |
768 | glBindBuffer(GL_ARRAY_BUFFER, particles->sort_buffer); |
769 | |
770 | ParticleInstanceData3D *particle_array; |
771 | #ifndef __EMSCRIPTEN__ |
772 | particle_array = static_cast<ParticleInstanceData3D *>(glMapBufferRange(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(ParticleInstanceData3D), GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); |
773 | ERR_FAIL_NULL(particle_array); |
774 | #else |
775 | LocalVector<ParticleInstanceData3D> particle_vector; |
776 | particle_vector.resize(particles->amount); |
777 | particle_array = particle_vector.ptr(); |
778 | glGetBufferSubData(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(ParticleInstanceData3D), particle_array); |
779 | #endif |
780 | SortArray<ParticleInstanceData3D, ParticlesViewSort> sorter; |
781 | sorter.compare.z_dir = axis; |
782 | sorter.sort(particle_array, particles->amount); |
783 | |
784 | #ifndef __EMSCRIPTEN__ |
785 | glUnmapBuffer(GL_ARRAY_BUFFER); |
786 | #else |
787 | glBufferSubData(GL_ARRAY_BUFFER, 0, particles->amount * sizeof(ParticleInstanceData3D), particle_vector.ptr()); |
788 | #endif |
789 | } |
790 | |
791 | glEnable(GL_RASTERIZER_DISCARD); |
792 | _particles_update_instance_buffer(particles, axis, p_up_axis); |
793 | glDisable(GL_RASTERIZER_DISCARD); |
794 | } |
795 | |
796 | void ParticlesStorage::_particles_update_buffers(Particles *particles) { |
797 | GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); |
798 | uint32_t userdata_count = 0; |
799 | |
800 | if (particles->process_material.is_valid()) { |
801 | GLES3::ParticleProcessMaterialData *material_data = static_cast<GLES3::ParticleProcessMaterialData *>(material_storage->material_get_data(particles->process_material, RS::SHADER_PARTICLES)); |
802 | if (material_data && material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { |
803 | userdata_count = material_data->shader_data->userdata_count; |
804 | } |
805 | } |
806 | |
807 | if (userdata_count != particles->userdata_count) { |
808 | // Mismatch userdata, re-create buffers. |
809 | _particles_free_data(particles); |
810 | } |
811 | |
812 | if (particles->amount > 0 && particles->front_process_buffer == 0) { |
813 | int total_amount = particles->amount; |
814 | |
815 | particles->userdata_count = userdata_count; |
816 | |
817 | uint32_t xform_size = particles->mode == RS::PARTICLES_MODE_2D ? 2 : 3; |
818 | particles->instance_buffer_stride_cache = sizeof(float) * 4 * (xform_size + 1); |
819 | particles->instance_buffer_size_cache = particles->instance_buffer_stride_cache * total_amount; |
820 | particles->num_attrib_arrays_cache = 5 + userdata_count + (xform_size - 2); |
821 | particles->process_buffer_stride_cache = sizeof(float) * 4 * particles->num_attrib_arrays_cache; |
822 | |
823 | PackedByteArray data; |
824 | data.resize_zeroed(particles->process_buffer_stride_cache * total_amount); |
825 | |
826 | PackedByteArray instance_data; |
827 | instance_data.resize_zeroed(particles->instance_buffer_size_cache); |
828 | |
829 | { |
830 | glGenVertexArrays(1, &particles->front_vertex_array); |
831 | glBindVertexArray(particles->front_vertex_array); |
832 | glGenBuffers(1, &particles->front_process_buffer); |
833 | glGenBuffers(1, &particles->front_instance_buffer); |
834 | |
835 | glBindBuffer(GL_ARRAY_BUFFER, particles->front_process_buffer); |
836 | GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->front_process_buffer, particles->process_buffer_stride_cache * total_amount, data.ptr(), GL_DYNAMIC_COPY, "Particles front process buffer" ); |
837 | |
838 | for (uint32_t j = 0; j < particles->num_attrib_arrays_cache; j++) { |
839 | glEnableVertexAttribArray(j); |
840 | glVertexAttribPointer(j, 4, GL_FLOAT, GL_FALSE, particles->process_buffer_stride_cache, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * j)); |
841 | } |
842 | glBindVertexArray(0); |
843 | |
844 | glBindBuffer(GL_ARRAY_BUFFER, particles->front_instance_buffer); |
845 | GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->front_instance_buffer, particles->instance_buffer_size_cache, instance_data.ptr(), GL_DYNAMIC_COPY, "Particles front instance buffer" ); |
846 | } |
847 | |
848 | { |
849 | glGenVertexArrays(1, &particles->back_vertex_array); |
850 | glBindVertexArray(particles->back_vertex_array); |
851 | glGenBuffers(1, &particles->back_process_buffer); |
852 | glGenBuffers(1, &particles->back_instance_buffer); |
853 | |
854 | glBindBuffer(GL_ARRAY_BUFFER, particles->back_process_buffer); |
855 | GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->back_process_buffer, particles->process_buffer_stride_cache * total_amount, data.ptr(), GL_DYNAMIC_COPY, "Particles back process buffer" ); |
856 | |
857 | for (uint32_t j = 0; j < particles->num_attrib_arrays_cache; j++) { |
858 | glEnableVertexAttribArray(j); |
859 | glVertexAttribPointer(j, 4, GL_FLOAT, GL_FALSE, particles->process_buffer_stride_cache, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * j)); |
860 | } |
861 | glBindVertexArray(0); |
862 | |
863 | glBindBuffer(GL_ARRAY_BUFFER, particles->back_instance_buffer); |
864 | GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->back_instance_buffer, particles->instance_buffer_size_cache, instance_data.ptr(), GL_DYNAMIC_COPY, "Particles back instance buffer" ); |
865 | } |
866 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
867 | } |
868 | } |
869 | |
870 | void ParticlesStorage::_particles_allocate_history_buffers(Particles *particles) { |
871 | if (particles->sort_buffer == 0) { |
872 | glGenBuffers(1, &particles->last_frame_buffer); |
873 | glBindBuffer(GL_ARRAY_BUFFER, particles->last_frame_buffer); |
874 | GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->last_frame_buffer, particles->instance_buffer_size_cache, nullptr, GL_DYNAMIC_READ, "Particles last frame buffer" ); |
875 | |
876 | glGenBuffers(1, &particles->sort_buffer); |
877 | glBindBuffer(GL_ARRAY_BUFFER, particles->sort_buffer); |
878 | GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, particles->sort_buffer, particles->instance_buffer_size_cache, nullptr, GL_DYNAMIC_READ, "Particles sort buffer" ); |
879 | |
880 | particles->sort_buffer_filled = false; |
881 | particles->last_frame_buffer_filled = false; |
882 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
883 | } |
884 | } |
885 | void ParticlesStorage::_particles_update_instance_buffer(Particles *particles, const Vector3 &p_axis, const Vector3 &p_up_axis) { |
886 | ParticlesCopyShaderGLES3::ShaderVariant variant = ParticlesCopyShaderGLES3::MODE_DEFAULT; |
887 | |
888 | uint64_t specialization = 0; |
889 | if (particles->mode == RS::ParticlesMode::PARTICLES_MODE_3D) { |
890 | specialization |= ParticlesCopyShaderGLES3::MODE_3D; |
891 | } |
892 | |
893 | bool success = particles_shader.copy_shader.version_bind_shader(particles_shader.copy_shader_version, variant, specialization); |
894 | if (!success) { |
895 | return; |
896 | } |
897 | |
898 | // Affect 2D only. |
899 | if (particles->use_local_coords) { |
900 | // In local mode, particle positions are calculated locally (relative to the node position) |
901 | // and they're also drawn locally. |
902 | // It works as expected, so we just pass an identity transform. |
903 | particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::INV_EMISSION_TRANSFORM, Transform3D(), particles_shader.copy_shader_version, variant, specialization); |
904 | } else { |
905 | // In global mode, particle positions are calculated globally (relative to the canvas origin) |
906 | // but they're drawn locally. |
907 | // So, we need to pass the inverse of the emission transform to bring the |
908 | // particles to local coordinates before drawing. |
909 | Transform3D inv = particles->emission_transform.affine_inverse(); |
910 | particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::INV_EMISSION_TRANSFORM, inv, particles_shader.copy_shader_version, variant, specialization); |
911 | } |
912 | |
913 | particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::FRAME_REMAINDER, particles->interpolate ? particles->frame_remainder : 0.0, particles_shader.copy_shader_version, variant, specialization); |
914 | particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::ALIGN_MODE, uint32_t(particles->transform_align), particles_shader.copy_shader_version, variant, specialization); |
915 | particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::ALIGN_UP, p_up_axis, particles_shader.copy_shader_version, variant, specialization); |
916 | particles_shader.copy_shader.version_set_uniform(ParticlesCopyShaderGLES3::SORT_DIRECTION, p_axis, particles_shader.copy_shader_version, variant, specialization); |
917 | |
918 | glBindVertexArray(particles->back_vertex_array); |
919 | glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, particles->front_instance_buffer, 0, particles->instance_buffer_size_cache); |
920 | glBeginTransformFeedback(GL_POINTS); |
921 | |
922 | if (particles->draw_order == RS::PARTICLES_DRAW_ORDER_LIFETIME) { |
923 | uint32_t lifetime_split = (MIN(int(particles->amount * particles->phase), particles->amount - 1) + 1) % particles->amount; |
924 | uint32_t stride = particles->process_buffer_stride_cache; |
925 | |
926 | glBindBuffer(GL_ARRAY_BUFFER, particles->back_process_buffer); |
927 | |
928 | // Offset VBO so you render starting at the newest particle. |
929 | if (particles->amount - lifetime_split > 0) { |
930 | glEnableVertexAttribArray(0); // Color. |
931 | glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 0)); |
932 | glEnableVertexAttribArray(1); // .xyz: velocity. .z: flags. |
933 | glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 1)); |
934 | glEnableVertexAttribArray(2); // Custom. |
935 | glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 2)); |
936 | glEnableVertexAttribArray(3); // Xform1. |
937 | glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 3)); |
938 | glEnableVertexAttribArray(4); // Xform2. |
939 | glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 4)); |
940 | if (particles->mode == RS::PARTICLES_MODE_3D) { |
941 | glEnableVertexAttribArray(5); // Xform3. |
942 | glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(stride * lifetime_split + sizeof(float) * 4 * 5)); |
943 | } |
944 | |
945 | uint32_t to_draw = particles->amount - lifetime_split; |
946 | glDrawArrays(GL_POINTS, 0, to_draw); |
947 | } |
948 | |
949 | // Then render from index 0 up intil the newest particle. |
950 | if (lifetime_split > 0) { |
951 | glEndTransformFeedback(); |
952 | // Now output to the second portion of the instance buffer. |
953 | glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, particles->front_instance_buffer, particles->instance_buffer_stride_cache * (particles->amount - lifetime_split), particles->instance_buffer_stride_cache * (lifetime_split)); |
954 | glBeginTransformFeedback(GL_POINTS); |
955 | // Reset back to normal. |
956 | for (uint32_t j = 0; j < particles->num_attrib_arrays_cache; j++) { |
957 | glEnableVertexAttribArray(j); |
958 | glVertexAttribPointer(j, 4, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(sizeof(float) * 4 * j)); |
959 | } |
960 | |
961 | glDrawArrays(GL_POINTS, 0, lifetime_split); |
962 | } |
963 | } else { |
964 | glDrawArrays(GL_POINTS, 0, particles->amount); |
965 | } |
966 | |
967 | glEndTransformFeedback(); |
968 | glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0, 0, 0); |
969 | glBindVertexArray(0); |
970 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
971 | } |
972 | |
973 | void ParticlesStorage::update_particles() { |
974 | glEnable(GL_RASTERIZER_DISCARD); |
975 | |
976 | GLuint global_buffer = GLES3::MaterialStorage::get_singleton()->global_shader_parameters_get_uniform_buffer(); |
977 | |
978 | glBindBufferBase(GL_UNIFORM_BUFFER, PARTICLES_GLOBALS_UNIFORM_LOCATION, global_buffer); |
979 | glBindBuffer(GL_UNIFORM_BUFFER, 0); |
980 | |
981 | while (particle_update_list) { |
982 | // Use transform feedback to process particles. |
983 | |
984 | Particles *particles = particle_update_list; |
985 | |
986 | particle_update_list = particles->update_list; |
987 | particles->update_list = nullptr; |
988 | particles->dirty = false; |
989 | |
990 | _particles_update_buffers(particles); |
991 | |
992 | if (particles->restart_request) { |
993 | particles->prev_ticks = 0; |
994 | particles->phase = 0; |
995 | particles->prev_phase = 0; |
996 | particles->clear = true; |
997 | particles->restart_request = false; |
998 | } |
999 | |
1000 | if (particles->inactive && !particles->emitting) { |
1001 | //go next |
1002 | continue; |
1003 | } |
1004 | |
1005 | if (particles->emitting) { |
1006 | if (particles->inactive) { |
1007 | //restart system from scratch |
1008 | particles->prev_ticks = 0; |
1009 | particles->phase = 0; |
1010 | particles->prev_phase = 0; |
1011 | particles->clear = true; |
1012 | } |
1013 | particles->inactive = false; |
1014 | particles->inactive_time = 0; |
1015 | } else { |
1016 | particles->inactive_time += particles->speed_scale * RSG::rasterizer->get_frame_delta_time(); |
1017 | if (particles->inactive_time > particles->lifetime * 1.2) { |
1018 | particles->inactive = true; |
1019 | continue; |
1020 | } |
1021 | } |
1022 | |
1023 | // Copy the instance buffer that was last used into the last_frame buffer. |
1024 | // sort_buffer should now be 2 frames out of date. |
1025 | if (particles->draw_order == RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME) { |
1026 | _particles_allocate_history_buffers(particles); |
1027 | SWAP(particles->last_frame_buffer, particles->sort_buffer); |
1028 | |
1029 | glBindBuffer(GL_COPY_READ_BUFFER, particles->back_instance_buffer); |
1030 | glBindBuffer(GL_COPY_WRITE_BUFFER, particles->last_frame_buffer); |
1031 | glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, particles->instance_buffer_size_cache); |
1032 | |
1033 | // Last frame's last_frame turned into this frame's sort buffer. |
1034 | particles->sort_buffer_filled = particles->last_frame_buffer_filled; |
1035 | particles->sort_buffer_phase = particles->last_frame_phase; |
1036 | particles->last_frame_buffer_filled = true; |
1037 | particles->last_frame_phase = particles->phase; |
1038 | glBindBuffer(GL_COPY_READ_BUFFER, 0); |
1039 | glBindBuffer(GL_COPY_WRITE_BUFFER, 0); |
1040 | } |
1041 | |
1042 | int fixed_fps = 0; |
1043 | if (particles->fixed_fps > 0) { |
1044 | fixed_fps = particles->fixed_fps; |
1045 | } |
1046 | |
1047 | bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0; |
1048 | |
1049 | if (particles->clear && particles->pre_process_time > 0.0) { |
1050 | double frame_time; |
1051 | if (fixed_fps > 0) { |
1052 | frame_time = 1.0 / fixed_fps; |
1053 | } else { |
1054 | frame_time = 1.0 / 30.0; |
1055 | } |
1056 | |
1057 | double todo = particles->pre_process_time; |
1058 | |
1059 | while (todo >= 0) { |
1060 | _particles_process(particles, frame_time); |
1061 | todo -= frame_time; |
1062 | } |
1063 | } |
1064 | |
1065 | if (fixed_fps > 0) { |
1066 | double frame_time; |
1067 | double decr; |
1068 | if (zero_time_scale) { |
1069 | frame_time = 0.0; |
1070 | decr = 1.0 / fixed_fps; |
1071 | } else { |
1072 | frame_time = 1.0 / fixed_fps; |
1073 | decr = frame_time; |
1074 | } |
1075 | double delta = RSG::rasterizer->get_frame_delta_time(); |
1076 | if (delta > 0.1) { //avoid recursive stalls if fps goes below 10 |
1077 | delta = 0.1; |
1078 | } else if (delta <= 0.0) { //unlikely but.. |
1079 | delta = 0.001; |
1080 | } |
1081 | double todo = particles->frame_remainder + delta; |
1082 | |
1083 | while (todo >= frame_time) { |
1084 | _particles_process(particles, frame_time); |
1085 | todo -= decr; |
1086 | } |
1087 | |
1088 | particles->frame_remainder = todo; |
1089 | |
1090 | } else { |
1091 | if (zero_time_scale) { |
1092 | _particles_process(particles, 0.0); |
1093 | } else { |
1094 | _particles_process(particles, RSG::rasterizer->get_frame_delta_time()); |
1095 | } |
1096 | } |
1097 | |
1098 | // Copy particles to instance buffer and pack Color/Custom. |
1099 | // We don't have camera information here, so don't copy here if we need camera information for view depth or align mode. |
1100 | if (particles->draw_order != RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RS::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) { |
1101 | _particles_update_instance_buffer(particles, Vector3(0.0, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)); |
1102 | |
1103 | if (particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME && particles->sort_buffer_filled) { |
1104 | if (particles->mode == RS::ParticlesMode::PARTICLES_MODE_2D) { |
1105 | _particles_reverse_lifetime_sort<ParticleInstanceData2D>(particles); |
1106 | } else { |
1107 | _particles_reverse_lifetime_sort<ParticleInstanceData3D>(particles); |
1108 | } |
1109 | } |
1110 | } |
1111 | |
1112 | SWAP(particles->front_instance_buffer, particles->back_instance_buffer); |
1113 | |
1114 | // At the end of update, the back_buffer contains the most up-to-date-information to read from. |
1115 | |
1116 | particles->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); |
1117 | } |
1118 | |
1119 | glDisable(GL_RASTERIZER_DISCARD); |
1120 | } |
1121 | |
1122 | template <typename ParticleInstanceData> |
1123 | void ParticlesStorage::_particles_reverse_lifetime_sort(Particles *particles) { |
1124 | glBindBuffer(GL_ARRAY_BUFFER, particles->sort_buffer); |
1125 | |
1126 | ParticleInstanceData *particle_array; |
1127 | uint32_t buffer_size = particles->amount * sizeof(ParticleInstanceData); |
1128 | #ifndef __EMSCRIPTEN__ |
1129 | particle_array = static_cast<ParticleInstanceData *>(glMapBufferRange(GL_ARRAY_BUFFER, 0, buffer_size, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)); |
1130 | |
1131 | ERR_FAIL_NULL(particle_array); |
1132 | #else |
1133 | LocalVector<ParticleInstanceData> particle_vector; |
1134 | particle_vector.resize(particles->amount); |
1135 | particle_array = particle_vector.ptr(); |
1136 | glGetBufferSubData(GL_ARRAY_BUFFER, 0, buffer_size, particle_array); |
1137 | #endif |
1138 | |
1139 | uint32_t lifetime_split = (MIN(int(particles->amount * particles->sort_buffer_phase), particles->amount - 1) + 1) % particles->amount; |
1140 | for (uint32_t i = 0; i < lifetime_split / 2; i++) { |
1141 | SWAP(particle_array[i], particle_array[lifetime_split - i - 1]); |
1142 | } |
1143 | |
1144 | for (uint32_t i = 0; i < (particles->amount - lifetime_split) / 2; i++) { |
1145 | SWAP(particle_array[lifetime_split + i], particle_array[particles->amount - 1 - i]); |
1146 | } |
1147 | |
1148 | #ifndef __EMSCRIPTEN__ |
1149 | glUnmapBuffer(GL_ARRAY_BUFFER); |
1150 | #else |
1151 | glBufferSubData(GL_ARRAY_BUFFER, 0, buffer_size, particle_vector.ptr()); |
1152 | #endif |
1153 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
1154 | } |
1155 | |
1156 | Dependency *ParticlesStorage::particles_get_dependency(RID p_particles) const { |
1157 | Particles *particles = particles_owner.get_or_null(p_particles); |
1158 | ERR_FAIL_NULL_V(particles, nullptr); |
1159 | |
1160 | return &particles->dependency; |
1161 | } |
1162 | |
1163 | bool ParticlesStorage::particles_is_inactive(RID p_particles) const { |
1164 | ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer." ); |
1165 | const Particles *particles = particles_owner.get_or_null(p_particles); |
1166 | ERR_FAIL_NULL_V(particles, false); |
1167 | return !particles->emitting && particles->inactive; |
1168 | } |
1169 | |
1170 | /* PARTICLES COLLISION API */ |
1171 | |
1172 | RID ParticlesStorage::particles_collision_allocate() { |
1173 | return particles_collision_owner.allocate_rid(); |
1174 | } |
1175 | void ParticlesStorage::particles_collision_initialize(RID p_rid) { |
1176 | particles_collision_owner.initialize_rid(p_rid, ParticlesCollision()); |
1177 | } |
1178 | |
1179 | void ParticlesStorage::particles_collision_free(RID p_rid) { |
1180 | ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_rid); |
1181 | |
1182 | if (particles_collision->heightfield_texture != 0) { |
1183 | GLES3::Utilities::get_singleton()->texture_free_data(particles_collision->heightfield_texture); |
1184 | particles_collision->heightfield_texture = 0; |
1185 | glDeleteFramebuffers(1, &particles_collision->heightfield_fb); |
1186 | particles_collision->heightfield_fb = 0; |
1187 | } |
1188 | particles_collision->dependency.deleted_notify(p_rid); |
1189 | particles_collision_owner.free(p_rid); |
1190 | } |
1191 | |
1192 | GLuint ParticlesStorage::particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const { |
1193 | ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); |
1194 | ERR_FAIL_NULL_V(particles_collision, 0); |
1195 | ERR_FAIL_COND_V(particles_collision->type != RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE, 0); |
1196 | |
1197 | if (particles_collision->heightfield_texture == 0) { |
1198 | //create |
1199 | const int resolutions[RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_MAX] = { 256, 512, 1024, 2048, 4096, 8192 }; |
1200 | Size2i size; |
1201 | if (particles_collision->extents.x > particles_collision->extents.z) { |
1202 | size.x = resolutions[particles_collision->heightfield_resolution]; |
1203 | size.y = int32_t(particles_collision->extents.z / particles_collision->extents.x * size.x); |
1204 | } else { |
1205 | size.y = resolutions[particles_collision->heightfield_resolution]; |
1206 | size.x = int32_t(particles_collision->extents.x / particles_collision->extents.z * size.y); |
1207 | } |
1208 | |
1209 | glGenTextures(1, &particles_collision->heightfield_texture); |
1210 | glActiveTexture(GL_TEXTURE0); |
1211 | glBindTexture(GL_TEXTURE_2D, particles_collision->heightfield_texture); |
1212 | glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, size.x, size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); |
1213 | |
1214 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
1215 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
1216 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); |
1217 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); |
1218 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
1219 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
1220 | |
1221 | glGenFramebuffers(1, &particles_collision->heightfield_fb); |
1222 | glBindFramebuffer(GL_FRAMEBUFFER, particles_collision->heightfield_fb); |
1223 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, particles_collision->heightfield_texture, 0); |
1224 | #ifdef DEBUG_ENABLED |
1225 | GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
1226 | if (status != GL_FRAMEBUFFER_COMPLETE) { |
1227 | WARN_PRINT("Could create heightmap texture status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status)); |
1228 | } |
1229 | #endif |
1230 | GLES3::Utilities::get_singleton()->texture_allocated_data(particles_collision->heightfield_texture, size.x * size.y * 4, "Particles collision heightfield texture" ); |
1231 | |
1232 | particles_collision->heightfield_fb_size = size; |
1233 | |
1234 | glBindTexture(GL_TEXTURE_2D, 0); |
1235 | glBindFramebuffer(GL_FRAMEBUFFER, 0); |
1236 | } |
1237 | |
1238 | return particles_collision->heightfield_fb; |
1239 | } |
1240 | |
1241 | void ParticlesStorage::particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) { |
1242 | ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); |
1243 | ERR_FAIL_NULL(particles_collision); |
1244 | |
1245 | if (p_type == particles_collision->type) { |
1246 | return; |
1247 | } |
1248 | |
1249 | if (particles_collision->heightfield_texture != 0) { |
1250 | GLES3::Utilities::get_singleton()->texture_free_data(particles_collision->heightfield_texture); |
1251 | particles_collision->heightfield_texture = 0; |
1252 | glDeleteFramebuffers(1, &particles_collision->heightfield_fb); |
1253 | particles_collision->heightfield_fb = 0; |
1254 | } |
1255 | |
1256 | particles_collision->type = p_type; |
1257 | particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); |
1258 | } |
1259 | |
1260 | void ParticlesStorage::particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) { |
1261 | ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); |
1262 | ERR_FAIL_NULL(particles_collision); |
1263 | particles_collision->cull_mask = p_cull_mask; |
1264 | } |
1265 | |
1266 | void ParticlesStorage::particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) { |
1267 | ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); |
1268 | ERR_FAIL_NULL(particles_collision); |
1269 | |
1270 | particles_collision->radius = p_radius; |
1271 | particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); |
1272 | } |
1273 | |
1274 | void ParticlesStorage::particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) { |
1275 | ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); |
1276 | ERR_FAIL_NULL(particles_collision); |
1277 | |
1278 | particles_collision->extents = p_extents; |
1279 | particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); |
1280 | } |
1281 | |
1282 | void ParticlesStorage::particles_collision_set_attractor_strength(RID p_particles_collision, real_t p_strength) { |
1283 | ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); |
1284 | ERR_FAIL_NULL(particles_collision); |
1285 | |
1286 | particles_collision->attractor_strength = p_strength; |
1287 | } |
1288 | |
1289 | void ParticlesStorage::particles_collision_set_attractor_directionality(RID p_particles_collision, real_t p_directionality) { |
1290 | ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); |
1291 | ERR_FAIL_NULL(particles_collision); |
1292 | |
1293 | particles_collision->attractor_directionality = p_directionality; |
1294 | } |
1295 | |
1296 | void ParticlesStorage::particles_collision_set_attractor_attenuation(RID p_particles_collision, real_t p_curve) { |
1297 | ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); |
1298 | ERR_FAIL_NULL(particles_collision); |
1299 | |
1300 | particles_collision->attractor_attenuation = p_curve; |
1301 | } |
1302 | |
1303 | void ParticlesStorage::particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) { |
1304 | WARN_PRINT_ONCE_ED("The GL Compatibility rendering backend does not support SDF collisions in 3D particle shaders" ); |
1305 | } |
1306 | |
1307 | void ParticlesStorage::particles_collision_height_field_update(RID p_particles_collision) { |
1308 | ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); |
1309 | ERR_FAIL_NULL(particles_collision); |
1310 | particles_collision->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); |
1311 | } |
1312 | |
1313 | void ParticlesStorage::particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) { |
1314 | ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); |
1315 | ERR_FAIL_NULL(particles_collision); |
1316 | ERR_FAIL_INDEX(p_resolution, RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_MAX); |
1317 | |
1318 | if (particles_collision->heightfield_resolution == p_resolution) { |
1319 | return; |
1320 | } |
1321 | |
1322 | particles_collision->heightfield_resolution = p_resolution; |
1323 | |
1324 | if (particles_collision->heightfield_texture != 0) { |
1325 | GLES3::Utilities::get_singleton()->texture_free_data(particles_collision->heightfield_texture); |
1326 | particles_collision->heightfield_texture = 0; |
1327 | glDeleteFramebuffers(1, &particles_collision->heightfield_fb); |
1328 | particles_collision->heightfield_fb = 0; |
1329 | } |
1330 | } |
1331 | |
1332 | AABB ParticlesStorage::particles_collision_get_aabb(RID p_particles_collision) const { |
1333 | ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); |
1334 | ERR_FAIL_NULL_V(particles_collision, AABB()); |
1335 | |
1336 | switch (particles_collision->type) { |
1337 | case RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT: |
1338 | case RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE: { |
1339 | AABB aabb; |
1340 | aabb.position = -Vector3(1, 1, 1) * particles_collision->radius; |
1341 | aabb.size = Vector3(2, 2, 2) * particles_collision->radius; |
1342 | return aabb; |
1343 | } |
1344 | default: { |
1345 | AABB aabb; |
1346 | aabb.position = -particles_collision->extents; |
1347 | aabb.size = particles_collision->extents * 2; |
1348 | return aabb; |
1349 | } |
1350 | } |
1351 | } |
1352 | |
1353 | Vector3 ParticlesStorage::particles_collision_get_extents(RID p_particles_collision) const { |
1354 | const ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); |
1355 | ERR_FAIL_NULL_V(particles_collision, Vector3()); |
1356 | return particles_collision->extents; |
1357 | } |
1358 | |
1359 | bool ParticlesStorage::particles_collision_is_heightfield(RID p_particles_collision) const { |
1360 | const ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); |
1361 | ERR_FAIL_NULL_V(particles_collision, false); |
1362 | return particles_collision->type == RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE; |
1363 | } |
1364 | |
1365 | Dependency *ParticlesStorage::particles_collision_get_dependency(RID p_particles_collision) const { |
1366 | ParticlesCollision *pc = particles_collision_owner.get_or_null(p_particles_collision); |
1367 | ERR_FAIL_NULL_V(pc, nullptr); |
1368 | |
1369 | return &pc->dependency; |
1370 | } |
1371 | |
1372 | /* Particles collision instance */ |
1373 | |
1374 | RID ParticlesStorage::particles_collision_instance_create(RID p_collision) { |
1375 | ParticlesCollisionInstance pci; |
1376 | pci.collision = p_collision; |
1377 | return particles_collision_instance_owner.make_rid(pci); |
1378 | } |
1379 | |
1380 | void ParticlesStorage::particles_collision_instance_free(RID p_rid) { |
1381 | particles_collision_instance_owner.free(p_rid); |
1382 | } |
1383 | |
1384 | void ParticlesStorage::particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) { |
1385 | ParticlesCollisionInstance *pci = particles_collision_instance_owner.get_or_null(p_collision_instance); |
1386 | ERR_FAIL_NULL(pci); |
1387 | pci->transform = p_transform; |
1388 | } |
1389 | |
1390 | void ParticlesStorage::particles_collision_instance_set_active(RID p_collision_instance, bool p_active) { |
1391 | ParticlesCollisionInstance *pci = particles_collision_instance_owner.get_or_null(p_collision_instance); |
1392 | ERR_FAIL_NULL(pci); |
1393 | pci->active = p_active; |
1394 | } |
1395 | |
1396 | #endif // GLES3_ENABLED |
1397 | |