summaryrefslogtreecommitdiffstats
path: root/scene/3d
diff options
context:
space:
mode:
Diffstat (limited to 'scene/3d')
-rw-r--r--scene/3d/cpu_particles_3d.cpp1
-rw-r--r--scene/3d/light_3d.cpp15
-rw-r--r--scene/3d/light_3d.h4
-rw-r--r--scene/3d/lightmap_gi.cpp15
-rw-r--r--scene/3d/mesh_instance_3d.cpp4
-rw-r--r--scene/3d/skeleton_3d.cpp143
-rw-r--r--scene/3d/skeleton_3d.h14
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);