summaryrefslogtreecommitdiffstats
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/mesh_instance_2d.cpp13
-rw-r--r--scene/2d/skeleton_2d.cpp44
-rw-r--r--scene/2d/skeleton_2d.h2
-rw-r--r--scene/3d/cpu_particles_3d.cpp1
-rw-r--r--scene/3d/node_3d.cpp22
-rw-r--r--scene/3d/skeleton_3d.cpp223
-rw-r--r--scene/3d/skeleton_3d.h13
-rw-r--r--scene/debugger/scene_debugger.cpp16
-rw-r--r--scene/gui/code_edit.cpp6
-rw-r--r--scene/main/viewport.cpp10
-rw-r--r--scene/resources/immediate_mesh.cpp2
-rw-r--r--scene/resources/material.cpp9
12 files changed, 258 insertions, 103 deletions
diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp
index ae45d431fe..1031a67343 100644
--- a/scene/2d/mesh_instance_2d.cpp
+++ b/scene/2d/mesh_instance_2d.cpp
@@ -54,7 +54,20 @@ void MeshInstance2D::_bind_methods() {
}
void MeshInstance2D::set_mesh(const Ref<Mesh> &p_mesh) {
+ if (mesh == p_mesh) {
+ return;
+ }
+
+ if (mesh.is_valid()) {
+ mesh->disconnect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
+ }
+
mesh = p_mesh;
+
+ if (mesh.is_valid()) {
+ mesh->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
+ }
+
queue_redraw();
}
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 90bfb4c84c..39cfccf983 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -159,7 +159,7 @@ void Bone2D::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
if (skeleton) {
- for (int i = 0; i < skeleton->bones.size(); i++) {
+ for (uint32_t i = 0; i < skeleton->bones.size(); i++) {
if (skeleton->bones[i].bone == this) {
skeleton->bones.remove_at(i);
break;
@@ -555,17 +555,17 @@ void Skeleton2D::_update_bone_setup() {
bones.sort(); //sorting so that they are always in the same order/index
- for (int i = 0; i < bones.size(); i++) {
- bones.write[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose
- bones.write[i].bone->skeleton_index = i;
+ for (uint32_t i = 0; i < bones.size(); i++) {
+ bones[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose
+ bones[i].bone->skeleton_index = i;
Bone2D *parent_bone = Object::cast_to<Bone2D>(bones[i].bone->get_parent());
if (parent_bone) {
- bones.write[i].parent_index = parent_bone->skeleton_index;
+ bones[i].parent_index = parent_bone->skeleton_index;
} else {
- bones.write[i].parent_index = -1;
+ bones[i].parent_index = -1;
}
- bones.write[i].local_pose_override = bones[i].bone->get_skeleton_rest();
+ bones[i].local_pose_override = bones[i].bone->get_skeleton_rest();
}
transform_dirty = true;
@@ -594,16 +594,16 @@ void Skeleton2D::_update_transform() {
transform_dirty = false;
- for (int i = 0; i < bones.size(); i++) {
- ERR_CONTINUE(bones[i].parent_index >= i);
+ for (uint32_t i = 0; i < bones.size(); i++) {
+ ERR_CONTINUE(bones[i].parent_index >= (int)i);
if (bones[i].parent_index >= 0) {
- bones.write[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform();
+ bones[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform();
} else {
- bones.write[i].accum_transform = bones[i].bone->get_transform();
+ bones[i].accum_transform = bones[i].bone->get_transform();
}
}
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
Transform2D final_xform = bones[i].accum_transform * bones[i].rest_inverse;
RS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, final_xform);
}
@@ -621,7 +621,7 @@ int Skeleton2D::get_bone_count() const {
Bone2D *Skeleton2D::get_bone(int p_idx) {
ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
- ERR_FAIL_INDEX_V(p_idx, bones.size(), nullptr);
+ ERR_FAIL_INDEX_V(p_idx, (int)bones.size(), nullptr);
return bones[p_idx].bone;
}
@@ -733,14 +733,14 @@ RID Skeleton2D::get_skeleton() const {
}
void Skeleton2D::set_bone_local_pose_override(int p_bone_idx, Transform2D p_override, real_t p_amount, bool p_persistent) {
- ERR_FAIL_INDEX_MSG(p_bone_idx, bones.size(), "Bone index is out of range!");
- bones.write[p_bone_idx].local_pose_override = p_override;
- bones.write[p_bone_idx].local_pose_override_amount = p_amount;
- bones.write[p_bone_idx].local_pose_override_persistent = p_persistent;
+ ERR_FAIL_INDEX_MSG(p_bone_idx, (int)bones.size(), "Bone index is out of range!");
+ bones[p_bone_idx].local_pose_override = p_override;
+ bones[p_bone_idx].local_pose_override_amount = p_amount;
+ bones[p_bone_idx].local_pose_override_persistent = p_persistent;
}
Transform2D Skeleton2D::get_bone_local_pose_override(int p_bone_idx) {
- ERR_FAIL_INDEX_V_MSG(p_bone_idx, bones.size(), Transform2D(), "Bone index is out of range!");
+ ERR_FAIL_INDEX_V_MSG(p_bone_idx, (int)bones.size(), Transform2D(), "Bone index is out of range!");
return bones[p_bone_idx].local_pose_override;
}
@@ -771,7 +771,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
}
// Do not cache the transform changes caused by the modifications!
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
bones[i].bone->copy_transform_to_cache = false;
}
@@ -783,7 +783,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
// Only apply the local pose override on _process. Otherwise, just calculate the local_pose_override and reset the transform.
if (p_execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process) {
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
if (bones[i].local_pose_override_amount > 0) {
bones[i].bone->set_meta("_local_pose_override_enabled_", true);
@@ -793,7 +793,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
bones[i].bone->propagate_call("force_update_transform");
if (bones[i].local_pose_override_persistent) {
- bones.write[i].local_pose_override_amount = 0.0;
+ bones[i].local_pose_override_amount = 0.0;
}
} else {
// TODO: see if there is a way to undo the override without having to resort to setting every bone's transform.
@@ -804,7 +804,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
}
// Cache any future transform changes
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
bones[i].bone->copy_transform_to_cache = true;
}
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index 033bdff41d..16bd6fd34b 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -123,7 +123,7 @@ class Skeleton2D : public Node2D {
bool local_pose_override_persistent = false;
};
- Vector<Bone> bones;
+ LocalVector<Bone> bones;
bool bone_setup_dirty = true;
void _make_bone_setup_dirty();
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/node_3d.cpp b/scene/3d/node_3d.cpp
index 86ce8a881a..85de85a9a6 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -1181,15 +1181,16 @@ void Node3D::_validate_property(PropertyInfo &p_property) const {
}
bool Node3D::_property_can_revert(const StringName &p_name) const {
- if (p_name == "basis") {
+ const String sname = p_name;
+ if (sname == "basis") {
return true;
- } else if (p_name == "scale") {
+ } else if (sname == "scale") {
return true;
- } else if (p_name == "quaternion") {
+ } else if (sname == "quaternion") {
return true;
- } else if (p_name == "rotation") {
+ } else if (sname == "rotation") {
return true;
- } else if (p_name == "position") {
+ } else if (sname == "position") {
return true;
}
return false;
@@ -1198,35 +1199,36 @@ bool Node3D::_property_can_revert(const StringName &p_name) const {
bool Node3D::_property_get_revert(const StringName &p_name, Variant &r_property) const {
bool valid = false;
- if (p_name == "basis") {
+ const String sname = p_name;
+ if (sname == "basis") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
r_property = Transform3D(variant).get_basis();
} else {
r_property = Basis();
}
- } else if (p_name == "scale") {
+ } else if (sname == "scale") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
r_property = Transform3D(variant).get_basis().get_scale();
} else {
r_property = Vector3(1.0, 1.0, 1.0);
}
- } else if (p_name == "quaternion") {
+ } else if (sname == "quaternion") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
r_property = Quaternion(Transform3D(variant).get_basis().get_rotation_quaternion());
} else {
r_property = Quaternion();
}
- } else if (p_name == "rotation") {
+ } else if (sname == "rotation") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
r_property = Transform3D(variant).get_basis().get_euler_normalized(data.euler_rotation_order);
} else {
r_property = Vector3();
}
- } else if (p_name == "position") {
+ } else if (sname == "position") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid) {
r_property = Transform3D(variant).get_origin();
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index db9c4db30d..9e4c9b1832 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -81,7 +81,7 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
return false;
}
- int which = path.get_slicec('/', 1).to_int();
+ uint32_t which = path.get_slicec('/', 1).to_int();
String what = path.get_slicec('/', 2);
if (which == bones.size() && what == "name") {
@@ -89,7 +89,7 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
return true;
}
- ERR_FAIL_INDEX_V(which, bones.size(), false);
+ ERR_FAIL_UNSIGNED_INDEX_V(which, bones.size(), false);
if (what == "parent") {
set_bone_parent(which, p_value);
@@ -153,10 +153,10 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
return false;
}
- int which = path.get_slicec('/', 1).to_int();
+ uint32_t which = path.get_slicec('/', 1).to_int();
String what = path.get_slicec('/', 2);
- ERR_FAIL_INDEX_V(which, bones.size(), false);
+ ERR_FAIL_UNSIGNED_INDEX_V(which, bones.size(), false);
if (what == "name") {
r_ret = get_bone_name(which);
@@ -182,7 +182,7 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
}
void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
const String prep = vformat("%s/%d/", PNAME("bones"), i);
p_list->push_back(PropertyInfo(Variant::STRING, prep + PNAME("name"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::INT, prep + PNAME("parent"), PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR));
@@ -241,7 +241,7 @@ void Skeleton3D::_update_process_order() {
return;
}
- Bone *bonesptr = bones.ptrw();
+ Bone *bonesptr = bones.ptr();
int len = bones.size();
parentless_bones.clear();
@@ -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");
@@ -283,7 +285,7 @@ void Skeleton3D::_update_process_order() {
void Skeleton3D::_update_bone_names() const {
String names;
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
if (i > 0) {
names += ",";
}
@@ -331,16 +333,21 @@ void Skeleton3D::_notification(int p_what) {
updating = true;
- Bone *bonesptr = bones.ptrw();
+ 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()) {
// Store unmodified bone poses.
- for (int i = 0; i < bones.size(); i++) {
+ 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();
}
@@ -412,9 +419,11 @@ void Skeleton3D::_notification(int p_what) {
if (!modifiers.is_empty()) {
// Restore unmodified bone poses.
- for (int i = 0; i < bones.size(); i++) {
- bones_backup[i].restore(bones.write[i]);
+ 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 +466,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;
}
@@ -534,7 +644,7 @@ void Skeleton3D::set_bone_name(int p_bone, const String &p_name) {
}
name_to_bone_index.erase(bones[p_bone].name);
- bones.write[p_bone].name = p_name;
+ bones[p_bone].name = p_name;
name_to_bone_index.insert(p_name, p_bone);
version++;
@@ -582,13 +692,13 @@ void Skeleton3D::set_bone_meta(int p_bone, const StringName &p_key, const Varian
ERR_FAIL_INDEX(p_bone, bone_size);
if (p_value.get_type() == Variant::NIL) {
- if (bones.write[p_bone].metadata.has(p_key)) {
- bones.write[p_bone].metadata.erase(p_key);
+ if (bones[p_bone].metadata.has(p_key)) {
+ bones[p_bone].metadata.erase(p_key);
}
return;
}
- bones.write[p_bone].metadata.insert(p_key, p_value, false);
+ bones[p_bone].metadata.insert(p_key, p_value, false);
}
bool Skeleton3D::is_bone_parent_of(int p_bone, int p_parent_bone_id) const {
@@ -615,7 +725,7 @@ void Skeleton3D::set_bone_parent(int p_bone, int p_parent) {
ERR_FAIL_COND(p_parent != -1 && (p_parent < 0));
ERR_FAIL_COND(p_bone == p_parent);
- bones.write[p_bone].parent = p_parent;
+ bones[p_bone].parent = p_parent;
process_order_dirty = true;
rest_dirty = true;
_make_dirty();
@@ -629,11 +739,11 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) {
int parent = bones[p_bone].parent;
while (parent >= 0) {
- bones.write[p_bone].rest = bones[parent].rest * bones[p_bone].rest;
+ bones[p_bone].rest = bones[parent].rest * bones[p_bone].rest;
parent = bones[parent].parent;
}
- bones.write[p_bone].parent = -1;
+ bones[p_bone].parent = -1;
process_order_dirty = true;
rest_dirty = true;
@@ -669,9 +779,10 @@ void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].rest = 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();
@@ -692,9 +803,10 @@ void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].enabled = 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 +819,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 {
@@ -727,12 +840,13 @@ void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose_position = p_pose.origin;
- bones.write[p_bone].pose_rotation = p_pose.basis.get_rotation_quaternion();
- bones.write[p_bone].pose_scale = p_pose.basis.get_scale();
- bones.write[p_bone].pose_cache_dirty = true;
+ bones[p_bone].pose_position = p_pose.origin;
+ bones[p_bone].pose_rotation = p_pose.basis.get_rotation_quaternion();
+ bones[p_bone].pose_scale = p_pose.basis.get_scale();
+ bones[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
}
@@ -740,30 +854,33 @@ void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose_position = p_position;
- bones.write[p_bone].pose_cache_dirty = true;
+ bones[p_bone].pose_position = 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) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose_rotation = p_rotation;
- bones.write[p_bone].pose_cache_dirty = true;
+ bones[p_bone].pose_rotation = 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) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose_scale = p_scale;
- bones.write[p_bone].pose_cache_dirty = true;
+ bones[p_bone].pose_scale = p_scale;
+ bones[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
}
@@ -794,7 +911,7 @@ void Skeleton3D::reset_bone_pose(int p_bone) {
}
void Skeleton3D::reset_bone_poses() {
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
reset_bone_pose(i);
}
}
@@ -802,7 +919,7 @@ void Skeleton3D::reset_bone_poses() {
Transform3D Skeleton3D::get_bone_pose(int p_bone) const {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
- const_cast<Skeleton3D *>(this)->bones.write[p_bone].update_pose_cache();
+ const_cast<Skeleton3D *>(this)->bones[p_bone].update_pose_cache();
return bones[p_bone].pose_cache;
}
@@ -853,7 +970,7 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() {
// Pose changed, rebuild cache of inverses.
const Bone *bonesptr = bones.ptr();
- int len = bones.size();
+ uint32_t len = bones.size();
// Calculate global rests and invert them.
LocalVector<int> bones_to_process;
@@ -877,7 +994,7 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() {
}
}
- for (int i = 0; i < len; i++) {
+ for (uint32_t i = 0; i < len; i++) {
// The inverse is what is actually required.
skin->set_bind_bone(i, i);
skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse());
@@ -937,15 +1054,15 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone_idx, bone_size);
- Bone *bonesptr = bones.ptrw();
- thread_local LocalVector<int> bones_to_process;
- bones_to_process.clear();
- bones_to_process.push_back(p_bone_idx);
+ Bone *bonesptr = bones.ptr();
- 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 +1109,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;
}
}
@@ -1171,20 +1282,22 @@ void Skeleton3D::_bind_methods() {
#ifndef DISABLE_DEPRECATED
void Skeleton3D::clear_bones_global_pose_override() {
- for (int i = 0; i < bones.size(); i += 1) {
- bones.write[i].global_pose_override_amount = 0;
- bones.write[i].global_pose_override_reset = true;
+ for (uint32_t i = 0; i < bones.size(); i += 1) {
+ bones[i].global_pose_override_amount = 0;
+ 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) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].global_pose_override_amount = p_amount;
- bones.write[p_bone].global_pose_override = p_pose;
- bones.write[p_bone].global_pose_override_reset = !p_persistent;
+ bones[p_bone].global_pose_override_amount = p_amount;
+ 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 07bdeccf2f..0db12600c3 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -107,6 +107,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) {
@@ -153,7 +155,7 @@ private:
HashSet<SkinReference *> skin_bindings;
void _skin_changed();
- Vector<Bone> bones;
+ LocalVector<Bone> bones;
bool process_order_dirty = false;
Vector<int> parentless_bones;
@@ -183,6 +185,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);
diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp
index 07c32eef13..22e5238fae 100644
--- a/scene/debugger/scene_debugger.cpp
+++ b/scene/debugger/scene_debugger.cpp
@@ -535,7 +535,21 @@ SceneDebuggerTree::SceneDebuggerTree(Node *p_root) {
}
}
}
- nodes.push_back(RemoteNode(count, n->get_name(), n->get_class(), n->get_instance_id(), n->get_scene_file_path(), view_flags));
+
+ String class_name;
+ ScriptInstance *script_instance = n->get_script_instance();
+ if (script_instance) {
+ Ref<Script> script = script_instance->get_script();
+ if (script.is_valid()) {
+ class_name = script->get_global_name();
+
+ if (class_name.is_empty()) {
+ // If there is no class_name in this script we just take the script path.
+ class_name = script->get_path();
+ }
+ }
+ }
+ nodes.push_back(RemoteNode(count, n->get_name(), class_name.is_empty() ? n->get_class() : class_name, n->get_instance_id(), n->get_scene_file_path(), view_flags));
}
}
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 635228670d..7346c9dcd3 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -277,15 +277,17 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
code_completion_force_item_center = -1;
queue_redraw();
}
- code_completion_pan_offset += 1.0f;
+ code_completion_pan_offset = 0;
} else if (code_completion_pan_offset >= +1.0) {
if (code_completion_current_selected < code_completion_options.size() - 1) {
code_completion_current_selected++;
code_completion_force_item_center = -1;
queue_redraw();
}
- code_completion_pan_offset -= 1.0f;
+ code_completion_pan_offset = 0;
}
+ accept_event();
+ return;
}
Ref<InputEventMouseButton> mb = p_gui_input;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 169a5dcb01..0cdb23618f 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1448,7 +1448,11 @@ void Viewport::_gui_show_tooltip() {
&tooltip_owner);
gui.tooltip_text = gui.tooltip_text.strip_edges();
- if (gui.tooltip_text.is_empty()) {
+ // Controls can implement `make_custom_tooltip` to provide their own tooltip.
+ // This should be a Control node which will be added as child to a TooltipPanel.
+ Control *base_tooltip = tooltip_owner ? tooltip_owner->make_custom_tooltip(gui.tooltip_text) : nullptr;
+
+ if (gui.tooltip_text.is_empty() && !base_tooltip) {
return; // Nothing to show.
}
@@ -1469,10 +1473,6 @@ void Viewport::_gui_show_tooltip() {
// Ensure no opaque background behind the panel as its StyleBox can be partially transparent (e.g. corners).
panel->set_transparent_background(true);
- // Controls can implement `make_custom_tooltip` to provide their own tooltip.
- // This should be a Control node which will be added as child to a TooltipPanel.
- Control *base_tooltip = tooltip_owner->make_custom_tooltip(gui.tooltip_text);
-
// If no custom tooltip is given, use a default implementation.
if (!base_tooltip) {
gui.tooltip_label = memnew(Label);
diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp
index 907c0ab4ca..072542f0ad 100644
--- a/scene/resources/immediate_mesh.cpp
+++ b/scene/resources/immediate_mesh.cpp
@@ -312,6 +312,8 @@ void ImmediateMesh::surface_end() {
uses_uv2s = false;
surface_active = false;
+
+ emit_changed();
}
void ImmediateMesh::clear_surfaces() {
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 9df009ec28..ecc1982aa5 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -382,14 +382,11 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
bool ShaderMaterial::_property_can_revert(const StringName &p_name) const {
if (shader.is_valid()) {
- const StringName *pr = remap_cache.getptr(p_name);
- if (pr) {
- Variant default_value = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), *pr);
- Variant current_value = get_shader_parameter(*pr);
- return default_value.get_type() != Variant::NIL && default_value != current_value;
- } else if (p_name == "render_priority" || p_name == "next_pass") {
+ if (remap_cache.has(p_name)) {
return true;
}
+ const String sname = p_name;
+ return sname == "render_priority" || sname == "next_pass";
}
return false;
}