Path: blob/master/scene/3d/gpu_particles_collision_3d.cpp
20897 views
/**************************************************************************/1/* gpu_particles_collision_3d.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "gpu_particles_collision_3d.h"3132#include "core/math/geometry_3d.h"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"3738void GPUParticlesCollision3D::set_cull_mask(uint32_t p_cull_mask) {39cull_mask = p_cull_mask;40RS::get_singleton()->particles_collision_set_cull_mask(collision, p_cull_mask);41}4243uint32_t GPUParticlesCollision3D::get_cull_mask() const {44return cull_mask;45}4647void GPUParticlesCollision3D::_bind_methods() {48ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &GPUParticlesCollision3D::set_cull_mask);49ClassDB::bind_method(D_METHOD("get_cull_mask"), &GPUParticlesCollision3D::get_cull_mask);5051ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");52}5354GPUParticlesCollision3D::GPUParticlesCollision3D(RS::ParticlesCollisionType p_type) {55collision = RS::get_singleton()->particles_collision_create();56RS::get_singleton()->particles_collision_set_collision_type(collision, p_type);57set_base(collision);58}5960GPUParticlesCollision3D::~GPUParticlesCollision3D() {61ERR_FAIL_NULL(RenderingServer::get_singleton());62RS::get_singleton()->free_rid(collision);63}6465/////////////////////////////////6667void GPUParticlesCollisionSphere3D::_bind_methods() {68ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesCollisionSphere3D::set_radius);69ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesCollisionSphere3D::get_radius);7071ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_radius", "get_radius");72}7374void GPUParticlesCollisionSphere3D::set_radius(real_t p_radius) {75radius = p_radius;76RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius);77update_gizmos();78}7980real_t GPUParticlesCollisionSphere3D::get_radius() const {81return radius;82}8384AABB GPUParticlesCollisionSphere3D::get_aabb() const {85return AABB(Vector3(-radius, -radius, -radius), Vector3(radius * 2, radius * 2, radius * 2));86}8788GPUParticlesCollisionSphere3D::GPUParticlesCollisionSphere3D() :89GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE) {90}9192GPUParticlesCollisionSphere3D::~GPUParticlesCollisionSphere3D() {93}9495///////////////////////////9697void GPUParticlesCollisionBox3D::_bind_methods() {98ClassDB::bind_method(D_METHOD("set_size", "size"), &GPUParticlesCollisionBox3D::set_size);99ClassDB::bind_method(D_METHOD("get_size"), &GPUParticlesCollisionBox3D::get_size);100101ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_size", "get_size");102}103104#ifndef DISABLE_DEPRECATED105bool GPUParticlesCollisionBox3D::_set(const StringName &p_name, const Variant &p_value) {106if (p_name == "extents") { // Compatibility with Godot 3.x.107set_size((Vector3)p_value * 2);108return true;109}110return false;111}112113bool GPUParticlesCollisionBox3D::_get(const StringName &p_name, Variant &r_property) const {114if (p_name == "extents") { // Compatibility with Godot 3.x.115r_property = size / 2;116return true;117}118return false;119}120#endif // DISABLE_DEPRECATED121122void GPUParticlesCollisionBox3D::set_size(const Vector3 &p_size) {123size = p_size;124RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), size / 2);125update_gizmos();126}127128Vector3 GPUParticlesCollisionBox3D::get_size() const {129return size;130}131132AABB GPUParticlesCollisionBox3D::get_aabb() const {133return AABB(-size / 2, size);134}135136GPUParticlesCollisionBox3D::GPUParticlesCollisionBox3D() :137GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_BOX_COLLIDE) {138}139140GPUParticlesCollisionBox3D::~GPUParticlesCollisionBox3D() {141}142143///////////////////////////////144///////////////////////////145146void GPUParticlesCollisionSDF3D::_find_meshes(const AABB &p_aabb, Node *p_at_node, List<PlotMesh> &plot_meshes) {147MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node);148if (mi && mi->is_visible_in_tree()) {149if ((mi->get_layer_mask() & bake_mask) == 0) {150return;151}152153Ref<Mesh> mesh = mi->get_mesh();154if (mesh.is_valid()) {155AABB aabb = mesh->get_aabb();156157Transform3D xf = get_global_transform().affine_inverse() * mi->get_global_transform();158159if (p_aabb.intersects(xf.xform(aabb))) {160PlotMesh pm;161pm.local_xform = xf;162pm.mesh = mesh;163plot_meshes.push_back(pm);164}165}166}167168Node3D *s = Object::cast_to<Node3D>(p_at_node);169if (s) {170if (s->is_visible_in_tree()) {171Array meshes = p_at_node->call("get_meshes");172for (int i = 0; i < meshes.size(); i += 2) {173Transform3D mxf = meshes[i];174Ref<Mesh> mesh = meshes[i + 1];175if (mesh.is_null()) {176continue;177}178179AABB aabb = mesh->get_aabb();180181Transform3D xf = get_global_transform().affine_inverse() * (s->get_global_transform() * mxf);182183if (p_aabb.intersects(xf.xform(aabb))) {184PlotMesh pm;185pm.local_xform = xf;186pm.mesh = mesh;187plot_meshes.push_back(pm);188}189}190}191}192193for (int i = 0; i < p_at_node->get_child_count(); i++) {194Node *child = p_at_node->get_child(i);195_find_meshes(p_aabb, child, plot_meshes);196}197}198199uint32_t GPUParticlesCollisionSDF3D::_create_bvh(LocalVector<BVH> &bvh_tree, FacePos *p_faces, uint32_t p_face_count, const Face3 *p_triangles, float p_thickness) {200if (p_face_count == 1) {201return BVH::LEAF_BIT | p_faces[0].index;202}203204uint32_t index = bvh_tree.size();205{206BVH bvh;207208for (uint32_t i = 0; i < p_face_count; i++) {209const Face3 &f = p_triangles[p_faces[i].index];210AABB aabb(f.vertex[0], Vector3());211aabb.expand_to(f.vertex[1]);212aabb.expand_to(f.vertex[2]);213if (p_thickness > 0.0) {214Vector3 normal = p_triangles[p_faces[i].index].get_plane().normal;215aabb.expand_to(f.vertex[0] - normal * p_thickness);216aabb.expand_to(f.vertex[1] - normal * p_thickness);217aabb.expand_to(f.vertex[2] - normal * p_thickness);218}219if (i == 0) {220bvh.bounds = aabb;221} else {222bvh.bounds.merge_with(aabb);223}224}225bvh_tree.push_back(bvh);226}227228uint32_t middle = p_face_count / 2;229230SortArray<FacePos, FaceSort> s;231s.compare.axis = bvh_tree[index].bounds.get_longest_axis_index();232s.sort(p_faces, p_face_count);233234uint32_t left = _create_bvh(bvh_tree, p_faces, middle, p_triangles, p_thickness);235uint32_t right = _create_bvh(bvh_tree, p_faces + middle, p_face_count - middle, p_triangles, p_thickness);236237bvh_tree[index].children[0] = left;238bvh_tree[index].children[1] = right;239240return index;241}242243static _FORCE_INLINE_ real_t Vector3_dot2(const Vector3 &p_vec3) {244return p_vec3.dot(p_vec3);245}246247void 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) {248if (p_bvh_cell & BVH::LEAF_BIT) {249p_bvh_cell &= BVH::LEAF_MASK; //remove bit250251Vector3 point = p_pos;252Plane p = p_triangles[p_bvh_cell].get_plane();253float d = p.distance_to(point);254float inside_d = 1e20;255if (d < 0 && d > -p_thickness) {256//inside planes, do this in 2D257258Vector3 x_axis = (p_triangles[p_bvh_cell].vertex[0] - p_triangles[p_bvh_cell].vertex[1]).normalized();259Vector3 y_axis = p.normal.cross(x_axis).normalized();260261Vector2 points[3];262for (int i = 0; i < 3; i++) {263points[i] = Vector2(x_axis.dot(p_triangles[p_bvh_cell].vertex[i]), y_axis.dot(p_triangles[p_bvh_cell].vertex[i]));264}265266Vector2 p2d = Vector2(x_axis.dot(point), y_axis.dot(point));267268{269// https://www.shadertoy.com/view/XsXSz4270271Vector2 e0 = points[1] - points[0];272Vector2 e1 = points[2] - points[1];273Vector2 e2 = points[0] - points[2];274275Vector2 v0 = p2d - points[0];276Vector2 v1 = p2d - points[1];277Vector2 v2 = p2d - points[2];278279Vector2 pq0 = v0 - e0 * CLAMP(v0.dot(e0) / e0.dot(e0), 0.0, 1.0);280Vector2 pq1 = v1 - e1 * CLAMP(v1.dot(e1) / e1.dot(e1), 0.0, 1.0);281Vector2 pq2 = v2 - e2 * CLAMP(v2.dot(e2) / e2.dot(e2), 0.0, 1.0);282283float s = SIGN(e0.x * e2.y - e0.y * e2.x);284Vector2 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)));285286inside_d = -Math::sqrt(d2.x) * SIGN(d2.y);287}288289//make sure distance to planes is not shorter if inside290if (inside_d < 0) {291inside_d = MAX(inside_d, d);292inside_d = MAX(inside_d, -(p_thickness + d));293}294295r_closest_distance = MIN(r_closest_distance, inside_d);296} else {297if (d < 0) {298point -= p.normal * p_thickness; //flatten299}300301// https://iquilezles.org/www/articles/distfunctions/distfunctions.htm302Vector3 a = p_triangles[p_bvh_cell].vertex[0];303Vector3 b = p_triangles[p_bvh_cell].vertex[1];304Vector3 c = p_triangles[p_bvh_cell].vertex[2];305306Vector3 ba = b - a;307Vector3 pa = point - a;308Vector3 cb = c - b;309Vector3 pb = point - b;310Vector3 ac = a - c;311Vector3 pc = point - c;312Vector3 nor = ba.cross(ac);313314inside_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(317Vector3_dot2(ba * CLAMP(ba.dot(pa) / Vector3_dot2(ba), 0.0, 1.0) - pa),318Vector3_dot2(cb * CLAMP(cb.dot(pb) / Vector3_dot2(cb), 0.0, 1.0) - pb)),319Vector3_dot2(ac * CLAMP(ac.dot(pc) / Vector3_dot2(ac), 0.0, 1.0) - pc))320: nor.dot(pa) * nor.dot(pa) / Vector3_dot2(nor));321322r_closest_distance = MIN(r_closest_distance, inside_d);323}324325} else {326bool pass = true;327if (!p_bvh[p_bvh_cell].bounds.has_point(p_pos)) {328//outside, find closest point329Vector3 he = p_bvh[p_bvh_cell].bounds.size * 0.5;330Vector3 center = p_bvh[p_bvh_cell].bounds.position + he;331332Vector3 rel = (p_pos - center).abs();333Vector3 closest = rel.min(he);334float d = rel.distance_to(closest);335336if (d >= r_closest_distance) {337pass = false; //already closer than this aabb, discard338}339}340341if (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}347348void GPUParticlesCollisionSDF3D::_compute_sdf_z(uint32_t p_z, ComputeSDFParams *params) {349int32_t z_ofs = p_z * params->size.y * params->size.x;350for (int32_t y = 0; y < params->size.y; y++) {351int32_t y_ofs = z_ofs + y * params->size.x;352for (int32_t x = 0; x < params->size.x; x++) {353int32_t x_ofs = y_ofs + x;354float &cell = params->cells[x_ofs];355356Vector3 pos = params->cell_offset + Vector3(x, y, p_z) * params->cell_size;357358cell = 1e20;359360_find_closest_distance(pos, params->bvh, 0, params->triangles, params->thickness, cell);361}362}363}364365void GPUParticlesCollisionSDF3D::_compute_sdf(ComputeSDFParams *params) {366WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &GPUParticlesCollisionSDF3D::_compute_sdf_z, params, params->size.z);367while (!WorkerThreadPool::get_singleton()->is_group_task_completed(group_task)) {368OS::get_singleton()->delay_usec(10000);369if (bake_step_function) {370bake_step_function(WorkerThreadPool::get_singleton()->get_group_processed_element_count(group_task) * 100 / params->size.z, "Baking SDF");371}372}373WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);374}375376Vector3i GPUParticlesCollisionSDF3D::get_estimated_cell_size() const {377static const int subdivs[RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };378int subdiv = subdivs[get_resolution()];379380AABB aabb(-size / 2, size);381382float cell_size = aabb.get_longest_axis_size() / float(subdiv);383384Vector3i sdf_size = Vector3i(aabb.size / cell_size);385sdf_size = sdf_size.maxi(1);386return sdf_size;387}388389Ref<Image> GPUParticlesCollisionSDF3D::bake() {390static const int subdivs[RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };391int subdiv = subdivs[get_resolution()];392393AABB aabb(-size / 2, size);394395float cell_size = aabb.get_longest_axis_size() / float(subdiv);396397Vector3i sdf_size = Vector3i(aabb.size / cell_size);398sdf_size = sdf_size.maxi(1);399400if (bake_begin_function) {401bake_begin_function(100);402}403404aabb.size = Vector3(sdf_size) * cell_size;405406List<PlotMesh> plot_meshes;407_find_meshes(aabb, get_parent(), plot_meshes);408409LocalVector<Face3> faces;410411if (bake_step_function) {412bake_step_function(0, "Finding Meshes");413}414415for (const PlotMesh &pm : plot_meshes) {416for (int i = 0; i < pm.mesh->get_surface_count(); i++) {417if (pm.mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {418continue; //only triangles419}420421Array a = pm.mesh->surface_get_arrays(i);422423Vector<Vector3> vertices = a[Mesh::ARRAY_VERTEX];424const Vector3 *vr = vertices.ptr();425Vector<int> index = a[Mesh::ARRAY_INDEX];426427if (index.size()) {428int facecount = index.size() / 3;429const int *ir = index.ptr();430431for (int j = 0; j < facecount; j++) {432Face3 face;433434for (int k = 0; k < 3; k++) {435face.vertex[k] = pm.local_xform.xform(vr[ir[j * 3 + k]]);436}437438//test against original bounds439if (!Geometry3D::triangle_box_overlap(aabb.get_center(), aabb.size * 0.5, face.vertex)) {440continue;441}442443faces.push_back(face);444}445446} else {447int facecount = vertices.size() / 3;448449for (int j = 0; j < facecount; j++) {450Face3 face;451452for (int k = 0; k < 3; k++) {453face.vertex[k] = pm.local_xform.xform(vr[j * 3 + k]);454}455456//test against original bounds457if (!Geometry3D::triangle_box_overlap(aabb.get_center(), aabb.size * 0.5, face.vertex)) {458continue;459}460461faces.push_back(face);462}463}464}465}466467//compute bvh468if (faces.size() <= 1) {469ERR_PRINT("No faces detected during GPUParticlesCollisionSDF3D bake. Check whether there are visible meshes matching the bake mask within its extents.");470if (bake_end_function) {471bake_end_function();472}473return Ref<Image>();474}475476LocalVector<FacePos> face_pos;477478face_pos.resize(faces.size());479480float th = cell_size * thickness;481482for (uint32_t i = 0; i < faces.size(); i++) {483face_pos[i].index = i;484face_pos[i].center = (faces[i].vertex[0] + faces[i].vertex[1] + faces[i].vertex[2]) / 2;485if (th > 0.0) {486face_pos[i].center -= faces[i].get_plane().normal * th * 0.5;487}488}489490if (bake_step_function) {491bake_step_function(0, "Creating BVH");492}493494LocalVector<BVH> bvh;495496_create_bvh(bvh, face_pos.ptr(), face_pos.size(), faces.ptr(), th);497498Vector<uint8_t> cells_data;499cells_data.resize(sdf_size.z * sdf_size.y * sdf_size.x * (int)sizeof(float));500501if (bake_step_function) {502bake_step_function(0, "Baking SDF");503}504505ComputeSDFParams params;506params.cells = (float *)cells_data.ptrw();507params.size = sdf_size;508params.cell_size = cell_size;509params.cell_offset = aabb.position + Vector3(cell_size * 0.5, cell_size * 0.5, cell_size * 0.5);510params.bvh = bvh.ptr();511params.triangles = faces.ptr();512params.thickness = th;513_compute_sdf(¶ms);514515Ref<Image> ret = Image::create_from_data(sdf_size.x, sdf_size.y * sdf_size.z, false, Image::FORMAT_RF, cells_data);516ret->convert(Image::FORMAT_RH); //convert to half, save space517ret->set_meta("depth", sdf_size.z); //hack, make sure to add to the docs of this function518519if (bake_end_function) {520bake_end_function();521}522523return ret;524}525526PackedStringArray GPUParticlesCollisionSDF3D::get_configuration_warnings() const {527PackedStringArray warnings = GPUParticlesCollision3D::get_configuration_warnings();528529if (bake_mask == 0) {530warnings.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."));531}532533return warnings;534}535536void GPUParticlesCollisionSDF3D::_bind_methods() {537ClassDB::bind_method(D_METHOD("set_size", "size"), &GPUParticlesCollisionSDF3D::set_size);538ClassDB::bind_method(D_METHOD("get_size"), &GPUParticlesCollisionSDF3D::get_size);539540ClassDB::bind_method(D_METHOD("set_resolution", "resolution"), &GPUParticlesCollisionSDF3D::set_resolution);541ClassDB::bind_method(D_METHOD("get_resolution"), &GPUParticlesCollisionSDF3D::get_resolution);542543ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticlesCollisionSDF3D::set_texture);544ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticlesCollisionSDF3D::get_texture);545546ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &GPUParticlesCollisionSDF3D::set_thickness);547ClassDB::bind_method(D_METHOD("get_thickness"), &GPUParticlesCollisionSDF3D::get_thickness);548549ClassDB::bind_method(D_METHOD("set_bake_mask", "mask"), &GPUParticlesCollisionSDF3D::set_bake_mask);550ClassDB::bind_method(D_METHOD("get_bake_mask"), &GPUParticlesCollisionSDF3D::get_bake_mask);551ClassDB::bind_method(D_METHOD("set_bake_mask_value", "layer_number", "value"), &GPUParticlesCollisionSDF3D::set_bake_mask_value);552ClassDB::bind_method(D_METHOD("get_bake_mask_value", "layer_number"), &GPUParticlesCollisionSDF3D::get_bake_mask_value);553554ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_size", "get_size");555ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "16,32,64,128,256,512"), "set_resolution", "get_resolution");556ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "thickness", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,suffix:m"), "set_thickness", "get_thickness");557ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_bake_mask", "get_bake_mask");558ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, Texture3D::get_class_static()), "set_texture", "get_texture");559560BIND_ENUM_CONSTANT(RESOLUTION_16);561BIND_ENUM_CONSTANT(RESOLUTION_32);562BIND_ENUM_CONSTANT(RESOLUTION_64);563BIND_ENUM_CONSTANT(RESOLUTION_128);564BIND_ENUM_CONSTANT(RESOLUTION_256);565BIND_ENUM_CONSTANT(RESOLUTION_512);566BIND_ENUM_CONSTANT(RESOLUTION_MAX);567}568569#ifndef DISABLE_DEPRECATED570bool GPUParticlesCollisionSDF3D::_set(const StringName &p_name, const Variant &p_value) {571if (p_name == "extents") { // Compatibility with Godot 3.x.572set_size((Vector3)p_value * 2);573return true;574}575return false;576}577578bool GPUParticlesCollisionSDF3D::_get(const StringName &p_name, Variant &r_property) const {579if (p_name == "extents") { // Compatibility with Godot 3.x.580r_property = size / 2;581return true;582}583return false;584}585#endif // DISABLE_DEPRECATED586587void GPUParticlesCollisionSDF3D::set_thickness(float p_thickness) {588thickness = p_thickness;589}590591float GPUParticlesCollisionSDF3D::get_thickness() const {592return thickness;593}594595void GPUParticlesCollisionSDF3D::set_size(const Vector3 &p_size) {596size = p_size;597RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), size / 2);598update_gizmos();599}600601Vector3 GPUParticlesCollisionSDF3D::get_size() const {602return size;603}604605void GPUParticlesCollisionSDF3D::set_resolution(Resolution p_resolution) {606resolution = p_resolution;607update_gizmos();608}609610GPUParticlesCollisionSDF3D::Resolution GPUParticlesCollisionSDF3D::get_resolution() const {611return resolution;612}613614void GPUParticlesCollisionSDF3D::set_bake_mask(uint32_t p_mask) {615bake_mask = p_mask;616update_configuration_warnings();617}618619uint32_t GPUParticlesCollisionSDF3D::get_bake_mask() const {620return bake_mask;621}622623void GPUParticlesCollisionSDF3D::set_bake_mask_value(int p_layer_number, bool p_value) {624ERR_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));625uint32_t mask = get_bake_mask();626if (p_value) {627mask |= 1 << (p_layer_number - 1);628} else {629mask &= ~(1 << (p_layer_number - 1));630}631set_bake_mask(mask);632}633634bool GPUParticlesCollisionSDF3D::get_bake_mask_value(int p_layer_number) const {635ERR_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));636return bake_mask & (1 << (p_layer_number - 1));637}638639void GPUParticlesCollisionSDF3D::set_texture(const Ref<Texture3D> &p_texture) {640texture = p_texture;641RID tex = texture.is_valid() ? texture->get_rid() : RID();642RS::get_singleton()->particles_collision_set_field_texture(_get_collision(), tex);643}644645Ref<Texture3D> GPUParticlesCollisionSDF3D::get_texture() const {646return texture;647}648649AABB GPUParticlesCollisionSDF3D::get_aabb() const {650return AABB(-size / 2, size);651}652653GPUParticlesCollisionSDF3D::BakeBeginFunc GPUParticlesCollisionSDF3D::bake_begin_function = nullptr;654GPUParticlesCollisionSDF3D::BakeStepFunc GPUParticlesCollisionSDF3D::bake_step_function = nullptr;655GPUParticlesCollisionSDF3D::BakeEndFunc GPUParticlesCollisionSDF3D::bake_end_function = nullptr;656657GPUParticlesCollisionSDF3D::GPUParticlesCollisionSDF3D() :658GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_SDF_COLLIDE) {659}660661GPUParticlesCollisionSDF3D::~GPUParticlesCollisionSDF3D() {662}663664////////////////////////////665////////////////////////////666667void GPUParticlesCollisionHeightField3D::_notification(int p_what) {668switch (p_what) {669case NOTIFICATION_INTERNAL_PROCESS: {670if (update_mode == UPDATE_MODE_ALWAYS) {671RS::get_singleton()->particles_collision_height_field_update(_get_collision());672}673674if (follow_camera_mode && get_viewport()) {675Camera3D *cam = get_viewport()->get_camera_3d();676if (cam) {677Transform3D xform = get_global_transform();678Vector3 x_axis = xform.basis.get_column(Vector3::AXIS_X).normalized();679Vector3 z_axis = xform.basis.get_column(Vector3::AXIS_Z).normalized();680float x_len = xform.basis.get_scale().x;681float z_len = xform.basis.get_scale().z;682683Vector3 cam_pos = cam->get_global_transform().origin;684Transform3D new_xform = xform;685686while (x_axis.dot(cam_pos - new_xform.origin) > x_len) {687new_xform.origin += x_axis * x_len;688}689while (x_axis.dot(cam_pos - new_xform.origin) < -x_len) {690new_xform.origin -= x_axis * x_len;691}692693while (z_axis.dot(cam_pos - new_xform.origin) > z_len) {694new_xform.origin += z_axis * z_len;695}696while (z_axis.dot(cam_pos - new_xform.origin) < -z_len) {697new_xform.origin -= z_axis * z_len;698}699700if (new_xform != xform) {701set_global_transform(new_xform);702RS::get_singleton()->particles_collision_height_field_update(_get_collision());703}704}705}706} break;707708case NOTIFICATION_TRANSFORM_CHANGED: {709RS::get_singleton()->particles_collision_height_field_update(_get_collision());710} break;711}712}713714void GPUParticlesCollisionHeightField3D::_bind_methods() {715ClassDB::bind_method(D_METHOD("set_size", "size"), &GPUParticlesCollisionHeightField3D::set_size);716ClassDB::bind_method(D_METHOD("get_size"), &GPUParticlesCollisionHeightField3D::get_size);717718ClassDB::bind_method(D_METHOD("set_resolution", "resolution"), &GPUParticlesCollisionHeightField3D::set_resolution);719ClassDB::bind_method(D_METHOD("get_resolution"), &GPUParticlesCollisionHeightField3D::get_resolution);720721ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &GPUParticlesCollisionHeightField3D::set_update_mode);722ClassDB::bind_method(D_METHOD("get_update_mode"), &GPUParticlesCollisionHeightField3D::get_update_mode);723724ClassDB::bind_method(D_METHOD("set_heightfield_mask", "heightfield_mask"), &GPUParticlesCollisionHeightField3D::set_heightfield_mask);725ClassDB::bind_method(D_METHOD("get_heightfield_mask"), &GPUParticlesCollisionHeightField3D::get_heightfield_mask);726727ClassDB::bind_method(D_METHOD("set_heightfield_mask_value", "layer_number", "value"), &GPUParticlesCollisionHeightField3D::set_heightfield_mask_value);728ClassDB::bind_method(D_METHOD("get_heightfield_mask_value", "layer_number"), &GPUParticlesCollisionHeightField3D::get_heightfield_mask_value);729730ClassDB::bind_method(D_METHOD("set_follow_camera_enabled", "enabled"), &GPUParticlesCollisionHeightField3D::set_follow_camera_enabled);731ClassDB::bind_method(D_METHOD("is_follow_camera_enabled"), &GPUParticlesCollisionHeightField3D::is_follow_camera_enabled);732733ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_size", "get_size");734ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "256 (Fastest),512 (Fast),1024 (Average),2048 (Slow),4096 (Slower),8192 (Slowest)"), "set_resolution", "get_resolution");735ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "When Moved (Fast),Always (Slow)"), "set_update_mode", "get_update_mode");736ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled"), "set_follow_camera_enabled", "is_follow_camera_enabled");737ADD_PROPERTY(PropertyInfo(Variant::INT, "heightfield_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_heightfield_mask", "get_heightfield_mask");738739BIND_ENUM_CONSTANT(RESOLUTION_256);740BIND_ENUM_CONSTANT(RESOLUTION_512);741BIND_ENUM_CONSTANT(RESOLUTION_1024);742BIND_ENUM_CONSTANT(RESOLUTION_2048);743BIND_ENUM_CONSTANT(RESOLUTION_4096);744BIND_ENUM_CONSTANT(RESOLUTION_8192);745BIND_ENUM_CONSTANT(RESOLUTION_MAX);746747BIND_ENUM_CONSTANT(UPDATE_MODE_WHEN_MOVED);748BIND_ENUM_CONSTANT(UPDATE_MODE_ALWAYS);749}750751#ifndef DISABLE_DEPRECATED752bool GPUParticlesCollisionHeightField3D::_set(const StringName &p_name, const Variant &p_value) {753if (p_name == "extents") { // Compatibility with Godot 3.x.754set_size((Vector3)p_value * 2);755return true;756}757return false;758}759760bool GPUParticlesCollisionHeightField3D::_get(const StringName &p_name, Variant &r_property) const {761if (p_name == "extents") { // Compatibility with Godot 3.x.762r_property = size / 2;763return true;764}765return false;766}767#endif // DISABLE_DEPRECATED768769void GPUParticlesCollisionHeightField3D::set_size(const Vector3 &p_size) {770size = p_size;771RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), size / 2);772update_gizmos();773RS::get_singleton()->particles_collision_height_field_update(_get_collision());774}775776Vector3 GPUParticlesCollisionHeightField3D::get_size() const {777return size;778}779780void GPUParticlesCollisionHeightField3D::set_resolution(Resolution p_resolution) {781resolution = p_resolution;782RS::get_singleton()->particles_collision_set_height_field_resolution(_get_collision(), RS::ParticlesCollisionHeightfieldResolution(resolution));783update_gizmos();784RS::get_singleton()->particles_collision_height_field_update(_get_collision());785}786787GPUParticlesCollisionHeightField3D::Resolution GPUParticlesCollisionHeightField3D::get_resolution() const {788return resolution;789}790791void GPUParticlesCollisionHeightField3D::set_update_mode(UpdateMode p_update_mode) {792update_mode = p_update_mode;793set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS);794}795796GPUParticlesCollisionHeightField3D::UpdateMode GPUParticlesCollisionHeightField3D::get_update_mode() const {797return update_mode;798}799800void GPUParticlesCollisionHeightField3D::set_heightfield_mask(uint32_t p_heightfield_mask) {801heightfield_mask = p_heightfield_mask;802RS::get_singleton()->particles_collision_set_height_field_mask(_get_collision(), p_heightfield_mask);803}804805uint32_t GPUParticlesCollisionHeightField3D::get_heightfield_mask() const {806return heightfield_mask;807}808809void GPUParticlesCollisionHeightField3D::set_heightfield_mask_value(int p_layer_number, bool p_value) {810ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive.");811ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive.");812uint32_t mask = get_heightfield_mask();813if (p_value) {814mask |= 1 << (p_layer_number - 1);815} else {816mask &= ~(1 << (p_layer_number - 1));817}818set_heightfield_mask(mask);819}820821bool GPUParticlesCollisionHeightField3D::get_heightfield_mask_value(int p_layer_number) const {822ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Render layer number must be between 1 and 20 inclusive.");823ERR_FAIL_COND_V_MSG(p_layer_number > 20, false, "Render layer number must be between 1 and 20 inclusive.");824return heightfield_mask & (1 << (p_layer_number - 1));825}826827void GPUParticlesCollisionHeightField3D::set_follow_camera_enabled(bool p_enabled) {828follow_camera_mode = p_enabled;829set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS);830}831832bool GPUParticlesCollisionHeightField3D::is_follow_camera_enabled() const {833return follow_camera_mode;834}835836AABB GPUParticlesCollisionHeightField3D::get_aabb() const {837return AABB(-size / 2, size);838}839840GPUParticlesCollisionHeightField3D::GPUParticlesCollisionHeightField3D() :841GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE) {842}843844GPUParticlesCollisionHeightField3D::~GPUParticlesCollisionHeightField3D() {845}846847////////////////////////////848////////////////////////////849850void GPUParticlesAttractor3D::set_cull_mask(uint32_t p_cull_mask) {851cull_mask = p_cull_mask;852RS::get_singleton()->particles_collision_set_cull_mask(collision, p_cull_mask);853}854855uint32_t GPUParticlesAttractor3D::get_cull_mask() const {856return cull_mask;857}858859void GPUParticlesAttractor3D::set_strength(real_t p_strength) {860strength = p_strength;861RS::get_singleton()->particles_collision_set_attractor_strength(collision, p_strength);862}863864real_t GPUParticlesAttractor3D::get_strength() const {865return strength;866}867868void GPUParticlesAttractor3D::set_attenuation(real_t p_attenuation) {869attenuation = p_attenuation;870RS::get_singleton()->particles_collision_set_attractor_attenuation(collision, p_attenuation);871}872873real_t GPUParticlesAttractor3D::get_attenuation() const {874return attenuation;875}876877void GPUParticlesAttractor3D::set_directionality(real_t p_directionality) {878directionality = p_directionality;879RS::get_singleton()->particles_collision_set_attractor_directionality(collision, p_directionality);880update_gizmos();881}882883real_t GPUParticlesAttractor3D::get_directionality() const {884return directionality;885}886887void GPUParticlesAttractor3D::_bind_methods() {888ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &GPUParticlesAttractor3D::set_cull_mask);889ClassDB::bind_method(D_METHOD("get_cull_mask"), &GPUParticlesAttractor3D::get_cull_mask);890891ClassDB::bind_method(D_METHOD("set_strength", "strength"), &GPUParticlesAttractor3D::set_strength);892ClassDB::bind_method(D_METHOD("get_strength"), &GPUParticlesAttractor3D::get_strength);893894ClassDB::bind_method(D_METHOD("set_attenuation", "attenuation"), &GPUParticlesAttractor3D::set_attenuation);895ClassDB::bind_method(D_METHOD("get_attenuation"), &GPUParticlesAttractor3D::get_attenuation);896897ClassDB::bind_method(D_METHOD("set_directionality", "amount"), &GPUParticlesAttractor3D::set_directionality);898ClassDB::bind_method(D_METHOD("get_directionality"), &GPUParticlesAttractor3D::get_directionality);899900ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "-128,128,0.01,or_greater,or_less"), "set_strength", "get_strength");901ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation", PROPERTY_HINT_EXP_EASING, "0,8,0.01"), "set_attenuation", "get_attenuation");902ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "directionality", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_directionality", "get_directionality");903ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");904}905906GPUParticlesAttractor3D::GPUParticlesAttractor3D(RS::ParticlesCollisionType p_type) {907collision = RS::get_singleton()->particles_collision_create();908RS::get_singleton()->particles_collision_set_collision_type(collision, p_type);909set_base(collision);910}911GPUParticlesAttractor3D::~GPUParticlesAttractor3D() {912ERR_FAIL_NULL(RenderingServer::get_singleton());913RS::get_singleton()->free_rid(collision);914}915916/////////////////////////////////917918void GPUParticlesAttractorSphere3D::_bind_methods() {919ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesAttractorSphere3D::set_radius);920ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesAttractorSphere3D::get_radius);921922ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_radius", "get_radius");923}924925void GPUParticlesAttractorSphere3D::set_radius(real_t p_radius) {926radius = p_radius;927RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius);928update_gizmos();929}930931real_t GPUParticlesAttractorSphere3D::get_radius() const {932return radius;933}934935AABB GPUParticlesAttractorSphere3D::get_aabb() const {936return AABB(Vector3(-radius, -radius, -radius), Vector3(radius * 2, radius * 2, radius * 2));937}938939GPUParticlesAttractorSphere3D::GPUParticlesAttractorSphere3D() :940GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT) {941}942943GPUParticlesAttractorSphere3D::~GPUParticlesAttractorSphere3D() {944}945946///////////////////////////947948void GPUParticlesAttractorBox3D::_bind_methods() {949ClassDB::bind_method(D_METHOD("set_size", "size"), &GPUParticlesAttractorBox3D::set_size);950ClassDB::bind_method(D_METHOD("get_size"), &GPUParticlesAttractorBox3D::get_size);951952ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_size", "get_size");953}954955#ifndef DISABLE_DEPRECATED956bool GPUParticlesAttractorBox3D::_set(const StringName &p_name, const Variant &p_value) {957if (p_name == "extents") { // Compatibility with Godot 3.x.958set_size((Vector3)p_value * 2);959return true;960}961return false;962}963964bool GPUParticlesAttractorBox3D::_get(const StringName &p_name, Variant &r_property) const {965if (p_name == "extents") { // Compatibility with Godot 3.x.966r_property = size / 2;967return true;968}969return false;970}971#endif // DISABLE_DEPRECATED972973void GPUParticlesAttractorBox3D::set_size(const Vector3 &p_size) {974size = p_size;975RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), size / 2);976update_gizmos();977}978979Vector3 GPUParticlesAttractorBox3D::get_size() const {980return size;981}982983AABB GPUParticlesAttractorBox3D::get_aabb() const {984return AABB(-size / 2, size);985}986987GPUParticlesAttractorBox3D::GPUParticlesAttractorBox3D() :988GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_BOX_ATTRACT) {989}990991GPUParticlesAttractorBox3D::~GPUParticlesAttractorBox3D() {992}993994///////////////////////////995996void GPUParticlesAttractorVectorField3D::_bind_methods() {997ClassDB::bind_method(D_METHOD("set_size", "size"), &GPUParticlesAttractorVectorField3D::set_size);998ClassDB::bind_method(D_METHOD("get_size"), &GPUParticlesAttractorVectorField3D::get_size);9991000ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticlesAttractorVectorField3D::set_texture);1001ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticlesAttractorVectorField3D::get_texture);10021003ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_size", "get_size");1004ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, Texture3D::get_class_static()), "set_texture", "get_texture");1005}10061007#ifndef DISABLE_DEPRECATED1008bool GPUParticlesAttractorVectorField3D::_set(const StringName &p_name, const Variant &p_value) {1009if (p_name == "extents") { // Compatibility with Godot 3.x.1010set_size((Vector3)p_value * 2);1011return true;1012}1013return false;1014}10151016bool GPUParticlesAttractorVectorField3D::_get(const StringName &p_name, Variant &r_property) const {1017if (p_name == "extents") { // Compatibility with Godot 3.x.1018r_property = size / 2;1019return true;1020}1021return false;1022}1023#endif // DISABLE_DEPRECATED10241025void GPUParticlesAttractorVectorField3D::set_size(const Vector3 &p_size) {1026size = p_size;1027RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), size / 2);1028update_gizmos();1029}10301031Vector3 GPUParticlesAttractorVectorField3D::get_size() const {1032return size;1033}10341035void GPUParticlesAttractorVectorField3D::set_texture(const Ref<Texture3D> &p_texture) {1036texture = p_texture;1037RID tex = texture.is_valid() ? texture->get_rid() : RID();1038RS::get_singleton()->particles_collision_set_field_texture(_get_collision(), tex);1039}10401041Ref<Texture3D> GPUParticlesAttractorVectorField3D::get_texture() const {1042return texture;1043}10441045AABB GPUParticlesAttractorVectorField3D::get_aabb() const {1046return AABB(-size / 2, size);1047}10481049GPUParticlesAttractorVectorField3D::GPUParticlesAttractorVectorField3D() :1050GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT) {1051}10521053GPUParticlesAttractorVectorField3D::~GPUParticlesAttractorVectorField3D() {1054}105510561057