diff options
Diffstat (limited to 'scene/3d')
-rw-r--r-- | scene/3d/cpu_particles_3d.cpp | 1 | ||||
-rw-r--r-- | scene/3d/light_3d.cpp | 15 | ||||
-rw-r--r-- | scene/3d/light_3d.h | 4 | ||||
-rw-r--r-- | scene/3d/lightmap_gi.cpp | 15 | ||||
-rw-r--r-- | scene/3d/mesh_instance_3d.cpp | 4 | ||||
-rw-r--r-- | scene/3d/skeleton_3d.cpp | 143 | ||||
-rw-r--r-- | scene/3d/skeleton_3d.h | 14 |
7 files changed, 169 insertions, 27 deletions
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index acbc443a93..5b84bf903f 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -1479,6 +1479,7 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_mesh"), &CPUParticles3D::get_mesh); ClassDB::bind_method(D_METHOD("restart"), &CPUParticles3D::restart); + ClassDB::bind_method(D_METHOD("capture_aabb"), &CPUParticles3D::capture_aabb); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 7b70986adc..2d18e62b10 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -146,6 +146,15 @@ bool Light3D::get_shadow_reverse_cull_face() const { return reverse_cull; } +void Light3D::set_shadow_caster_mask(uint32_t p_caster_mask) { + shadow_caster_mask = p_caster_mask; + RS::get_singleton()->light_set_shadow_caster_mask(light, shadow_caster_mask); +} + +uint32_t Light3D::get_shadow_caster_mask() const { + return shadow_caster_mask; +} + AABB Light3D::get_aabb() const { if (type == RenderingServer::LIGHT_DIRECTIONAL) { return AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2)); @@ -300,7 +309,7 @@ bool Light3D::is_editor_only() const { } void Light3D::_validate_property(PropertyInfo &p_property) const { - if (!shadow && (p_property.name == "shadow_bias" || p_property.name == "shadow_normal_bias" || p_property.name == "shadow_reverse_cull_face" || p_property.name == "shadow_transmittance_bias" || p_property.name == "shadow_opacity" || p_property.name == "shadow_blur" || p_property.name == "distance_fade_shadow")) { + if (!shadow && (p_property.name == "shadow_bias" || p_property.name == "shadow_normal_bias" || p_property.name == "shadow_reverse_cull_face" || p_property.name == "shadow_transmittance_bias" || p_property.name == "shadow_opacity" || p_property.name == "shadow_blur" || p_property.name == "distance_fade_shadow" || p_property.name == "shadow_caster_mask")) { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } @@ -354,6 +363,9 @@ void Light3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shadow_reverse_cull_face", "enable"), &Light3D::set_shadow_reverse_cull_face); ClassDB::bind_method(D_METHOD("get_shadow_reverse_cull_face"), &Light3D::get_shadow_reverse_cull_face); + ClassDB::bind_method(D_METHOD("set_shadow_caster_mask", "caster_mask"), &Light3D::set_shadow_caster_mask); + ClassDB::bind_method(D_METHOD("get_shadow_caster_mask"), &Light3D::get_shadow_caster_mask); + ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &Light3D::set_bake_mode); ClassDB::bind_method(D_METHOD("get_bake_mode"), &Light3D::get_bake_mode); @@ -388,6 +400,7 @@ void Light3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_transmittance_bias", PROPERTY_HINT_RANGE, "-16,16,0.001"), "set_param", "get_param", PARAM_TRANSMITTANCE_BIAS); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_opacity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_OPACITY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_BLUR); + ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_caster_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_shadow_caster_mask", "get_shadow_caster_mask"); ADD_GROUP("Distance Fade", "distance_fade_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_fade_enabled"), "set_enable_distance_fade", "is_distance_fade_enabled"); diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index d6eca8d8b6..5f549469c6 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -75,6 +75,7 @@ private: bool negative = false; bool reverse_cull = false; uint32_t cull_mask = 0; + uint32_t shadow_caster_mask = 0xFFFFFFFF; bool distance_fade_enabled = false; real_t distance_fade_begin = 40.0; real_t distance_fade_shadow = 50.0; @@ -136,6 +137,9 @@ public: void set_shadow_reverse_cull_face(bool p_enable); bool get_shadow_reverse_cull_face() const; + void set_shadow_caster_mask(uint32_t p_caster_mask); + uint32_t get_shadow_caster_mask() const; + void set_bake_mode(BakeMode p_mode); BakeMode get_bake_mode() const; diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index a1f32fccbe..012ef7860d 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -1602,19 +1602,16 @@ Ref<CameraAttributes> LightmapGI::get_camera_attributes() const { PackedStringArray LightmapGI::get_configuration_warnings() const { PackedStringArray warnings = VisualInstance3D::get_configuration_warnings(); -#ifndef MODULE_LIGHTMAPPER_RD_ENABLED -#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED) - warnings.push_back(vformat(RTR("Lightmaps cannot be baked on %s. Rendering existing baked lightmaps will still work."), OS::get_singleton()->get_name())); -#else - warnings.push_back(RTR("Lightmaps cannot be baked, as the `lightmapper_rd` module was disabled at compile-time. Rendering existing baked lightmaps will still work.")); -#endif - return warnings; -#endif - +#ifdef MODULE_LIGHTMAPPER_RD_ENABLED if (!DisplayServer::get_singleton()->can_create_rendering_device()) { warnings.push_back(vformat(RTR("Lightmaps can only be baked from a GPU that supports the RenderingDevice backends.\nYour GPU (%s) does not support RenderingDevice, as it does not support Vulkan, Direct3D 12, or Metal.\nLightmap baking will not be available on this device, although rendering existing baked lightmaps will work."), RenderingServer::get_singleton()->get_video_adapter_name())); return warnings; } +#elif defined(ANDROID_ENABLED) || defined(IOS_ENABLED) + warnings.push_back(vformat(RTR("Lightmaps cannot be baked on %s. Rendering existing baked lightmaps will still work."), OS::get_singleton()->get_name())); +#else + warnings.push_back(RTR("Lightmaps cannot be baked, as the `lightmapper_rd` module was disabled at compile-time. Rendering existing baked lightmaps will still work.")); +#endif return warnings; } diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index e8166802f8..f551cb401c 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -332,8 +332,8 @@ void MeshInstance3D::create_multiple_convex_collisions(const Ref<MeshConvexDecom void MeshInstance3D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_READY: { - callable_mp(this, &MeshInstance3D::_resolve_skeleton_path).call_deferred(); + case NOTIFICATION_ENTER_TREE: { + _resolve_skeleton_path(); } break; case NOTIFICATION_TRANSLATION_CHANGED: { if (mesh.is_valid()) { diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index f778fccf09..c1c992588b 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -276,6 +276,8 @@ void Skeleton3D::_update_process_order() { concatenated_bone_names = StringName(); + _update_bones_nested_set(); + process_order_dirty = false; emit_signal("bone_list_changed"); @@ -324,6 +326,8 @@ void Skeleton3D::_notification(int p_what) { #ifndef DISABLE_DEPRECATED setup_simulator(); #endif // _DISABLE_DEPRECATED + update_flags = UPDATE_FLAG_POSE; + _notification(NOTIFICATION_UPDATE_SKELETON); } break; case NOTIFICATION_UPDATE_SKELETON: { // Update bone transforms to apply unprocessed poses. @@ -334,6 +338,8 @@ void Skeleton3D::_notification(int p_what) { Bone *bonesptr = bones.ptr(); int len = bones.size(); + thread_local LocalVector<bool> bone_global_pose_dirty_backup; + // Process modifiers. _find_modifiers(); if (!modifiers.is_empty()) { @@ -341,6 +347,9 @@ void Skeleton3D::_notification(int p_what) { for (uint32_t i = 0; i < bones.size(); i++) { bones_backup[i].save(bones[i]); } + // Store dirty flags for global bone poses. + bone_global_pose_dirty_backup = bone_global_pose_dirty; + _process_modifiers(); } @@ -415,6 +424,8 @@ void Skeleton3D::_notification(int p_what) { for (uint32_t i = 0; i < bones.size(); i++) { bones_backup[i].restore(bones[i]); } + // Restore dirty flags for global bone poses. + bone_global_pose_dirty = bone_global_pose_dirty_backup; } updating = false; @@ -457,10 +468,111 @@ void Skeleton3D::_make_modifiers_dirty() { _update_deferred(UPDATE_FLAG_MODIFIER); } +void Skeleton3D::_update_bones_nested_set() { + nested_set_offset_to_bone_index.resize(bones.size()); + bone_global_pose_dirty.resize(bones.size()); + _make_bone_global_poses_dirty(); + + int offset = 0; + for (int bone : parentless_bones) { + offset += _update_bone_nested_set(bone, offset); + } +} + +int Skeleton3D::_update_bone_nested_set(int p_bone, int p_offset) { + Bone &bone = bones[p_bone]; + int offset = p_offset + 1; + int span = 1; + + for (int child_bone : bone.child_bones) { + int subspan = _update_bone_nested_set(child_bone, offset); + offset += subspan; + span += subspan; + } + + nested_set_offset_to_bone_index[p_offset] = p_bone; + bone.nested_set_offset = p_offset; + bone.nested_set_span = span; + + return span; +} + +void Skeleton3D::_make_bone_global_poses_dirty() { + for (uint32_t i = 0; i < bone_global_pose_dirty.size(); i++) { + bone_global_pose_dirty[i] = true; + } +} + +void Skeleton3D::_make_bone_global_pose_subtree_dirty(int p_bone) { + if (process_order_dirty) { + return; + } + + const Bone &bone = bones[p_bone]; + int span_offset = bone.nested_set_offset; + // No need to make subtree dirty when bone is already dirty. + if (bone_global_pose_dirty[span_offset]) { + return; + } + + // Make global poses of subtree dirty. + int span_end = span_offset + bone.nested_set_span; + for (int i = span_offset; i < span_end; i++) { + bone_global_pose_dirty[i] = true; + } +} + +void Skeleton3D::_update_bone_global_pose(int p_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + + _update_process_order(); + + // Global pose is already calculated. + int nested_set_offset = bones[p_bone].nested_set_offset; + if (!bone_global_pose_dirty[nested_set_offset]) { + return; + } + + thread_local LocalVector<int> bone_list; + bone_list.clear(); + Transform3D global_pose; + + // Create list of parent bones for which the global pose needs to be recalculated. + for (int bone = p_bone; bone >= 0; bone = bones[bone].parent) { + int offset = bones[bone].nested_set_offset; + // Stop searching when global pose is not dirty. + if (!bone_global_pose_dirty[offset]) { + global_pose = bones[bone].global_pose; + break; + } + + bone_list.push_back(bone); + } + + // Calculate global poses for all parent bones and the current bone. + for (int i = bone_list.size() - 1; i >= 0; i--) { + int bone_idx = bone_list[i]; + Bone &bone = bones[bone_idx]; + bool bone_enabled = bone.enabled && !show_rest_only; + Transform3D bone_pose = bone_enabled ? get_bone_pose(bone_idx) : get_bone_rest(bone_idx); + + global_pose *= bone_pose; +#ifndef DISABLE_DEPRECATED + if (bone.global_pose_override_amount >= CMP_EPSILON) { + global_pose = global_pose.interpolate_with(bone.global_pose_override, bone.global_pose_override_amount); + } +#endif // _DISABLE_DEPRECATED + + bone.global_pose = global_pose; + bone_global_pose_dirty[bone.nested_set_offset] = false; + } +} + Transform3D Skeleton3D::get_bone_global_pose(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - const_cast<Skeleton3D *>(this)->force_update_all_dirty_bones(); + const_cast<Skeleton3D *>(this)->_update_bone_global_pose(p_bone); return bones[p_bone].global_pose; } @@ -672,6 +784,7 @@ void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) { bones[p_bone].rest = p_rest; rest_dirty = true; _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } Transform3D Skeleton3D::get_bone_rest(int p_bone) const { const int bone_size = bones.size(); @@ -695,6 +808,7 @@ void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) { bones[p_bone].enabled = p_enabled; emit_signal(SceneStringName(bone_enabled_changed), p_bone); _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } bool Skeleton3D::is_bone_enabled(int p_bone) const { @@ -707,6 +821,7 @@ void Skeleton3D::set_show_rest_only(bool p_enabled) { show_rest_only = p_enabled; emit_signal(SceneStringName(show_rest_only_changed)); _make_dirty(); + _make_bone_global_poses_dirty(); } bool Skeleton3D::is_show_rest_only() const { @@ -733,6 +848,7 @@ void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) { bones[p_bone].pose_cache_dirty = true; if (is_inside_tree()) { _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } } @@ -744,6 +860,7 @@ void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) { bones[p_bone].pose_cache_dirty = true; if (is_inside_tree()) { _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } } void Skeleton3D::set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation) { @@ -754,6 +871,7 @@ void Skeleton3D::set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation bones[p_bone].pose_cache_dirty = true; if (is_inside_tree()) { _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } } void Skeleton3D::set_bone_pose_scale(int p_bone, const Vector3 &p_scale) { @@ -764,6 +882,7 @@ void Skeleton3D::set_bone_pose_scale(int p_bone, const Vector3 &p_scale) { bones[p_bone].pose_cache_dirty = true; if (is_inside_tree()) { _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } } @@ -938,14 +1057,14 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { ERR_FAIL_INDEX(p_bone_idx, bone_size); Bone *bonesptr = bones.ptr(); - thread_local LocalVector<int> bones_to_process; - bones_to_process.clear(); - bones_to_process.push_back(p_bone_idx); - uint32_t index = 0; - while (index < bones_to_process.size()) { - int current_bone_idx = bones_to_process[index]; + // Loop through nested set. + for (int offset = 0; offset < bone_size; offset++) { + if (!bone_global_pose_dirty[offset]) { + continue; + } + int current_bone_idx = nested_set_offset_to_bone_index[offset]; Bone &b = bonesptr[current_bone_idx]; bool bone_enabled = b.enabled && !show_rest_only; @@ -992,13 +1111,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { } #endif // _DISABLE_DEPRECATED - // Add the bone's children to the list of bones to be processed. - int child_bone_size = b.child_bones.size(); - for (int i = 0; i < child_bone_size; i++) { - bones_to_process.push_back(b.child_bones[i]); - } - - index++; + bone_global_pose_dirty[offset] = false; } } @@ -1176,6 +1289,7 @@ void Skeleton3D::clear_bones_global_pose_override() { bones[i].global_pose_override_reset = true; } _make_dirty(); + _make_bone_global_poses_dirty(); } void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) { @@ -1185,6 +1299,7 @@ void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_ bones[p_bone].global_pose_override = p_pose; bones[p_bone].global_pose_override_reset = !p_persistent; _make_dirty(); + _make_bone_global_pose_subtree_dirty(p_bone); } Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const { diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 7213dc580c..ecfe095f1d 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -31,6 +31,7 @@ #ifndef SKELETON_3D_H #define SKELETON_3D_H +#include "core/templates/a_hash_map.h" #include "scene/3d/node_3d.h" #include "scene/resources/3d/skin.h" @@ -107,6 +108,8 @@ private: Quaternion pose_rotation; Vector3 pose_scale = Vector3(1, 1, 1); Transform3D global_pose; + int nested_set_offset = 0; // Offset in nested set of bone hierarchy. + int nested_set_span = 0; // Subtree span in nested set of bone hierarchy. void update_pose_cache() { if (pose_cache_dirty) { @@ -157,7 +160,7 @@ private: bool process_order_dirty = false; Vector<int> parentless_bones; - HashMap<String, int> name_to_bone_index; + AHashMap<String, int> name_to_bone_index; mutable StringName concatenated_bone_names = StringName(); void _update_bone_names() const; @@ -183,6 +186,15 @@ private: void _make_modifiers_dirty(); LocalVector<BonePoseBackup> bones_backup; + // Global bone pose calculation. + LocalVector<int> nested_set_offset_to_bone_index; // Map from Bone::nested_set_offset to bone index. + LocalVector<bool> bone_global_pose_dirty; // Indexable with Bone::nested_set_offset. + void _update_bones_nested_set(); + int _update_bone_nested_set(int p_bone, int p_offset); + void _make_bone_global_poses_dirty(); + void _make_bone_global_pose_subtree_dirty(int p_bone); + void _update_bone_global_pose(int p_bone); + #ifndef DISABLE_DEPRECATED void _add_bone_bind_compat_88791(const String &p_name); |