diff --git a/doc/classes/GPUParticlesCollisionHeightField3D.xml b/doc/classes/GPUParticlesCollisionHeightField3D.xml index 0b9a94af633..04bfbdb6fd5 100644 --- a/doc/classes/GPUParticlesCollisionHeightField3D.xml +++ b/doc/classes/GPUParticlesCollisionHeightField3D.xml @@ -12,11 +12,33 @@ + + + + + + Returns [code]true[/code] if the specified layer of the [member heightfield_mask] is enabled, given a [param layer_number] between [code]1[/code] and [code]20[/code], inclusive. + + + + + + + + Based on [param value], enables or disables the specified layer in the [member heightfield_mask], given a [param layer_number] between [code]1[/code] and [code]20[/code], inclusive. + + + If [code]true[/code], the [GPUParticlesCollisionHeightField3D] will follow the current camera in global space. The [GPUParticlesCollisionHeightField3D] does not need to be a child of the [Camera3D] node for this to work. Following the camera has a performance cost, as it will force the heightmap to update whenever the camera moves. Consider lowering [member resolution] to improve performance if [member follow_camera_enabled] is [code]true[/code]. + + The visual layers to account for when updating the heightmap. Only [MeshInstance3D]s whose [member VisualInstance3D.layers] match with this [member heightfield_mask] will be included in the heightmap collision update. By default, all 20 user-visible layers are taken into account for updating the heightmap collision. + [b]Note:[/b] Since the [member heightfield_mask] allows for 32 layers to be stored in total, there are an additional 12 layers that are only used internally by the engine and aren't exposed in the editor. Setting [member heightfield_mask] using a script allows you to toggle those reserved layers, which can be useful for editor plugins. + To adjust [member heightfield_mask] more easily using a script, use [method get_heightfield_mask_value] and [method set_heightfield_mask_value]. + Higher resolutions can represent small details more accurately in large scenes, at the cost of lower performance. If [member update_mode] is [constant UPDATE_MODE_ALWAYS], consider using the lowest resolution possible. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index fd16a665096..f968c302f4c 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -2893,6 +2893,14 @@ Sets the signed distance field [param texture] for the 3D GPU particles collision specified by the [param particles_collision] RID. Equivalent to [member GPUParticlesCollisionSDF3D.texture] or [member GPUParticlesAttractorVectorField3D.texture] depending on the [param particles_collision] type. + + + + + + Sets the heightfield [param mask] for the 3D GPU particles heightfield collision specified by the [param particles_collision] RID. Equivalent to [member GPUParticlesCollisionHeightField3D.heightfield_mask]. + + diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp index 2ee8bad9447..73d7e340d2a 100644 --- a/drivers/gles3/storage/particles_storage.cpp +++ b/drivers/gles3/storage/particles_storage.cpp @@ -1432,6 +1432,18 @@ bool ParticlesStorage::particles_collision_is_heightfield(RID p_particles_collis return particles_collision->type == RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE; } +uint32_t ParticlesStorage::particles_collision_get_height_field_mask(RID p_particles_collision) const { + const ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_NULL_V(particles_collision, false); + return particles_collision->heightfield_mask; +} + +void ParticlesStorage::particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_NULL(particles_collision); + particles_collision->heightfield_mask = p_heightfield_mask; +} + Dependency *ParticlesStorage::particles_collision_get_dependency(RID p_particles_collision) const { ParticlesCollision *pc = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_NULL_V(pc, nullptr); diff --git a/drivers/gles3/storage/particles_storage.h b/drivers/gles3/storage/particles_storage.h index 8d20d44935e..382364abd6d 100644 --- a/drivers/gles3/storage/particles_storage.h +++ b/drivers/gles3/storage/particles_storage.h @@ -287,6 +287,7 @@ class ParticlesStorage : public RendererParticlesStorage { GLuint heightfield_texture = 0; GLuint heightfield_fb = 0; Size2i heightfield_fb_size; + uint32_t heightfield_mask = (1 << 20) - 1; RS::ParticlesCollisionHeightfieldResolution heightfield_resolution = RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_1024; @@ -434,6 +435,8 @@ class ParticlesStorage : public RendererParticlesStorage { Vector3 particles_collision_get_extents(RID p_particles_collision) const; virtual bool particles_collision_is_heightfield(RID p_particles_collision) const override; GLuint particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const; + virtual uint32_t particles_collision_get_height_field_mask(RID p_particles_collision) const override; + virtual void particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) override; _FORCE_INLINE_ Size2i particles_collision_get_heightfield_size(RID p_particles_collision) const { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index c6dc9083669..aa6f4650b2a 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -721,6 +721,12 @@ void GPUParticlesCollisionHeightField3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &GPUParticlesCollisionHeightField3D::set_update_mode); ClassDB::bind_method(D_METHOD("get_update_mode"), &GPUParticlesCollisionHeightField3D::get_update_mode); + ClassDB::bind_method(D_METHOD("set_heightfield_mask", "heightfield_mask"), &GPUParticlesCollisionHeightField3D::set_heightfield_mask); + ClassDB::bind_method(D_METHOD("get_heightfield_mask"), &GPUParticlesCollisionHeightField3D::get_heightfield_mask); + + ClassDB::bind_method(D_METHOD("set_heightfield_mask_value", "layer_number", "value"), &GPUParticlesCollisionHeightField3D::set_heightfield_mask_value); + ClassDB::bind_method(D_METHOD("get_heightfield_mask_value", "layer_number"), &GPUParticlesCollisionHeightField3D::get_heightfield_mask_value); + ClassDB::bind_method(D_METHOD("set_follow_camera_enabled", "enabled"), &GPUParticlesCollisionHeightField3D::set_follow_camera_enabled); ClassDB::bind_method(D_METHOD("is_follow_camera_enabled"), &GPUParticlesCollisionHeightField3D::is_follow_camera_enabled); @@ -728,6 +734,7 @@ void GPUParticlesCollisionHeightField3D::_bind_methods() { 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"); ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "When Moved (Fast),Always (Slow)"), "set_update_mode", "get_update_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled"), "set_follow_camera_enabled", "is_follow_camera_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "heightfield_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_heightfield_mask", "get_heightfield_mask"); BIND_ENUM_CONSTANT(RESOLUTION_256); BIND_ENUM_CONSTANT(RESOLUTION_512); @@ -790,6 +797,33 @@ GPUParticlesCollisionHeightField3D::UpdateMode GPUParticlesCollisionHeightField3 return update_mode; } +void GPUParticlesCollisionHeightField3D::set_heightfield_mask(uint32_t p_heightfield_mask) { + heightfield_mask = p_heightfield_mask; + RS::get_singleton()->particles_collision_set_height_field_mask(_get_collision(), p_heightfield_mask); +} + +uint32_t GPUParticlesCollisionHeightField3D::get_heightfield_mask() const { + return heightfield_mask; +} + +void GPUParticlesCollisionHeightField3D::set_heightfield_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive."); + uint32_t mask = get_heightfield_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); + } else { + mask &= ~(1 << (p_layer_number - 1)); + } + set_heightfield_mask(mask); +} + +bool GPUParticlesCollisionHeightField3D::get_heightfield_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 20, false, "Render layer number must be between 1 and 20 inclusive."); + return heightfield_mask & (1 << (p_layer_number - 1)); +} + void GPUParticlesCollisionHeightField3D::set_follow_camera_enabled(bool p_enabled) { follow_camera_mode = p_enabled; set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS); diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h index 16493200691..3a21851ff9d 100644 --- a/scene/3d/gpu_particles_collision_3d.h +++ b/scene/3d/gpu_particles_collision_3d.h @@ -225,6 +225,7 @@ class GPUParticlesCollisionHeightField3D : public GPUParticlesCollision3D { }; private: + uint32_t heightfield_mask = (1 << 20) - 1; // Only the first 20 bits are set by default to ignore editor layers. Vector3 size = Vector3(2, 2, 2); Resolution resolution = RESOLUTION_1024; bool follow_camera_mode = false; @@ -249,6 +250,12 @@ class GPUParticlesCollisionHeightField3D : public GPUParticlesCollision3D { void set_update_mode(UpdateMode p_update_mode); UpdateMode get_update_mode() const; + void set_heightfield_mask(uint32_t p_heightfield_mask); + uint32_t get_heightfield_mask() const; + + void set_heightfield_mask_value(int p_layer_number, bool p_value); + bool get_heightfield_mask_value(int p_layer_number) const; + void set_follow_camera_enabled(bool p_enabled); bool is_follow_camera_enabled() const; diff --git a/servers/rendering/dummy/storage/particles_storage.h b/servers/rendering/dummy/storage/particles_storage.h index 03160ea2d8e..1b55b2afc79 100644 --- a/servers/rendering/dummy/storage/particles_storage.h +++ b/servers/rendering/dummy/storage/particles_storage.h @@ -114,6 +114,8 @@ class ParticlesStorage : public RendererParticlesStorage { virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) override {} virtual AABB particles_collision_get_aabb(RID p_particles_collision) const override { return AABB(); } virtual bool particles_collision_is_heightfield(RID p_particles_collision) const override { return false; } + virtual uint32_t particles_collision_get_height_field_mask(RID p_particles_collision) const override { return 0; } + virtual void particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) override {} virtual RID particles_collision_instance_create(RID p_collision) override { return RID(); } virtual void particles_collision_instance_free(RID p_rid) override {} diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp index 6bb7eff83cf..7b36e768b71 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp @@ -1857,6 +1857,18 @@ void ParticlesStorage::particles_collision_set_cull_mask(RID p_particles_collisi particles_collision->cull_mask = p_cull_mask; } +uint32_t ParticlesStorage::particles_collision_get_height_field_mask(RID p_particles_collision) const { + const ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_NULL_V(particles_collision, false); + return particles_collision->heightfield_mask; +} + +void ParticlesStorage::particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) { + ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); + ERR_FAIL_NULL(particles_collision); + particles_collision->heightfield_mask = p_heightfield_mask; +} + void ParticlesStorage::particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) { ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision); ERR_FAIL_NULL(particles_collision); diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.h b/servers/rendering/renderer_rd/storage_rd/particles_storage.h index 27be125bfa2..9c46968f640 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.h @@ -404,6 +404,7 @@ class ParticlesStorage : public RendererParticlesStorage { RID heightfield_texture; RID heightfield_fb; Size2i heightfield_fb_size; + uint32_t heightfield_mask = (1 << 20) - 1; RS::ParticlesCollisionHeightfieldResolution heightfield_resolution = RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_1024; @@ -581,6 +582,8 @@ class ParticlesStorage : public RendererParticlesStorage { Vector3 particles_collision_get_extents(RID p_particles_collision) const; virtual bool particles_collision_is_heightfield(RID p_particles_collision) const override; RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const; + virtual uint32_t particles_collision_get_height_field_mask(RID p_particles_collision) const override; + virtual void particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) override; Dependency *particles_collision_get_dependency(RID p_particles) const; diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 0b98fc6769e..fefe225e243 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -3971,15 +3971,19 @@ void RendererSceneCull::render_particle_colliders() { struct CullAABB { PagedArray *result; + uint32_t heightfield_mask; _FORCE_INLINE_ bool operator()(void *p_data) { Instance *p_instance = (Instance *)p_data; - result->push_back(p_instance); + if (p_instance->layer_mask & heightfield_mask) { + result->push_back(p_instance); + } return false; } }; CullAABB cull_aabb; cull_aabb.result = &instance_cull_result; + cull_aabb.heightfield_mask = RSG::particles_storage->particles_collision_get_height_field_mask(hfpc->base); hfpc->scenario->indexers[Scenario::INDEXER_GEOMETRY].aabb_query(hfpc->transformed_aabb, cull_aabb); hfpc->scenario->indexers[Scenario::INDEXER_VOLUMES].aabb_query(hfpc->transformed_aabb, cull_aabb); diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 8e5faaf8381..ffbc5c70950 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -618,6 +618,7 @@ class RenderingServerDefault : public RenderingServer { FUNC2(particles_collision_set_attractor_attenuation, RID, real_t) FUNC2(particles_collision_set_field_texture, RID, RID) FUNC1(particles_collision_height_field_update, RID) + FUNC2(particles_collision_set_height_field_mask, RID, uint32_t) FUNC2(particles_collision_set_height_field_resolution, RID, ParticlesCollisionHeightfieldResolution) /* FOG VOLUME */ diff --git a/servers/rendering/storage/particles_storage.h b/servers/rendering/storage/particles_storage.h index 7543b05f5c5..6208aceed5d 100644 --- a/servers/rendering/storage/particles_storage.h +++ b/servers/rendering/storage/particles_storage.h @@ -120,6 +120,8 @@ class RendererParticlesStorage { virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) = 0; //for SDF and vector field virtual AABB particles_collision_get_aabb(RID p_particles_collision) const = 0; virtual bool particles_collision_is_heightfield(RID p_particles_collision) const = 0; + virtual uint32_t particles_collision_get_height_field_mask(RID p_particles_collision) const = 0; + virtual void particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) = 0; //used from 2D and 3D virtual RID particles_collision_instance_create(RID p_collision) = 0; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 422faa13a09..36229dc1dfd 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2745,6 +2745,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("particles_collision_height_field_update", "particles_collision"), &RenderingServer::particles_collision_height_field_update); ClassDB::bind_method(D_METHOD("particles_collision_set_height_field_resolution", "particles_collision", "resolution"), &RenderingServer::particles_collision_set_height_field_resolution); + ClassDB::bind_method(D_METHOD("particles_collision_set_height_field_mask", "particles_collision", "mask"), &RenderingServer::particles_collision_set_height_field_mask); BIND_ENUM_CONSTANT(PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT); BIND_ENUM_CONSTANT(PARTICLES_COLLISION_TYPE_BOX_ATTRACT); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 4880da43933..f889cce0482 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -857,6 +857,7 @@ class RenderingServer : public Object { }; virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, ParticlesCollisionHeightfieldResolution p_resolution) = 0; // For SDF and vector field. + virtual void particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) = 0; /* FOG VOLUME API */