1 | /**************************************************************************/ |
2 | /* gpu_particles_collision_3d.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 | #include "gpu_particles_collision_3d.h" |
32 | |
33 | #include "core/object/worker_thread_pool.h" |
34 | #include "mesh_instance_3d.h" |
35 | #include "scene/3d/camera_3d.h" |
36 | #include "scene/main/viewport.h" |
37 | |
38 | void GPUParticlesCollision3D::set_cull_mask(uint32_t p_cull_mask) { |
39 | cull_mask = p_cull_mask; |
40 | RS::get_singleton()->particles_collision_set_cull_mask(collision, p_cull_mask); |
41 | } |
42 | |
43 | uint32_t GPUParticlesCollision3D::get_cull_mask() const { |
44 | return cull_mask; |
45 | } |
46 | |
47 | void GPUParticlesCollision3D::_bind_methods() { |
48 | ClassDB::bind_method(D_METHOD("set_cull_mask" , "mask" ), &GPUParticlesCollision3D::set_cull_mask); |
49 | ClassDB::bind_method(D_METHOD("get_cull_mask" ), &GPUParticlesCollision3D::get_cull_mask); |
50 | |
51 | ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask" , PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask" , "get_cull_mask" ); |
52 | } |
53 | |
54 | GPUParticlesCollision3D::GPUParticlesCollision3D(RS::ParticlesCollisionType p_type) { |
55 | collision = RS::get_singleton()->particles_collision_create(); |
56 | RS::get_singleton()->particles_collision_set_collision_type(collision, p_type); |
57 | set_base(collision); |
58 | } |
59 | |
60 | GPUParticlesCollision3D::~GPUParticlesCollision3D() { |
61 | ERR_FAIL_NULL(RenderingServer::get_singleton()); |
62 | RS::get_singleton()->free(collision); |
63 | } |
64 | |
65 | ///////////////////////////////// |
66 | |
67 | void GPUParticlesCollisionSphere3D::_bind_methods() { |
68 | ClassDB::bind_method(D_METHOD("set_radius" , "radius" ), &GPUParticlesCollisionSphere3D::set_radius); |
69 | ClassDB::bind_method(D_METHOD("get_radius" ), &GPUParticlesCollisionSphere3D::get_radius); |
70 | |
71 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius" , PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m" ), "set_radius" , "get_radius" ); |
72 | } |
73 | |
74 | void GPUParticlesCollisionSphere3D::set_radius(real_t p_radius) { |
75 | radius = p_radius; |
76 | RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius); |
77 | update_gizmos(); |
78 | } |
79 | |
80 | real_t GPUParticlesCollisionSphere3D::get_radius() const { |
81 | return radius; |
82 | } |
83 | |
84 | AABB GPUParticlesCollisionSphere3D::get_aabb() const { |
85 | return AABB(Vector3(-radius, -radius, -radius), Vector3(radius * 2, radius * 2, radius * 2)); |
86 | } |
87 | |
88 | GPUParticlesCollisionSphere3D::GPUParticlesCollisionSphere3D() : |
89 | GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE) { |
90 | } |
91 | |
92 | GPUParticlesCollisionSphere3D::~GPUParticlesCollisionSphere3D() { |
93 | } |
94 | |
95 | /////////////////////////// |
96 | |
97 | void GPUParticlesCollisionBox3D::_bind_methods() { |
98 | ClassDB::bind_method(D_METHOD("set_size" , "size" ), &GPUParticlesCollisionBox3D::set_size); |
99 | ClassDB::bind_method(D_METHOD("get_size" ), &GPUParticlesCollisionBox3D::get_size); |
100 | |
101 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size" , PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m" ), "set_size" , "get_size" ); |
102 | } |
103 | |
104 | #ifndef DISABLE_DEPRECATED |
105 | bool GPUParticlesCollisionBox3D::_set(const StringName &p_name, const Variant &p_value) { |
106 | if (p_name == "extents" ) { // Compatibility with Godot 3.x. |
107 | set_size((Vector3)p_value * 2); |
108 | return true; |
109 | } |
110 | return false; |
111 | } |
112 | |
113 | bool GPUParticlesCollisionBox3D::_get(const StringName &p_name, Variant &r_property) const { |
114 | if (p_name == "extents" ) { // Compatibility with Godot 3.x. |
115 | r_property = size / 2; |
116 | return true; |
117 | } |
118 | return false; |
119 | } |
120 | #endif // DISABLE_DEPRECATED |
121 | |
122 | void GPUParticlesCollisionBox3D::set_size(const Vector3 &p_size) { |
123 | size = p_size; |
124 | RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), size / 2); |
125 | update_gizmos(); |
126 | } |
127 | |
128 | Vector3 GPUParticlesCollisionBox3D::get_size() const { |
129 | return size; |
130 | } |
131 | |
132 | AABB GPUParticlesCollisionBox3D::get_aabb() const { |
133 | return AABB(-size / 2, size); |
134 | } |
135 | |
136 | GPUParticlesCollisionBox3D::GPUParticlesCollisionBox3D() : |
137 | GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_BOX_COLLIDE) { |
138 | } |
139 | |
140 | GPUParticlesCollisionBox3D::~GPUParticlesCollisionBox3D() { |
141 | } |
142 | |
143 | /////////////////////////////// |
144 | /////////////////////////// |
145 | |
146 | void GPUParticlesCollisionSDF3D::_find_meshes(const AABB &p_aabb, Node *p_at_node, List<PlotMesh> &plot_meshes) { |
147 | MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node); |
148 | if (mi && mi->is_visible_in_tree()) { |
149 | if ((mi->get_layer_mask() & bake_mask) == 0) { |
150 | return; |
151 | } |
152 | |
153 | Ref<Mesh> mesh = mi->get_mesh(); |
154 | if (mesh.is_valid()) { |
155 | AABB aabb = mesh->get_aabb(); |
156 | |
157 | Transform3D xf = get_global_transform().affine_inverse() * mi->get_global_transform(); |
158 | |
159 | if (p_aabb.intersects(xf.xform(aabb))) { |
160 | PlotMesh pm; |
161 | pm.local_xform = xf; |
162 | pm.mesh = mesh; |
163 | plot_meshes.push_back(pm); |
164 | } |
165 | } |
166 | } |
167 | |
168 | Node3D *s = Object::cast_to<Node3D>(p_at_node); |
169 | if (s) { |
170 | if (s->is_visible_in_tree()) { |
171 | Array meshes = p_at_node->call("get_meshes" ); |
172 | for (int i = 0; i < meshes.size(); i += 2) { |
173 | Transform3D mxf = meshes[i]; |
174 | Ref<Mesh> mesh = meshes[i + 1]; |
175 | if (!mesh.is_valid()) { |
176 | continue; |
177 | } |
178 | |
179 | AABB aabb = mesh->get_aabb(); |
180 | |
181 | Transform3D xf = get_global_transform().affine_inverse() * (s->get_global_transform() * mxf); |
182 | |
183 | if (p_aabb.intersects(xf.xform(aabb))) { |
184 | PlotMesh pm; |
185 | pm.local_xform = xf; |
186 | pm.mesh = mesh; |
187 | plot_meshes.push_back(pm); |
188 | } |
189 | } |
190 | } |
191 | } |
192 | |
193 | for (int i = 0; i < p_at_node->get_child_count(); i++) { |
194 | Node *child = p_at_node->get_child(i); |
195 | _find_meshes(p_aabb, child, plot_meshes); |
196 | } |
197 | } |
198 | |
199 | uint32_t GPUParticlesCollisionSDF3D::_create_bvh(LocalVector<BVH> &bvh_tree, FacePos *p_faces, uint32_t p_face_count, const Face3 *p_triangles, float p_thickness) { |
200 | if (p_face_count == 1) { |
201 | return BVH::LEAF_BIT | p_faces[0].index; |
202 | } |
203 | |
204 | uint32_t index = bvh_tree.size(); |
205 | { |
206 | BVH bvh; |
207 | |
208 | for (uint32_t i = 0; i < p_face_count; i++) { |
209 | const Face3 &f = p_triangles[p_faces[i].index]; |
210 | AABB aabb(f.vertex[0], Vector3()); |
211 | aabb.expand_to(f.vertex[1]); |
212 | aabb.expand_to(f.vertex[2]); |
213 | if (p_thickness > 0.0) { |
214 | Vector3 normal = p_triangles[p_faces[i].index].get_plane().normal; |
215 | aabb.expand_to(f.vertex[0] - normal * p_thickness); |
216 | aabb.expand_to(f.vertex[1] - normal * p_thickness); |
217 | aabb.expand_to(f.vertex[2] - normal * p_thickness); |
218 | } |
219 | if (i == 0) { |
220 | bvh.bounds = aabb; |
221 | } else { |
222 | bvh.bounds.merge_with(aabb); |
223 | } |
224 | } |
225 | bvh_tree.push_back(bvh); |
226 | } |
227 | |
228 | uint32_t middle = p_face_count / 2; |
229 | |
230 | SortArray<FacePos, FaceSort> s; |
231 | s.compare.axis = bvh_tree[index].bounds.get_longest_axis_index(); |
232 | s.sort(p_faces, p_face_count); |
233 | |
234 | uint32_t left = _create_bvh(bvh_tree, p_faces, middle, p_triangles, p_thickness); |
235 | uint32_t right = _create_bvh(bvh_tree, p_faces + middle, p_face_count - middle, p_triangles, p_thickness); |
236 | |
237 | bvh_tree[index].children[0] = left; |
238 | bvh_tree[index].children[1] = right; |
239 | |
240 | return index; |
241 | } |
242 | |
243 | static _FORCE_INLINE_ real_t Vector3_dot2(const Vector3 &p_vec3) { |
244 | return p_vec3.dot(p_vec3); |
245 | } |
246 | |
247 | void GPUParticlesCollisionSDF3D::_find_closest_distance(const Vector3 &p_pos, const BVH *p_bvh, uint32_t p_bvh_cell, const Face3 *p_triangles, float p_thickness, float &r_closest_distance) { |
248 | if (p_bvh_cell & BVH::LEAF_BIT) { |
249 | p_bvh_cell &= BVH::LEAF_MASK; //remove bit |
250 | |
251 | Vector3 point = p_pos; |
252 | Plane p = p_triangles[p_bvh_cell].get_plane(); |
253 | float d = p.distance_to(point); |
254 | float inside_d = 1e20; |
255 | if (d < 0 && d > -p_thickness) { |
256 | //inside planes, do this in 2D |
257 | |
258 | Vector3 x_axis = (p_triangles[p_bvh_cell].vertex[0] - p_triangles[p_bvh_cell].vertex[1]).normalized(); |
259 | Vector3 y_axis = p.normal.cross(x_axis).normalized(); |
260 | |
261 | Vector2 points[3]; |
262 | for (int i = 0; i < 3; i++) { |
263 | points[i] = Vector2(x_axis.dot(p_triangles[p_bvh_cell].vertex[i]), y_axis.dot(p_triangles[p_bvh_cell].vertex[i])); |
264 | } |
265 | |
266 | Vector2 p2d = Vector2(x_axis.dot(point), y_axis.dot(point)); |
267 | |
268 | { |
269 | // https://www.shadertoy.com/view/XsXSz4 |
270 | |
271 | Vector2 e0 = points[1] - points[0]; |
272 | Vector2 e1 = points[2] - points[1]; |
273 | Vector2 e2 = points[0] - points[2]; |
274 | |
275 | Vector2 v0 = p2d - points[0]; |
276 | Vector2 v1 = p2d - points[1]; |
277 | Vector2 v2 = p2d - points[2]; |
278 | |
279 | Vector2 pq0 = v0 - e0 * CLAMP(v0.dot(e0) / e0.dot(e0), 0.0, 1.0); |
280 | Vector2 pq1 = v1 - e1 * CLAMP(v1.dot(e1) / e1.dot(e1), 0.0, 1.0); |
281 | Vector2 pq2 = v2 - e2 * CLAMP(v2.dot(e2) / e2.dot(e2), 0.0, 1.0); |
282 | |
283 | float s = SIGN(e0.x * e2.y - e0.y * e2.x); |
284 | Vector2 d2 = Vector2(pq0.dot(pq0), s * (v0.x * e0.y - v0.y * e0.x)).min(Vector2(pq1.dot(pq1), s * (v1.x * e1.y - v1.y * e1.x))).min(Vector2(pq2.dot(pq2), s * (v2.x * e2.y - v2.y * e2.x))); |
285 | |
286 | inside_d = -Math::sqrt(d2.x) * SIGN(d2.y); |
287 | } |
288 | |
289 | //make sure distance to planes is not shorter if inside |
290 | if (inside_d < 0) { |
291 | inside_d = MAX(inside_d, d); |
292 | inside_d = MAX(inside_d, -(p_thickness + d)); |
293 | } |
294 | |
295 | r_closest_distance = MIN(r_closest_distance, inside_d); |
296 | } else { |
297 | if (d < 0) { |
298 | point -= p.normal * p_thickness; //flatten |
299 | } |
300 | |
301 | // https://iquilezles.org/www/articles/distfunctions/distfunctions.htm |
302 | Vector3 a = p_triangles[p_bvh_cell].vertex[0]; |
303 | Vector3 b = p_triangles[p_bvh_cell].vertex[1]; |
304 | Vector3 c = p_triangles[p_bvh_cell].vertex[2]; |
305 | |
306 | Vector3 ba = b - a; |
307 | Vector3 pa = point - a; |
308 | Vector3 cb = c - b; |
309 | Vector3 pb = point - b; |
310 | Vector3 ac = a - c; |
311 | Vector3 pc = point - c; |
312 | Vector3 nor = ba.cross(ac); |
313 | |
314 | inside_d = Math::sqrt( |
315 | (SIGN(ba.cross(nor).dot(pa)) + SIGN(cb.cross(nor).dot(pb)) + SIGN(ac.cross(nor).dot(pc)) < 2.0) |
316 | ? MIN(MIN( |
317 | Vector3_dot2(ba * CLAMP(ba.dot(pa) / Vector3_dot2(ba), 0.0, 1.0) - pa), |
318 | Vector3_dot2(cb * CLAMP(cb.dot(pb) / Vector3_dot2(cb), 0.0, 1.0) - pb)), |
319 | Vector3_dot2(ac * CLAMP(ac.dot(pc) / Vector3_dot2(ac), 0.0, 1.0) - pc)) |
320 | : nor.dot(pa) * nor.dot(pa) / Vector3_dot2(nor)); |
321 | |
322 | r_closest_distance = MIN(r_closest_distance, inside_d); |
323 | } |
324 | |
325 | } else { |
326 | bool pass = true; |
327 | if (!p_bvh[p_bvh_cell].bounds.has_point(p_pos)) { |
328 | //outside, find closest point |
329 | Vector3 he = p_bvh[p_bvh_cell].bounds.size * 0.5; |
330 | Vector3 center = p_bvh[p_bvh_cell].bounds.position + he; |
331 | |
332 | Vector3 rel = (p_pos - center).abs(); |
333 | Vector3 closest(MIN(rel.x, he.x), MIN(rel.y, he.y), MIN(rel.z, he.z)); |
334 | float d = rel.distance_to(closest); |
335 | |
336 | if (d >= r_closest_distance) { |
337 | pass = false; //already closer than this aabb, discard |
338 | } |
339 | } |
340 | |
341 | if (pass) { |
342 | _find_closest_distance(p_pos, p_bvh, p_bvh[p_bvh_cell].children[0], p_triangles, p_thickness, r_closest_distance); |
343 | _find_closest_distance(p_pos, p_bvh, p_bvh[p_bvh_cell].children[1], p_triangles, p_thickness, r_closest_distance); |
344 | } |
345 | } |
346 | } |
347 | |
348 | void GPUParticlesCollisionSDF3D::_compute_sdf_z(uint32_t p_z, ComputeSDFParams *params) { |
349 | int32_t z_ofs = p_z * params->size.y * params->size.x; |
350 | for (int32_t y = 0; y < params->size.y; y++) { |
351 | int32_t y_ofs = z_ofs + y * params->size.x; |
352 | for (int32_t x = 0; x < params->size.x; x++) { |
353 | int32_t x_ofs = y_ofs + x; |
354 | float &cell = params->cells[x_ofs]; |
355 | |
356 | Vector3 pos = params->cell_offset + Vector3(x, y, p_z) * params->cell_size; |
357 | |
358 | cell = 1e20; |
359 | |
360 | _find_closest_distance(pos, params->bvh, 0, params->triangles, params->thickness, cell); |
361 | } |
362 | } |
363 | } |
364 | |
365 | void GPUParticlesCollisionSDF3D::_compute_sdf(ComputeSDFParams *params) { |
366 | WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &GPUParticlesCollisionSDF3D::_compute_sdf_z, params, params->size.z); |
367 | while (!WorkerThreadPool::get_singleton()->is_group_task_completed(group_task)) { |
368 | OS::get_singleton()->delay_usec(10000); |
369 | if (bake_step_function) { |
370 | bake_step_function(WorkerThreadPool::get_singleton()->get_group_processed_element_count(group_task) * 100 / params->size.z, "Baking SDF" ); |
371 | } |
372 | } |
373 | WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); |
374 | } |
375 | |
376 | Vector3i GPUParticlesCollisionSDF3D::get_estimated_cell_size() const { |
377 | static const int subdivs[RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 }; |
378 | int subdiv = subdivs[get_resolution()]; |
379 | |
380 | AABB aabb(-size / 2, size); |
381 | |
382 | float cell_size = aabb.get_longest_axis_size() / float(subdiv); |
383 | |
384 | Vector3i sdf_size = Vector3i(aabb.size / cell_size); |
385 | sdf_size.x = MAX(1, sdf_size.x); |
386 | sdf_size.y = MAX(1, sdf_size.y); |
387 | sdf_size.z = MAX(1, sdf_size.z); |
388 | return sdf_size; |
389 | } |
390 | |
391 | Ref<Image> GPUParticlesCollisionSDF3D::bake() { |
392 | static const int subdivs[RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 }; |
393 | int subdiv = subdivs[get_resolution()]; |
394 | |
395 | AABB aabb(-size / 2, size); |
396 | |
397 | float cell_size = aabb.get_longest_axis_size() / float(subdiv); |
398 | |
399 | Vector3i sdf_size = Vector3i(aabb.size / cell_size); |
400 | sdf_size.x = MAX(1, sdf_size.x); |
401 | sdf_size.y = MAX(1, sdf_size.y); |
402 | sdf_size.z = MAX(1, sdf_size.z); |
403 | |
404 | if (bake_begin_function) { |
405 | bake_begin_function(100); |
406 | } |
407 | |
408 | aabb.size = Vector3(sdf_size) * cell_size; |
409 | |
410 | List<PlotMesh> plot_meshes; |
411 | _find_meshes(aabb, get_parent(), plot_meshes); |
412 | |
413 | LocalVector<Face3> faces; |
414 | |
415 | if (bake_step_function) { |
416 | bake_step_function(0, "Finding Meshes" ); |
417 | } |
418 | |
419 | for (const PlotMesh &pm : plot_meshes) { |
420 | for (int i = 0; i < pm.mesh->get_surface_count(); i++) { |
421 | if (pm.mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { |
422 | continue; //only triangles |
423 | } |
424 | |
425 | Array a = pm.mesh->surface_get_arrays(i); |
426 | |
427 | Vector<Vector3> vertices = a[Mesh::ARRAY_VERTEX]; |
428 | const Vector3 *vr = vertices.ptr(); |
429 | Vector<int> index = a[Mesh::ARRAY_INDEX]; |
430 | |
431 | if (index.size()) { |
432 | int facecount = index.size() / 3; |
433 | const int *ir = index.ptr(); |
434 | |
435 | for (int j = 0; j < facecount; j++) { |
436 | Face3 face; |
437 | |
438 | for (int k = 0; k < 3; k++) { |
439 | face.vertex[k] = pm.local_xform.xform(vr[ir[j * 3 + k]]); |
440 | } |
441 | |
442 | //test against original bounds |
443 | if (!Geometry3D::triangle_box_overlap(aabb.get_center(), aabb.size * 0.5, face.vertex)) { |
444 | continue; |
445 | } |
446 | |
447 | faces.push_back(face); |
448 | } |
449 | |
450 | } else { |
451 | int facecount = vertices.size() / 3; |
452 | |
453 | for (int j = 0; j < facecount; j++) { |
454 | Face3 face; |
455 | |
456 | for (int k = 0; k < 3; k++) { |
457 | face.vertex[k] = pm.local_xform.xform(vr[j * 3 + k]); |
458 | } |
459 | |
460 | //test against original bounds |
461 | if (!Geometry3D::triangle_box_overlap(aabb.get_center(), aabb.size * 0.5, face.vertex)) { |
462 | continue; |
463 | } |
464 | |
465 | faces.push_back(face); |
466 | } |
467 | } |
468 | } |
469 | } |
470 | |
471 | //compute bvh |
472 | if (faces.size() <= 1) { |
473 | ERR_PRINT("No faces detected during GPUParticlesCollisionSDF3D bake. Check whether there are visible meshes matching the bake mask within its extents." ); |
474 | if (bake_end_function) { |
475 | bake_end_function(); |
476 | } |
477 | return Ref<Image>(); |
478 | } |
479 | |
480 | LocalVector<FacePos> face_pos; |
481 | |
482 | face_pos.resize(faces.size()); |
483 | |
484 | float th = cell_size * thickness; |
485 | |
486 | for (uint32_t i = 0; i < faces.size(); i++) { |
487 | face_pos[i].index = i; |
488 | face_pos[i].center = (faces[i].vertex[0] + faces[i].vertex[1] + faces[i].vertex[2]) / 2; |
489 | if (th > 0.0) { |
490 | face_pos[i].center -= faces[i].get_plane().normal * th * 0.5; |
491 | } |
492 | } |
493 | |
494 | if (bake_step_function) { |
495 | bake_step_function(0, "Creating BVH" ); |
496 | } |
497 | |
498 | LocalVector<BVH> bvh; |
499 | |
500 | _create_bvh(bvh, face_pos.ptr(), face_pos.size(), faces.ptr(), th); |
501 | |
502 | Vector<uint8_t> cells_data; |
503 | cells_data.resize(sdf_size.z * sdf_size.y * sdf_size.x * (int)sizeof(float)); |
504 | |
505 | if (bake_step_function) { |
506 | bake_step_function(0, "Baking SDF" ); |
507 | } |
508 | |
509 | ComputeSDFParams params; |
510 | params.cells = (float *)cells_data.ptrw(); |
511 | params.size = sdf_size; |
512 | params.cell_size = cell_size; |
513 | params.cell_offset = aabb.position + Vector3(cell_size * 0.5, cell_size * 0.5, cell_size * 0.5); |
514 | params.bvh = bvh.ptr(); |
515 | params.triangles = faces.ptr(); |
516 | params.thickness = th; |
517 | _compute_sdf(¶ms); |
518 | |
519 | Ref<Image> ret = Image::create_from_data(sdf_size.x, sdf_size.y * sdf_size.z, false, Image::FORMAT_RF, cells_data); |
520 | ret->convert(Image::FORMAT_RH); //convert to half, save space |
521 | ret->set_meta("depth" , sdf_size.z); //hack, make sure to add to the docs of this function |
522 | |
523 | if (bake_end_function) { |
524 | bake_end_function(); |
525 | } |
526 | |
527 | return ret; |
528 | } |
529 | |
530 | PackedStringArray GPUParticlesCollisionSDF3D::get_configuration_warnings() const { |
531 | PackedStringArray warnings = Node::get_configuration_warnings(); |
532 | |
533 | if (bake_mask == 0) { |
534 | warnings.push_back(RTR("The Bake Mask has no bits enabled, which means baking will not produce any collision for this GPUParticlesCollisionSDF3D.\nTo resolve this, enable at least one bit in the Bake Mask property." )); |
535 | } |
536 | |
537 | return warnings; |
538 | } |
539 | |
540 | void GPUParticlesCollisionSDF3D::_bind_methods() { |
541 | ClassDB::bind_method(D_METHOD("set_size" , "size" ), &GPUParticlesCollisionSDF3D::set_size); |
542 | ClassDB::bind_method(D_METHOD("get_size" ), &GPUParticlesCollisionSDF3D::get_size); |
543 | |
544 | ClassDB::bind_method(D_METHOD("set_resolution" , "resolution" ), &GPUParticlesCollisionSDF3D::set_resolution); |
545 | ClassDB::bind_method(D_METHOD("get_resolution" ), &GPUParticlesCollisionSDF3D::get_resolution); |
546 | |
547 | ClassDB::bind_method(D_METHOD("set_texture" , "texture" ), &GPUParticlesCollisionSDF3D::set_texture); |
548 | ClassDB::bind_method(D_METHOD("get_texture" ), &GPUParticlesCollisionSDF3D::get_texture); |
549 | |
550 | ClassDB::bind_method(D_METHOD("set_thickness" , "thickness" ), &GPUParticlesCollisionSDF3D::set_thickness); |
551 | ClassDB::bind_method(D_METHOD("get_thickness" ), &GPUParticlesCollisionSDF3D::get_thickness); |
552 | |
553 | ClassDB::bind_method(D_METHOD("set_bake_mask" , "mask" ), &GPUParticlesCollisionSDF3D::set_bake_mask); |
554 | ClassDB::bind_method(D_METHOD("get_bake_mask" ), &GPUParticlesCollisionSDF3D::get_bake_mask); |
555 | ClassDB::bind_method(D_METHOD("set_bake_mask_value" , "layer_number" , "value" ), &GPUParticlesCollisionSDF3D::set_bake_mask_value); |
556 | ClassDB::bind_method(D_METHOD("get_bake_mask_value" , "layer_number" ), &GPUParticlesCollisionSDF3D::get_bake_mask_value); |
557 | |
558 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size" , PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m" ), "set_size" , "get_size" ); |
559 | ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution" , PROPERTY_HINT_ENUM, "16,32,64,128,256,512" ), "set_resolution" , "get_resolution" ); |
560 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "thickness" , PROPERTY_HINT_RANGE, "0.0,2.0,0.01,suffix:m" ), "set_thickness" , "get_thickness" ); |
561 | ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mask" , PROPERTY_HINT_LAYERS_3D_RENDER), "set_bake_mask" , "get_bake_mask" ); |
562 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture" , PROPERTY_HINT_RESOURCE_TYPE, "Texture3D" ), "set_texture" , "get_texture" ); |
563 | |
564 | BIND_ENUM_CONSTANT(RESOLUTION_16); |
565 | BIND_ENUM_CONSTANT(RESOLUTION_32); |
566 | BIND_ENUM_CONSTANT(RESOLUTION_64); |
567 | BIND_ENUM_CONSTANT(RESOLUTION_128); |
568 | BIND_ENUM_CONSTANT(RESOLUTION_256); |
569 | BIND_ENUM_CONSTANT(RESOLUTION_512); |
570 | BIND_ENUM_CONSTANT(RESOLUTION_MAX); |
571 | } |
572 | |
573 | #ifndef DISABLE_DEPRECATED |
574 | bool GPUParticlesCollisionSDF3D::_set(const StringName &p_name, const Variant &p_value) { |
575 | if (p_name == "extents" ) { // Compatibility with Godot 3.x. |
576 | set_size((Vector3)p_value * 2); |
577 | return true; |
578 | } |
579 | return false; |
580 | } |
581 | |
582 | bool GPUParticlesCollisionSDF3D::_get(const StringName &p_name, Variant &r_property) const { |
583 | if (p_name == "extents" ) { // Compatibility with Godot 3.x. |
584 | r_property = size / 2; |
585 | return true; |
586 | } |
587 | return false; |
588 | } |
589 | #endif // DISABLE_DEPRECATED |
590 | |
591 | void GPUParticlesCollisionSDF3D::set_thickness(float p_thickness) { |
592 | thickness = p_thickness; |
593 | } |
594 | |
595 | float GPUParticlesCollisionSDF3D::get_thickness() const { |
596 | return thickness; |
597 | } |
598 | |
599 | void GPUParticlesCollisionSDF3D::set_size(const Vector3 &p_size) { |
600 | size = p_size; |
601 | RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), size / 2); |
602 | update_gizmos(); |
603 | } |
604 | |
605 | Vector3 GPUParticlesCollisionSDF3D::get_size() const { |
606 | return size; |
607 | } |
608 | |
609 | void GPUParticlesCollisionSDF3D::set_resolution(Resolution p_resolution) { |
610 | resolution = p_resolution; |
611 | update_gizmos(); |
612 | } |
613 | |
614 | GPUParticlesCollisionSDF3D::Resolution GPUParticlesCollisionSDF3D::get_resolution() const { |
615 | return resolution; |
616 | } |
617 | |
618 | void GPUParticlesCollisionSDF3D::set_bake_mask(uint32_t p_mask) { |
619 | bake_mask = p_mask; |
620 | update_configuration_warnings(); |
621 | } |
622 | |
623 | uint32_t GPUParticlesCollisionSDF3D::get_bake_mask() const { |
624 | return bake_mask; |
625 | } |
626 | |
627 | void GPUParticlesCollisionSDF3D::set_bake_mask_value(int p_layer_number, bool p_value) { |
628 | ERR_FAIL_COND_MSG(p_layer_number < 1 || p_layer_number > 20, vformat("The render layer number (%d) must be between 1 and 20 (inclusive)." , p_layer_number)); |
629 | uint32_t mask = get_bake_mask(); |
630 | if (p_value) { |
631 | mask |= 1 << (p_layer_number - 1); |
632 | } else { |
633 | mask &= ~(1 << (p_layer_number - 1)); |
634 | } |
635 | set_bake_mask(mask); |
636 | } |
637 | |
638 | bool GPUParticlesCollisionSDF3D::get_bake_mask_value(int p_layer_number) const { |
639 | ERR_FAIL_COND_V_MSG(p_layer_number < 1 || p_layer_number > 20, false, vformat("The render layer number (%d) must be between 1 and 20 (inclusive)." , p_layer_number)); |
640 | return bake_mask & (1 << (p_layer_number - 1)); |
641 | } |
642 | |
643 | void GPUParticlesCollisionSDF3D::set_texture(const Ref<Texture3D> &p_texture) { |
644 | texture = p_texture; |
645 | RID tex = texture.is_valid() ? texture->get_rid() : RID(); |
646 | RS::get_singleton()->particles_collision_set_field_texture(_get_collision(), tex); |
647 | } |
648 | |
649 | Ref<Texture3D> GPUParticlesCollisionSDF3D::get_texture() const { |
650 | return texture; |
651 | } |
652 | |
653 | AABB GPUParticlesCollisionSDF3D::get_aabb() const { |
654 | return AABB(-size / 2, size); |
655 | } |
656 | |
657 | GPUParticlesCollisionSDF3D::BakeBeginFunc GPUParticlesCollisionSDF3D::bake_begin_function = nullptr; |
658 | GPUParticlesCollisionSDF3D::BakeStepFunc GPUParticlesCollisionSDF3D::bake_step_function = nullptr; |
659 | GPUParticlesCollisionSDF3D::BakeEndFunc GPUParticlesCollisionSDF3D::bake_end_function = nullptr; |
660 | |
661 | GPUParticlesCollisionSDF3D::GPUParticlesCollisionSDF3D() : |
662 | GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_SDF_COLLIDE) { |
663 | } |
664 | |
665 | GPUParticlesCollisionSDF3D::~GPUParticlesCollisionSDF3D() { |
666 | } |
667 | |
668 | //////////////////////////// |
669 | //////////////////////////// |
670 | |
671 | void GPUParticlesCollisionHeightField3D::_notification(int p_what) { |
672 | switch (p_what) { |
673 | case NOTIFICATION_INTERNAL_PROCESS: { |
674 | if (update_mode == UPDATE_MODE_ALWAYS) { |
675 | RS::get_singleton()->particles_collision_height_field_update(_get_collision()); |
676 | } |
677 | |
678 | if (follow_camera_mode && get_viewport()) { |
679 | Camera3D *cam = get_viewport()->get_camera_3d(); |
680 | if (cam) { |
681 | Transform3D xform = get_global_transform(); |
682 | Vector3 x_axis = xform.basis.get_column(Vector3::AXIS_X).normalized(); |
683 | Vector3 z_axis = xform.basis.get_column(Vector3::AXIS_Z).normalized(); |
684 | float x_len = xform.basis.get_scale().x; |
685 | float z_len = xform.basis.get_scale().z; |
686 | |
687 | Vector3 cam_pos = cam->get_global_transform().origin; |
688 | Transform3D new_xform = xform; |
689 | |
690 | while (x_axis.dot(cam_pos - new_xform.origin) > x_len) { |
691 | new_xform.origin += x_axis * x_len; |
692 | } |
693 | while (x_axis.dot(cam_pos - new_xform.origin) < -x_len) { |
694 | new_xform.origin -= x_axis * x_len; |
695 | } |
696 | |
697 | while (z_axis.dot(cam_pos - new_xform.origin) > z_len) { |
698 | new_xform.origin += z_axis * z_len; |
699 | } |
700 | while (z_axis.dot(cam_pos - new_xform.origin) < -z_len) { |
701 | new_xform.origin -= z_axis * z_len; |
702 | } |
703 | |
704 | if (new_xform != xform) { |
705 | set_global_transform(new_xform); |
706 | RS::get_singleton()->particles_collision_height_field_update(_get_collision()); |
707 | } |
708 | } |
709 | } |
710 | } break; |
711 | |
712 | case NOTIFICATION_TRANSFORM_CHANGED: { |
713 | RS::get_singleton()->particles_collision_height_field_update(_get_collision()); |
714 | } break; |
715 | } |
716 | } |
717 | |
718 | void GPUParticlesCollisionHeightField3D::_bind_methods() { |
719 | ClassDB::bind_method(D_METHOD("set_size" , "size" ), &GPUParticlesCollisionHeightField3D::set_size); |
720 | ClassDB::bind_method(D_METHOD("get_size" ), &GPUParticlesCollisionHeightField3D::get_size); |
721 | |
722 | ClassDB::bind_method(D_METHOD("set_resolution" , "resolution" ), &GPUParticlesCollisionHeightField3D::set_resolution); |
723 | ClassDB::bind_method(D_METHOD("get_resolution" ), &GPUParticlesCollisionHeightField3D::get_resolution); |
724 | |
725 | ClassDB::bind_method(D_METHOD("set_update_mode" , "update_mode" ), &GPUParticlesCollisionHeightField3D::set_update_mode); |
726 | ClassDB::bind_method(D_METHOD("get_update_mode" ), &GPUParticlesCollisionHeightField3D::get_update_mode); |
727 | |
728 | ClassDB::bind_method(D_METHOD("set_follow_camera_enabled" , "enabled" ), &GPUParticlesCollisionHeightField3D::set_follow_camera_enabled); |
729 | ClassDB::bind_method(D_METHOD("is_follow_camera_enabled" ), &GPUParticlesCollisionHeightField3D::is_follow_camera_enabled); |
730 | |
731 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size" , PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m" ), "set_size" , "get_size" ); |
732 | ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution" , PROPERTY_HINT_ENUM, "256 (Fastest),512 (Fast),1024 (Average),2048 (Slow),4096 (Slower),8192 (Slowest)" ), "set_resolution" , "get_resolution" ); |
733 | ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode" , PROPERTY_HINT_ENUM, "When Moved (Fast),Always (Slow)" ), "set_update_mode" , "get_update_mode" ); |
734 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled" ), "set_follow_camera_enabled" , "is_follow_camera_enabled" ); |
735 | |
736 | BIND_ENUM_CONSTANT(RESOLUTION_256); |
737 | BIND_ENUM_CONSTANT(RESOLUTION_512); |
738 | BIND_ENUM_CONSTANT(RESOLUTION_1024); |
739 | BIND_ENUM_CONSTANT(RESOLUTION_2048); |
740 | BIND_ENUM_CONSTANT(RESOLUTION_4096); |
741 | BIND_ENUM_CONSTANT(RESOLUTION_8192); |
742 | BIND_ENUM_CONSTANT(RESOLUTION_MAX); |
743 | |
744 | BIND_ENUM_CONSTANT(UPDATE_MODE_WHEN_MOVED); |
745 | BIND_ENUM_CONSTANT(UPDATE_MODE_ALWAYS); |
746 | } |
747 | |
748 | #ifndef DISABLE_DEPRECATED |
749 | bool GPUParticlesCollisionHeightField3D::_set(const StringName &p_name, const Variant &p_value) { |
750 | if (p_name == "extents" ) { // Compatibility with Godot 3.x. |
751 | set_size((Vector3)p_value * 2); |
752 | return true; |
753 | } |
754 | return false; |
755 | } |
756 | |
757 | bool GPUParticlesCollisionHeightField3D::_get(const StringName &p_name, Variant &r_property) const { |
758 | if (p_name == "extents" ) { // Compatibility with Godot 3.x. |
759 | r_property = size / 2; |
760 | return true; |
761 | } |
762 | return false; |
763 | } |
764 | #endif // DISABLE_DEPRECATED |
765 | |
766 | void GPUParticlesCollisionHeightField3D::set_size(const Vector3 &p_size) { |
767 | size = p_size; |
768 | RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), size / 2); |
769 | update_gizmos(); |
770 | RS::get_singleton()->particles_collision_height_field_update(_get_collision()); |
771 | } |
772 | |
773 | Vector3 GPUParticlesCollisionHeightField3D::get_size() const { |
774 | return size; |
775 | } |
776 | |
777 | void GPUParticlesCollisionHeightField3D::set_resolution(Resolution p_resolution) { |
778 | resolution = p_resolution; |
779 | RS::get_singleton()->particles_collision_set_height_field_resolution(_get_collision(), RS::ParticlesCollisionHeightfieldResolution(resolution)); |
780 | update_gizmos(); |
781 | RS::get_singleton()->particles_collision_height_field_update(_get_collision()); |
782 | } |
783 | |
784 | GPUParticlesCollisionHeightField3D::Resolution GPUParticlesCollisionHeightField3D::get_resolution() const { |
785 | return resolution; |
786 | } |
787 | |
788 | void GPUParticlesCollisionHeightField3D::set_update_mode(UpdateMode p_update_mode) { |
789 | update_mode = p_update_mode; |
790 | set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS); |
791 | } |
792 | |
793 | GPUParticlesCollisionHeightField3D::UpdateMode GPUParticlesCollisionHeightField3D::get_update_mode() const { |
794 | return update_mode; |
795 | } |
796 | |
797 | void GPUParticlesCollisionHeightField3D::set_follow_camera_enabled(bool p_enabled) { |
798 | follow_camera_mode = p_enabled; |
799 | set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS); |
800 | } |
801 | |
802 | bool GPUParticlesCollisionHeightField3D::is_follow_camera_enabled() const { |
803 | return follow_camera_mode; |
804 | } |
805 | |
806 | AABB GPUParticlesCollisionHeightField3D::get_aabb() const { |
807 | return AABB(-size / 2, size); |
808 | } |
809 | |
810 | GPUParticlesCollisionHeightField3D::GPUParticlesCollisionHeightField3D() : |
811 | GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE) { |
812 | } |
813 | |
814 | GPUParticlesCollisionHeightField3D::~GPUParticlesCollisionHeightField3D() { |
815 | } |
816 | |
817 | //////////////////////////// |
818 | //////////////////////////// |
819 | |
820 | void GPUParticlesAttractor3D::set_cull_mask(uint32_t p_cull_mask) { |
821 | cull_mask = p_cull_mask; |
822 | RS::get_singleton()->particles_collision_set_cull_mask(collision, p_cull_mask); |
823 | } |
824 | |
825 | uint32_t GPUParticlesAttractor3D::get_cull_mask() const { |
826 | return cull_mask; |
827 | } |
828 | |
829 | void GPUParticlesAttractor3D::set_strength(real_t p_strength) { |
830 | strength = p_strength; |
831 | RS::get_singleton()->particles_collision_set_attractor_strength(collision, p_strength); |
832 | } |
833 | |
834 | real_t GPUParticlesAttractor3D::get_strength() const { |
835 | return strength; |
836 | } |
837 | |
838 | void GPUParticlesAttractor3D::set_attenuation(real_t p_attenuation) { |
839 | attenuation = p_attenuation; |
840 | RS::get_singleton()->particles_collision_set_attractor_attenuation(collision, p_attenuation); |
841 | } |
842 | |
843 | real_t GPUParticlesAttractor3D::get_attenuation() const { |
844 | return attenuation; |
845 | } |
846 | |
847 | void GPUParticlesAttractor3D::set_directionality(real_t p_directionality) { |
848 | directionality = p_directionality; |
849 | RS::get_singleton()->particles_collision_set_attractor_directionality(collision, p_directionality); |
850 | update_gizmos(); |
851 | } |
852 | |
853 | real_t GPUParticlesAttractor3D::get_directionality() const { |
854 | return directionality; |
855 | } |
856 | |
857 | void GPUParticlesAttractor3D::_bind_methods() { |
858 | ClassDB::bind_method(D_METHOD("set_cull_mask" , "mask" ), &GPUParticlesAttractor3D::set_cull_mask); |
859 | ClassDB::bind_method(D_METHOD("get_cull_mask" ), &GPUParticlesAttractor3D::get_cull_mask); |
860 | |
861 | ClassDB::bind_method(D_METHOD("set_strength" , "strength" ), &GPUParticlesAttractor3D::set_strength); |
862 | ClassDB::bind_method(D_METHOD("get_strength" ), &GPUParticlesAttractor3D::get_strength); |
863 | |
864 | ClassDB::bind_method(D_METHOD("set_attenuation" , "attenuation" ), &GPUParticlesAttractor3D::set_attenuation); |
865 | ClassDB::bind_method(D_METHOD("get_attenuation" ), &GPUParticlesAttractor3D::get_attenuation); |
866 | |
867 | ClassDB::bind_method(D_METHOD("set_directionality" , "amount" ), &GPUParticlesAttractor3D::set_directionality); |
868 | ClassDB::bind_method(D_METHOD("get_directionality" ), &GPUParticlesAttractor3D::get_directionality); |
869 | |
870 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength" , PROPERTY_HINT_RANGE, "-128,128,0.01,or_greater,or_less" ), "set_strength" , "get_strength" ); |
871 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation" , PROPERTY_HINT_EXP_EASING, "0,8,0.01" ), "set_attenuation" , "get_attenuation" ); |
872 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "directionality" , PROPERTY_HINT_RANGE, "0,1,0.01" ), "set_directionality" , "get_directionality" ); |
873 | ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask" , PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask" , "get_cull_mask" ); |
874 | } |
875 | |
876 | GPUParticlesAttractor3D::GPUParticlesAttractor3D(RS::ParticlesCollisionType p_type) { |
877 | collision = RS::get_singleton()->particles_collision_create(); |
878 | RS::get_singleton()->particles_collision_set_collision_type(collision, p_type); |
879 | set_base(collision); |
880 | } |
881 | GPUParticlesAttractor3D::~GPUParticlesAttractor3D() { |
882 | ERR_FAIL_NULL(RenderingServer::get_singleton()); |
883 | RS::get_singleton()->free(collision); |
884 | } |
885 | |
886 | ///////////////////////////////// |
887 | |
888 | void GPUParticlesAttractorSphere3D::_bind_methods() { |
889 | ClassDB::bind_method(D_METHOD("set_radius" , "radius" ), &GPUParticlesAttractorSphere3D::set_radius); |
890 | ClassDB::bind_method(D_METHOD("get_radius" ), &GPUParticlesAttractorSphere3D::get_radius); |
891 | |
892 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius" , PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m" ), "set_radius" , "get_radius" ); |
893 | } |
894 | |
895 | void GPUParticlesAttractorSphere3D::set_radius(real_t p_radius) { |
896 | radius = p_radius; |
897 | RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius); |
898 | update_gizmos(); |
899 | } |
900 | |
901 | real_t GPUParticlesAttractorSphere3D::get_radius() const { |
902 | return radius; |
903 | } |
904 | |
905 | AABB GPUParticlesAttractorSphere3D::get_aabb() const { |
906 | return AABB(Vector3(-radius, -radius, -radius), Vector3(radius * 2, radius * 2, radius * 2)); |
907 | } |
908 | |
909 | GPUParticlesAttractorSphere3D::GPUParticlesAttractorSphere3D() : |
910 | GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT) { |
911 | } |
912 | |
913 | GPUParticlesAttractorSphere3D::~GPUParticlesAttractorSphere3D() { |
914 | } |
915 | |
916 | /////////////////////////// |
917 | |
918 | void GPUParticlesAttractorBox3D::_bind_methods() { |
919 | ClassDB::bind_method(D_METHOD("set_size" , "size" ), &GPUParticlesAttractorBox3D::set_size); |
920 | ClassDB::bind_method(D_METHOD("get_size" ), &GPUParticlesAttractorBox3D::get_size); |
921 | |
922 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size" , PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m" ), "set_size" , "get_size" ); |
923 | } |
924 | |
925 | #ifndef DISABLE_DEPRECATED |
926 | bool GPUParticlesAttractorBox3D::_set(const StringName &p_name, const Variant &p_value) { |
927 | if (p_name == "extents" ) { // Compatibility with Godot 3.x. |
928 | set_size((Vector3)p_value * 2); |
929 | return true; |
930 | } |
931 | return false; |
932 | } |
933 | |
934 | bool GPUParticlesAttractorBox3D::_get(const StringName &p_name, Variant &r_property) const { |
935 | if (p_name == "extents" ) { // Compatibility with Godot 3.x. |
936 | r_property = size / 2; |
937 | return true; |
938 | } |
939 | return false; |
940 | } |
941 | #endif // DISABLE_DEPRECATED |
942 | |
943 | void GPUParticlesAttractorBox3D::set_size(const Vector3 &p_size) { |
944 | size = p_size; |
945 | RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), size / 2); |
946 | update_gizmos(); |
947 | } |
948 | |
949 | Vector3 GPUParticlesAttractorBox3D::get_size() const { |
950 | return size; |
951 | } |
952 | |
953 | AABB GPUParticlesAttractorBox3D::get_aabb() const { |
954 | return AABB(-size / 2, size); |
955 | } |
956 | |
957 | GPUParticlesAttractorBox3D::GPUParticlesAttractorBox3D() : |
958 | GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_BOX_ATTRACT) { |
959 | } |
960 | |
961 | GPUParticlesAttractorBox3D::~GPUParticlesAttractorBox3D() { |
962 | } |
963 | |
964 | /////////////////////////// |
965 | |
966 | void GPUParticlesAttractorVectorField3D::_bind_methods() { |
967 | ClassDB::bind_method(D_METHOD("set_size" , "size" ), &GPUParticlesAttractorVectorField3D::set_size); |
968 | ClassDB::bind_method(D_METHOD("get_size" ), &GPUParticlesAttractorVectorField3D::get_size); |
969 | |
970 | ClassDB::bind_method(D_METHOD("set_texture" , "texture" ), &GPUParticlesAttractorVectorField3D::set_texture); |
971 | ClassDB::bind_method(D_METHOD("get_texture" ), &GPUParticlesAttractorVectorField3D::get_texture); |
972 | |
973 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size" , PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m" ), "set_size" , "get_size" ); |
974 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture" , PROPERTY_HINT_RESOURCE_TYPE, "Texture3D" ), "set_texture" , "get_texture" ); |
975 | } |
976 | |
977 | #ifndef DISABLE_DEPRECATED |
978 | bool GPUParticlesAttractorVectorField3D::_set(const StringName &p_name, const Variant &p_value) { |
979 | if (p_name == "extents" ) { // Compatibility with Godot 3.x. |
980 | set_size((Vector3)p_value * 2); |
981 | return true; |
982 | } |
983 | return false; |
984 | } |
985 | |
986 | bool GPUParticlesAttractorVectorField3D::_get(const StringName &p_name, Variant &r_property) const { |
987 | if (p_name == "extents" ) { // Compatibility with Godot 3.x. |
988 | r_property = size / 2; |
989 | return true; |
990 | } |
991 | return false; |
992 | } |
993 | #endif // DISABLE_DEPRECATED |
994 | |
995 | void GPUParticlesAttractorVectorField3D::set_size(const Vector3 &p_size) { |
996 | size = p_size; |
997 | RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), size / 2); |
998 | update_gizmos(); |
999 | } |
1000 | |
1001 | Vector3 GPUParticlesAttractorVectorField3D::get_size() const { |
1002 | return size; |
1003 | } |
1004 | |
1005 | void GPUParticlesAttractorVectorField3D::set_texture(const Ref<Texture3D> &p_texture) { |
1006 | texture = p_texture; |
1007 | RID tex = texture.is_valid() ? texture->get_rid() : RID(); |
1008 | RS::get_singleton()->particles_collision_set_field_texture(_get_collision(), tex); |
1009 | } |
1010 | |
1011 | Ref<Texture3D> GPUParticlesAttractorVectorField3D::get_texture() const { |
1012 | return texture; |
1013 | } |
1014 | |
1015 | AABB GPUParticlesAttractorVectorField3D::get_aabb() const { |
1016 | return AABB(-size / 2, size); |
1017 | } |
1018 | |
1019 | GPUParticlesAttractorVectorField3D::GPUParticlesAttractorVectorField3D() : |
1020 | GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT) { |
1021 | } |
1022 | |
1023 | GPUParticlesAttractorVectorField3D::~GPUParticlesAttractorVectorField3D() { |
1024 | } |
1025 | |