gpu_particles_collision_3d.cpp 36 KB

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