summaryrefslogtreecommitdiffstats
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/camera_2d.cpp4
-rw-r--r--scene/3d/bone_attachment_3d.cpp20
-rw-r--r--scene/3d/bone_attachment_3d.h2
-rw-r--r--scene/3d/decal.cpp2
-rw-r--r--scene/3d/physical_bone_simulator_3d.cpp396
-rw-r--r--scene/3d/physical_bone_simulator_3d.h110
-rw-r--r--scene/3d/physics/physical_bone_3d.cpp136
-rw-r--r--scene/3d/physics/physical_bone_3d.h13
-rw-r--r--scene/3d/skeleton_3d.cpp593
-rw-r--r--scene/3d/skeleton_3d.h118
-rw-r--r--scene/3d/skeleton_ik_3d.cpp108
-rw-r--r--scene/3d/skeleton_ik_3d.h23
-rw-r--r--scene/3d/skeleton_modifier_3d.cpp139
-rw-r--r--scene/3d/skeleton_modifier_3d.h81
-rw-r--r--scene/3d/xr_body_modifier_3d.cpp45
-rw-r--r--scene/3d/xr_body_modifier_3d.h15
-rw-r--r--scene/3d/xr_hand_modifier_3d.cpp46
-rw-r--r--scene/3d/xr_hand_modifier_3d.h17
-rw-r--r--scene/animation/animation_mixer.cpp25
-rw-r--r--scene/animation/animation_mixer.h5
-rw-r--r--scene/animation/animation_tree.cpp5
-rw-r--r--scene/gui/control.cpp4
-rw-r--r--scene/main/node.cpp4
-rw-r--r--scene/register_scene_types.cpp4
-rw-r--r--scene/resources/skeleton_profile.cpp57
-rw-r--r--scene/resources/skeleton_profile.h12
26 files changed, 1357 insertions, 627 deletions
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index e9bab274c6..4b866fc6de 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -627,7 +627,7 @@ void Camera2D::align() {
}
void Camera2D::set_position_smoothing_speed(real_t p_speed) {
- position_smoothing_speed = p_speed;
+ position_smoothing_speed = MAX(0, p_speed);
_update_process_internal_for_smoothing();
}
@@ -636,7 +636,7 @@ real_t Camera2D::get_position_smoothing_speed() const {
}
void Camera2D::set_rotation_smoothing_speed(real_t p_speed) {
- rotation_smoothing_speed = p_speed;
+ rotation_smoothing_speed = MAX(0, p_speed);
_update_process_internal_for_smoothing();
}
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index 76e89f24d8..221200284d 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -187,7 +187,7 @@ void BoneAttachment3D::_transform_changed() {
return;
}
- if (override_pose) {
+ if (override_pose && !overriding) {
Skeleton3D *sk = _get_skeleton3d();
ERR_FAIL_NULL_MSG(sk, "Cannot override pose: Skeleton not found!");
@@ -198,8 +198,11 @@ void BoneAttachment3D::_transform_changed() {
our_trans = sk->get_global_transform().affine_inverse() * get_global_transform();
}
- sk->set_bone_global_pose_override(bone_idx, our_trans, 1.0, true);
+ overriding = true;
+ sk->set_bone_global_pose(bone_idx, our_trans);
+ sk->force_update_all_dirty_bones();
}
+ overriding = false;
}
void BoneAttachment3D::set_bone_name(const String &p_name) {
@@ -246,14 +249,6 @@ void BoneAttachment3D::set_override_pose(bool p_override) {
override_pose = p_override;
set_notify_transform(override_pose);
set_process_internal(override_pose);
-
- if (!override_pose) {
- Skeleton3D *sk = _get_skeleton3d();
- if (sk) {
- sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false);
- }
- _transform_changed();
- }
notify_property_list_changed();
}
@@ -314,6 +309,10 @@ void BoneAttachment3D::_notification(int p_what) {
}
void BoneAttachment3D::on_bone_pose_update(int p_bone_index) {
+ if (updating) {
+ return;
+ }
+ updating = true;
if (bone_idx == p_bone_index) {
Skeleton3D *sk = _get_skeleton3d();
if (sk) {
@@ -331,6 +330,7 @@ void BoneAttachment3D::on_bone_pose_update(int p_bone_index) {
}
}
}
+ updating = false;
}
#ifdef TOOLS_ENABLED
void BoneAttachment3D::notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Dictionary p_rename_map) {
diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h
index 1bf44c2756..ec0f7344d7 100644
--- a/scene/3d/bone_attachment_3d.h
+++ b/scene/3d/bone_attachment_3d.h
@@ -45,6 +45,7 @@ class BoneAttachment3D : public Node3D {
bool override_pose = false;
bool _override_dirty = false;
+ bool overriding = false;
bool use_external_skeleton = false;
NodePath external_skeleton_node;
@@ -53,6 +54,7 @@ class BoneAttachment3D : public Node3D {
void _check_bind();
void _check_unbind();
+ bool updating = false;
void _transform_changed();
void _update_external_skeleton_cache();
Skeleton3D *_get_skeleton3d();
diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp
index caabb4225f..8415fb38cb 100644
--- a/scene/3d/decal.cpp
+++ b/scene/3d/decal.cpp
@@ -231,7 +231,7 @@ void Decal::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "texture_emission", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_EMISSION);
ADD_GROUP("Parameters", "");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_emission_energy", "get_emission_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_emission_energy", "get_emission_energy");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "albedo_mix", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_albedo_mix", "get_albedo_mix");
// A Normal Fade of 1.0 causes the decal to be invisible even if fully perpendicular to a surface.
diff --git a/scene/3d/physical_bone_simulator_3d.cpp b/scene/3d/physical_bone_simulator_3d.cpp
new file mode 100644
index 0000000000..aba052165c
--- /dev/null
+++ b/scene/3d/physical_bone_simulator_3d.cpp
@@ -0,0 +1,396 @@
+/**************************************************************************/
+/* physical_bone_simulator_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "physical_bone_simulator_3d.h"
+
+void PhysicalBoneSimulator3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) {
+ if (p_old) {
+ if (p_old->is_connected(SNAME("bone_list_changed"), callable_mp(this, &PhysicalBoneSimulator3D::_bone_list_changed))) {
+ p_old->disconnect(SNAME("bone_list_changed"), callable_mp(this, &PhysicalBoneSimulator3D::_bone_list_changed));
+ }
+ if (p_old->is_connected(SNAME("pose_updated"), callable_mp(this, &PhysicalBoneSimulator3D::_pose_updated))) {
+ p_old->disconnect(SNAME("pose_updated"), callable_mp(this, &PhysicalBoneSimulator3D::_pose_updated));
+ }
+ }
+ if (p_new) {
+ if (!p_new->is_connected(SNAME("bone_list_changed"), callable_mp(this, &PhysicalBoneSimulator3D::_bone_list_changed))) {
+ p_new->connect(SNAME("bone_list_changed"), callable_mp(this, &PhysicalBoneSimulator3D::_bone_list_changed));
+ }
+ if (!p_new->is_connected(SNAME("pose_updated"), callable_mp(this, &PhysicalBoneSimulator3D::_pose_updated))) {
+ p_new->connect(SNAME("pose_updated"), callable_mp(this, &PhysicalBoneSimulator3D::_pose_updated));
+ }
+ }
+ _bone_list_changed();
+}
+
+void PhysicalBoneSimulator3D::_bone_list_changed() {
+ bones.clear();
+ Skeleton3D *skeleton = get_skeleton();
+ if (!skeleton) {
+ return;
+ }
+ for (int i = 0; i < skeleton->get_bone_count(); i++) {
+ SimulatedBone sb;
+ sb.parent = skeleton->get_bone_parent(i);
+ sb.child_bones = skeleton->get_bone_children(i);
+ bones.push_back(sb);
+ }
+ _rebuild_physical_bones_cache();
+ _pose_updated();
+}
+
+void PhysicalBoneSimulator3D::_pose_updated() {
+ Skeleton3D *skeleton = get_skeleton();
+ if (!skeleton || simulating) {
+ return;
+ }
+ ERR_FAIL_COND(skeleton->get_bone_count() != bones.size());
+ for (int i = 0; i < skeleton->get_bone_count(); i++) {
+ bones.write[i].global_pose = skeleton->get_bone_global_pose(i);
+ }
+}
+
+void PhysicalBoneSimulator3D::_set_active(bool p_active) {
+ if (!Engine::get_singleton()->is_editor_hint()) {
+ _reset_physical_bones_state();
+ }
+}
+
+void PhysicalBoneSimulator3D::_reset_physical_bones_state() {
+ for (int i = 0; i < bones.size(); i += 1) {
+ if (bones[i].physical_bone) {
+ bones[i].physical_bone->reset_physics_simulation_state();
+ }
+ }
+}
+
+bool PhysicalBoneSimulator3D::is_simulating_physics() const {
+ return simulating;
+}
+
+int PhysicalBoneSimulator3D::find_bone(const String &p_name) const {
+ Skeleton3D *skeleton = get_skeleton();
+ if (!skeleton) {
+ return -1;
+ }
+ return skeleton->find_bone(p_name);
+}
+
+String PhysicalBoneSimulator3D::get_bone_name(int p_bone) const {
+ Skeleton3D *skeleton = get_skeleton();
+ if (!skeleton) {
+ return String();
+ }
+ return skeleton->get_bone_name(p_bone);
+}
+
+int PhysicalBoneSimulator3D::get_bone_count() const {
+ return bones.size();
+}
+
+bool PhysicalBoneSimulator3D::is_bone_parent_of(int p_bone, int p_parent_bone_id) const {
+ Skeleton3D *skeleton = get_skeleton();
+ if (!skeleton) {
+ return false;
+ }
+ return skeleton->is_bone_parent_of(p_bone, p_parent_bone_id);
+}
+
+void PhysicalBoneSimulator3D::bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
+ ERR_FAIL_COND(bones[p_bone].physical_bone);
+ ERR_FAIL_NULL(p_physical_bone);
+ bones.write[p_bone].physical_bone = p_physical_bone;
+
+ _rebuild_physical_bones_cache();
+}
+
+void PhysicalBoneSimulator3D::unbind_physical_bone_from_bone(int p_bone) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
+ bones.write[p_bone].physical_bone = nullptr;
+
+ _rebuild_physical_bones_cache();
+}
+
+PhysicalBone3D *PhysicalBoneSimulator3D::get_physical_bone(int p_bone) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
+
+ return bones[p_bone].physical_bone;
+}
+
+PhysicalBone3D *PhysicalBoneSimulator3D::get_physical_bone_parent(int p_bone) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
+
+ if (bones[p_bone].cache_parent_physical_bone) {
+ return bones[p_bone].cache_parent_physical_bone;
+ }
+
+ return _get_physical_bone_parent(p_bone);
+}
+
+PhysicalBone3D *PhysicalBoneSimulator3D::_get_physical_bone_parent(int p_bone) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
+
+ const int parent_bone = bones[p_bone].parent;
+ if (parent_bone < 0) {
+ return nullptr;
+ }
+
+ PhysicalBone3D *pb = bones[parent_bone].physical_bone;
+ if (pb) {
+ return pb;
+ } else {
+ return get_physical_bone_parent(parent_bone);
+ }
+}
+
+void PhysicalBoneSimulator3D::_rebuild_physical_bones_cache() {
+ const int b_size = bones.size();
+ for (int i = 0; i < b_size; ++i) {
+ PhysicalBone3D *parent_pb = _get_physical_bone_parent(i);
+ if (parent_pb != bones[i].cache_parent_physical_bone) {
+ bones.write[i].cache_parent_physical_bone = parent_pb;
+ if (bones[i].physical_bone) {
+ bones[i].physical_bone->_on_bone_parent_changed();
+ }
+ }
+ }
+}
+
+#ifndef DISABLE_DEPRECATED
+void _pb_stop_simulation_compat(Node *p_node) {
+ PhysicalBoneSimulator3D *ps = Object::cast_to<PhysicalBoneSimulator3D>(p_node);
+ if (ps) {
+ return; // Prevent conflict.
+ }
+ for (int i = p_node->get_child_count() - 1; i >= 0; --i) {
+ _pb_stop_simulation_compat(p_node->get_child(i));
+ }
+ PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node);
+ if (pb) {
+ pb->set_simulate_physics(false);
+ }
+}
+#endif // _DISABLE_DEPRECATED
+
+void _pb_stop_simulation(Node *p_node) {
+ for (int i = p_node->get_child_count() - 1; i >= 0; --i) {
+ PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node->get_child(i));
+ if (!pb) {
+ continue;
+ }
+ _pb_stop_simulation(pb);
+ }
+ PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node);
+ if (pb) {
+ pb->set_simulate_physics(false);
+ }
+}
+
+void PhysicalBoneSimulator3D::physical_bones_stop_simulation() {
+ simulating = false;
+ _reset_physical_bones_state();
+#ifndef DISABLE_DEPRECATED
+ if (is_compat) {
+ Skeleton3D *sk = get_skeleton();
+ if (sk) {
+ _pb_stop_simulation_compat(sk);
+ }
+ } else {
+ _pb_stop_simulation(this);
+ }
+#else
+ _pb_stop_simulation(this);
+#endif // _DISABLE_DEPRECATED
+}
+
+#ifndef DISABLE_DEPRECATED
+void _pb_start_simulation_compat(const PhysicalBoneSimulator3D *p_simulator, Node *p_node, const Vector<int> &p_sim_bones) {
+ PhysicalBoneSimulator3D *ps = Object::cast_to<PhysicalBoneSimulator3D>(p_node);
+ if (ps) {
+ return; // Prevent conflict.
+ }
+ for (int i = p_node->get_child_count() - 1; i >= 0; --i) {
+ _pb_start_simulation_compat(p_simulator, p_node->get_child(i), p_sim_bones);
+ }
+ PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node);
+ if (pb) {
+ if (p_sim_bones.is_empty()) { // If no bones are specified, activate ragdoll on full body.
+ pb->set_simulate_physics(true);
+ } else {
+ for (int i = p_sim_bones.size() - 1; i >= 0; --i) {
+ if (p_sim_bones[i] == pb->get_bone_id() || p_simulator->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) {
+ pb->set_simulate_physics(true);
+ break;
+ }
+ }
+ }
+ }
+}
+#endif // _DISABLE_DEPRECATED
+
+void _pb_start_simulation(const PhysicalBoneSimulator3D *p_simulator, Node *p_node, const Vector<int> &p_sim_bones) {
+ for (int i = p_node->get_child_count() - 1; i >= 0; --i) {
+ PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node->get_child(i));
+ if (!pb) {
+ continue;
+ }
+ _pb_start_simulation(p_simulator, pb, p_sim_bones);
+ }
+ PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node);
+ if (pb) {
+ if (p_sim_bones.is_empty()) { // If no bones are specified, activate ragdoll on full body.
+ pb->set_simulate_physics(true);
+ } else {
+ for (int i = p_sim_bones.size() - 1; i >= 0; --i) {
+ if (p_sim_bones[i] == pb->get_bone_id() || p_simulator->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) {
+ pb->set_simulate_physics(true);
+ break;
+ }
+ }
+ }
+ }
+}
+
+void PhysicalBoneSimulator3D::physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones) {
+ simulating = true;
+ _reset_physical_bones_state();
+
+ _pose_updated();
+
+ Vector<int> sim_bones;
+ if (p_bones.size() > 0) {
+ sim_bones.resize(p_bones.size());
+ int c = 0;
+ for (int i = sim_bones.size() - 1; i >= 0; --i) {
+ int bone_id = find_bone(p_bones[i]);
+ if (bone_id != -1) {
+ sim_bones.write[c++] = bone_id;
+ }
+ }
+ sim_bones.resize(c);
+ }
+
+#ifndef DISABLE_DEPRECATED
+ if (is_compat) {
+ Skeleton3D *sk = get_skeleton();
+ if (sk) {
+ _pb_start_simulation_compat(this, sk, sim_bones);
+ }
+ } else {
+ _pb_start_simulation(this, this, sim_bones);
+ }
+#else
+ _pb_start_simulation(this, this, sim_bones);
+#endif // _DISABLE_DEPRECATED
+}
+
+void _physical_bones_add_remove_collision_exception(bool p_add, Node *p_node, RID p_exception) {
+ for (int i = p_node->get_child_count() - 1; i >= 0; --i) {
+ _physical_bones_add_remove_collision_exception(p_add, p_node->get_child(i), p_exception);
+ }
+
+ CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_node);
+ if (co) {
+ if (p_add) {
+ PhysicsServer3D::get_singleton()->body_add_collision_exception(co->get_rid(), p_exception);
+ } else {
+ PhysicsServer3D::get_singleton()->body_remove_collision_exception(co->get_rid(), p_exception);
+ }
+ }
+}
+
+void PhysicalBoneSimulator3D::physical_bones_add_collision_exception(RID p_exception) {
+ _physical_bones_add_remove_collision_exception(true, this, p_exception);
+}
+
+void PhysicalBoneSimulator3D::physical_bones_remove_collision_exception(RID p_exception) {
+ _physical_bones_add_remove_collision_exception(false, this, p_exception);
+}
+
+Transform3D PhysicalBoneSimulator3D::get_bone_global_pose(int p_bone) const {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
+ return bones[p_bone].global_pose;
+}
+
+void PhysicalBoneSimulator3D::set_bone_global_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].global_pose = p_pose;
+}
+
+void PhysicalBoneSimulator3D::_process_modification() {
+ Skeleton3D *skeleton = get_skeleton();
+ if (!skeleton) {
+ return;
+ }
+ if (!enabled) {
+ for (int i = 0; i < bones.size(); i++) {
+ if (bones[i].physical_bone) {
+ if (bones[i].physical_bone->is_simulating_physics() == false) {
+ bones[i].physical_bone->reset_to_rest_position();
+ }
+ }
+ }
+ } else {
+ ERR_FAIL_COND(skeleton->get_bone_count() != bones.size());
+ LocalVector<Transform3D> local_poses;
+ for (int i = 0; i < skeleton->get_bone_count(); i++) {
+ Transform3D pt;
+ if (skeleton->get_bone_parent(i) >= 0) {
+ pt = get_bone_global_pose(skeleton->get_bone_parent(i));
+ }
+ local_poses.push_back(pt.affine_inverse() * bones[i].global_pose);
+ }
+ for (int i = 0; i < skeleton->get_bone_count(); i++) {
+ skeleton->set_bone_pose_position(i, local_poses[i].origin);
+ skeleton->set_bone_pose_rotation(i, local_poses[i].basis.get_rotation_quaternion());
+ skeleton->set_bone_pose_scale(i, local_poses[i].basis.get_scale());
+ }
+ }
+}
+
+void PhysicalBoneSimulator3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("is_simulating_physics"), &PhysicalBoneSimulator3D::is_simulating_physics);
+
+ ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &PhysicalBoneSimulator3D::physical_bones_stop_simulation);
+ ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &PhysicalBoneSimulator3D::physical_bones_start_simulation_on, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &PhysicalBoneSimulator3D::physical_bones_add_collision_exception);
+ ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &PhysicalBoneSimulator3D::physical_bones_remove_collision_exception);
+}
+
+PhysicalBoneSimulator3D::PhysicalBoneSimulator3D() {
+}
diff --git a/scene/3d/physical_bone_simulator_3d.h b/scene/3d/physical_bone_simulator_3d.h
new file mode 100644
index 0000000000..ee900e0e77
--- /dev/null
+++ b/scene/3d/physical_bone_simulator_3d.h
@@ -0,0 +1,110 @@
+/**************************************************************************/
+/* physical_bone_simulator_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef PHYSICAL_BONE_SIMULATOR_3D_H
+#define PHYSICAL_BONE_SIMULATOR_3D_H
+
+#include "scene/3d/skeleton_modifier_3d.h"
+
+#include "scene/3d/physics/physical_bone_3d.h"
+
+class PhysicalBone3D;
+
+class PhysicalBoneSimulator3D : public SkeletonModifier3D {
+ GDCLASS(PhysicalBoneSimulator3D, SkeletonModifier3D);
+
+ bool simulating = false;
+ bool enabled = true;
+
+ struct SimulatedBone {
+ int parent;
+ Vector<int> child_bones;
+
+ Transform3D global_pose;
+
+ PhysicalBone3D *physical_bone = nullptr;
+ PhysicalBone3D *cache_parent_physical_bone = nullptr;
+
+ SimulatedBone() {
+ parent = -1;
+ global_pose = Transform3D();
+ physical_bone = nullptr;
+ cache_parent_physical_bone = nullptr;
+ }
+ };
+
+ Vector<SimulatedBone> bones;
+
+ /// This is a slow API, so it's cached
+ PhysicalBone3D *_get_physical_bone_parent(int p_bone);
+ void _rebuild_physical_bones_cache();
+ void _reset_physical_bones_state();
+
+protected:
+ static void _bind_methods();
+
+ virtual void _set_active(bool p_active) override;
+
+ void _bone_list_changed();
+ void _pose_updated();
+
+ virtual void _process_modification() override;
+
+ virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override;
+
+public:
+#ifndef DISABLE_DEPRECATED
+ bool is_compat = false;
+#endif // _DISABLE_DEPRECATED
+ bool is_simulating_physics() const;
+
+ int find_bone(const String &p_name) const;
+ String get_bone_name(int p_bone) const;
+ int get_bone_count() const;
+ bool is_bone_parent_of(int p_bone_id, int p_parent_bone_id) const;
+
+ Transform3D get_bone_global_pose(int p_bone) const;
+ void set_bone_global_pose(int p_bone, const Transform3D &p_pose);
+
+ void bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone);
+ void unbind_physical_bone_from_bone(int p_bone);
+
+ PhysicalBone3D *get_physical_bone(int p_bone);
+ PhysicalBone3D *get_physical_bone_parent(int p_bone);
+
+ void physical_bones_stop_simulation();
+ void physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones);
+ void physical_bones_add_collision_exception(RID p_exception);
+ void physical_bones_remove_collision_exception(RID p_exception);
+
+ PhysicalBoneSimulator3D();
+};
+
+#endif // PHYSICAL_BONE_SIMULATOR_3D_H
diff --git a/scene/3d/physics/physical_bone_3d.cpp b/scene/3d/physics/physical_bone_3d.cpp
index 10c1fceb94..c6be2a9da8 100644
--- a/scene/3d/physics/physical_bone_3d.cpp
+++ b/scene/3d/physics/physical_bone_3d.cpp
@@ -29,6 +29,9 @@
/**************************************************************************/
#include "physical_bone_3d.h"
+#ifndef DISABLE_DEPRECATED
+#include "scene/3d/skeleton_3d.h"
+#endif //_DISABLE_DEPRECATED
bool PhysicalBone3D::JointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
return false;
@@ -89,15 +92,14 @@ void PhysicalBone3D::reset_physics_simulation_state() {
}
void PhysicalBone3D::reset_to_rest_position() {
- if (parent_skeleton) {
- Transform3D new_transform = parent_skeleton->get_global_transform();
+ PhysicalBoneSimulator3D *simulator = get_simulator();
+ Skeleton3D *skeleton = get_skeleton();
+ if (simulator && skeleton) {
if (bone_id == -1) {
- new_transform *= body_offset;
+ set_global_transform((skeleton->get_global_transform() * body_offset).orthonormalized());
} else {
- new_transform *= parent_skeleton->get_bone_global_pose(bone_id) * body_offset;
+ set_global_transform((skeleton->get_global_transform() * simulator->get_bone_global_pose(bone_id) * body_offset).orthonormalized());
}
- new_transform.orthonormalize();
- set_global_transform(new_transform);
}
}
@@ -734,15 +736,14 @@ bool PhysicalBone3D::_get(const StringName &p_name, Variant &r_ret) const {
}
void PhysicalBone3D::_get_property_list(List<PropertyInfo> *p_list) const {
- Skeleton3D *parent = find_skeleton_parent(get_parent());
-
- if (parent) {
+ Skeleton3D *skeleton = get_skeleton();
+ if (skeleton) {
String names;
- for (int i = 0; i < parent->get_bone_count(); i++) {
+ for (int i = 0; i < skeleton->get_bone_count(); i++) {
if (i > 0) {
names += ",";
}
- names += parent->get_bone_name(i);
+ names += skeleton->get_bone_name(i);
}
p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("bone_name"), PROPERTY_HINT_ENUM, names));
@@ -758,7 +759,8 @@ void PhysicalBone3D::_get_property_list(List<PropertyInfo> *p_list) const {
void PhysicalBone3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
- parent_skeleton = find_skeleton_parent(get_parent());
+ case NOTIFICATION_PARENTED:
+ _update_simulator_path();
update_bone_id();
reset_to_rest_position();
reset_physics_simulation_state();
@@ -768,13 +770,13 @@ void PhysicalBone3D::_notification(int p_what) {
break;
case NOTIFICATION_EXIT_TREE: {
- if (parent_skeleton) {
+ PhysicalBoneSimulator3D *simulator = get_simulator();
+ if (simulator) {
if (-1 != bone_id) {
- parent_skeleton->unbind_physical_bone_from_bone(bone_id);
+ simulator->unbind_physical_bone_from_bone(bone_id);
bone_id = -1;
}
}
- parent_skeleton = nullptr;
PhysicsServer3D::get_singleton()->joint_clear(joint);
} break;
@@ -818,10 +820,12 @@ void PhysicalBone3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
Transform3D global_transform(p_state->get_transform());
- // Update skeleton
- if (parent_skeleton) {
+ // Update simulator
+ PhysicalBoneSimulator3D *simulator = get_simulator();
+ Skeleton3D *skeleton = get_skeleton();
+ if (simulator && skeleton) {
if (-1 != bone_id) {
- parent_skeleton->set_bone_global_pose_override(bone_id, parent_skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse), 1.0, true);
+ simulator->set_bone_global_pose(bone_id, skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse));
}
}
}
@@ -916,14 +920,6 @@ void PhysicalBone3D::_bind_methods() {
BIND_ENUM_CONSTANT(JOINT_TYPE_6DOF);
}
-Skeleton3D *PhysicalBone3D::find_skeleton_parent(Node *p_parent) {
- if (!p_parent) {
- return nullptr;
- }
- Skeleton3D *s = Object::cast_to<Skeleton3D>(p_parent);
- return s ? s : find_skeleton_parent(p_parent->get_parent());
-}
-
void PhysicalBone3D::_update_joint_offset() {
_fix_joint_offset();
@@ -938,18 +934,20 @@ void PhysicalBone3D::_update_joint_offset() {
void PhysicalBone3D::_fix_joint_offset() {
// Clamp joint origin to bone origin
- if (parent_skeleton) {
+ PhysicalBoneSimulator3D *simulator = get_simulator();
+ if (simulator) {
joint_offset.origin = body_offset.affine_inverse().origin;
}
}
void PhysicalBone3D::_reload_joint() {
- if (!parent_skeleton) {
+ PhysicalBoneSimulator3D *simulator = get_simulator();
+ if (!simulator || !simulator->get_skeleton()) {
PhysicsServer3D::get_singleton()->joint_clear(joint);
return;
}
- PhysicalBone3D *body_a = parent_skeleton->get_physical_bone_parent(bone_id);
+ PhysicalBone3D *body_a = simulator->get_physical_bone_parent(bone_id);
if (!body_a) {
PhysicsServer3D::get_singleton()->joint_clear(joint);
return;
@@ -1041,6 +1039,36 @@ void PhysicalBone3D::_on_bone_parent_changed() {
_reload_joint();
}
+void PhysicalBone3D::_update_simulator_path() {
+ simulator_id = ObjectID();
+ PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(get_parent());
+ if (sim) {
+ simulator_id = sim->get_instance_id();
+ return;
+ }
+#ifndef DISABLE_DEPRECATED
+ Skeleton3D *sk = cast_to<Skeleton3D>(get_parent());
+ if (sk) {
+ PhysicalBoneSimulator3D *ssim = cast_to<PhysicalBoneSimulator3D>(sk->get_simulator());
+ if (ssim) {
+ simulator_id = ssim->get_instance_id();
+ }
+ }
+#endif // _DISABLE_DEPRECATED
+}
+
+PhysicalBoneSimulator3D *PhysicalBone3D::get_simulator() const {
+ return Object::cast_to<PhysicalBoneSimulator3D>(ObjectDB::get_instance(simulator_id));
+}
+
+Skeleton3D *PhysicalBone3D::get_skeleton() const {
+ PhysicalBoneSimulator3D *simulator = get_simulator();
+ if (simulator) {
+ return simulator->get_skeleton();
+ }
+ return nullptr;
+}
+
#ifdef TOOLS_ENABLED
void PhysicalBone3D::_set_gizmo_move_joint(bool p_move_joint) {
gizmo_move_joint = p_move_joint;
@@ -1059,10 +1087,6 @@ const PhysicalBone3D::JointData *PhysicalBone3D::get_joint_data() const {
return joint_data;
}
-Skeleton3D *PhysicalBone3D::find_skeleton_parent() {
- return find_skeleton_parent(this);
-}
-
void PhysicalBone3D::set_joint_type(JointType p_joint_type) {
if (p_joint_type == get_joint_type()) {
return;
@@ -1269,21 +1293,22 @@ PhysicalBone3D::~PhysicalBone3D() {
}
void PhysicalBone3D::update_bone_id() {
- if (!parent_skeleton) {
+ PhysicalBoneSimulator3D *simulator = get_simulator();
+ if (!simulator) {
return;
}
- const int new_bone_id = parent_skeleton->find_bone(bone_name);
+ const int new_bone_id = simulator->find_bone(bone_name);
if (new_bone_id != bone_id) {
if (-1 != bone_id) {
// Assert the unbind from old node
- parent_skeleton->unbind_physical_bone_from_bone(bone_id);
+ simulator->unbind_physical_bone_from_bone(bone_id);
}
bone_id = new_bone_id;
- parent_skeleton->bind_physical_bone_to_bone(bone_id, this);
+ simulator->bind_physical_bone_to_bone(bone_id, this);
_fix_joint_offset();
reset_physics_simulation_state();
@@ -1292,10 +1317,12 @@ void PhysicalBone3D::update_bone_id() {
void PhysicalBone3D::update_offset() {
#ifdef TOOLS_ENABLED
- if (parent_skeleton) {
- Transform3D bone_transform(parent_skeleton->get_global_transform());
+ PhysicalBoneSimulator3D *simulator = get_simulator();
+ Skeleton3D *skeleton = get_skeleton();
+ if (simulator && skeleton) {
+ Transform3D bone_transform(skeleton->get_global_transform());
if (-1 != bone_id) {
- bone_transform *= parent_skeleton->get_bone_global_pose(bone_id);
+ bone_transform *= simulator->get_bone_global_pose(bone_id);
}
if (gizmo_move_joint) {
@@ -1309,7 +1336,7 @@ void PhysicalBone3D::update_offset() {
}
void PhysicalBone3D::_start_physics_simulation() {
- if (_internal_simulate_physics || !parent_skeleton) {
+ if (_internal_simulate_physics || !simulator_id.is_valid()) {
return;
}
reset_to_rest_position();
@@ -1323,23 +1350,22 @@ void PhysicalBone3D::_start_physics_simulation() {
}
void PhysicalBone3D::_stop_physics_simulation() {
- if (!parent_skeleton) {
- return;
- }
- if (parent_skeleton->get_animate_physical_bones()) {
- set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC);
- PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer());
- PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask());
- PhysicsServer3D::get_singleton()->body_set_collision_priority(get_rid(), get_collision_priority());
- } else {
- set_body_mode(PhysicsServer3D::BODY_MODE_STATIC);
- PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), 0);
- PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), 0);
- PhysicsServer3D::get_singleton()->body_set_collision_priority(get_rid(), 1.0);
+ PhysicalBoneSimulator3D *simulator = get_simulator();
+ if (simulator) {
+ if (simulator->is_simulating_physics()) {
+ set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC);
+ PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer());
+ PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask());
+ PhysicsServer3D::get_singleton()->body_set_collision_priority(get_rid(), get_collision_priority());
+ } else {
+ set_body_mode(PhysicsServer3D::BODY_MODE_STATIC);
+ PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), 0);
+ PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), 0);
+ PhysicsServer3D::get_singleton()->body_set_collision_priority(get_rid(), 1.0);
+ }
}
if (_internal_simulate_physics) {
PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), Callable());
- parent_skeleton->set_bone_global_pose_override(bone_id, Transform3D(), 0.0, false);
set_as_top_level(false);
_internal_simulate_physics = false;
}
diff --git a/scene/3d/physics/physical_bone_3d.h b/scene/3d/physics/physical_bone_3d.h
index 953400da2f..4765e41572 100644
--- a/scene/3d/physics/physical_bone_3d.h
+++ b/scene/3d/physics/physical_bone_3d.h
@@ -31,8 +31,10 @@
#ifndef PHYSICAL_BONE_3D_H
#define PHYSICAL_BONE_3D_H
+#include "scene/3d/physical_bone_simulator_3d.h"
#include "scene/3d/physics/physics_body_3d.h"
-#include "scene/3d/skeleton_3d.h"
+
+class PhysicalBoneSimulator3D;
class PhysicalBone3D : public PhysicsBody3D {
GDCLASS(PhysicalBone3D, PhysicsBody3D);
@@ -169,7 +171,7 @@ private:
Transform3D joint_offset;
RID joint;
- Skeleton3D *parent_skeleton = nullptr;
+ ObjectID simulator_id;
Transform3D body_offset;
Transform3D body_offset_inverse;
bool simulate_physics = false;
@@ -206,15 +208,19 @@ protected:
private:
void _sync_body_state(PhysicsDirectBodyState3D *p_state);
- static Skeleton3D *find_skeleton_parent(Node *p_parent);
void _update_joint_offset();
void _fix_joint_offset();
void _reload_joint();
+ void _update_simulator_path();
+
public:
void _on_bone_parent_changed();
+ PhysicalBoneSimulator3D *get_simulator() const;
+ Skeleton3D *get_skeleton() const;
+
void set_linear_velocity(const Vector3 &p_velocity);
Vector3 get_linear_velocity() const override;
@@ -231,7 +237,6 @@ public:
#endif
const JointData *get_joint_data() const;
- Skeleton3D *find_skeleton_parent();
int get_bone_id() const {
return bone_id;
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index f0ffb7b2d5..566801c9f7 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -32,10 +32,11 @@
#include "skeleton_3d.compat.inc"
#include "core/variant/type_info.h"
-#include "scene/3d/physics/physical_bone_3d.h"
-#include "scene/3d/physics/physics_body_3d.h"
#include "scene/resources/surface_tool.h"
#include "scene/scene_string_names.h"
+#ifndef DISABLE_DEPRECATED
+#include "scene/3d/physical_bone_simulator_3d.h"
+#endif // _DISABLE_DEPRECATED
void SkinReference::_skin_changed() {
if (skeleton_node) {
@@ -70,6 +71,12 @@ SkinReference::~SkinReference() {
bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
String path = p_path;
+#ifndef DISABLE_DEPRECATED
+ if (path.begins_with("animate_physical_bones")) {
+ set_animate_physical_bones(p_value);
+ }
+#endif
+
if (!path.begins_with("bones/")) {
return false;
}
@@ -134,6 +141,12 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
String path = p_path;
+#ifndef DISABLE_DEPRECATED
+ if (path.begins_with("animate_physical_bones")) {
+ r_ret = get_animate_physical_bones();
+ }
+#endif
+
if (!path.begins_with("bones/")) {
return false;
}
@@ -251,26 +264,70 @@ void Skeleton3D::_update_process_order() {
}
process_order_dirty = false;
+
+ emit_signal("bone_list_changed");
}
+#ifndef DISABLE_DEPRECATED
+void Skeleton3D::setup_simulator() {
+ PhysicalBoneSimulator3D *sim = memnew(PhysicalBoneSimulator3D);
+ simulator = sim;
+ sim->is_compat = true;
+ sim->set_active(false); // Don't run unneeded process.
+ add_child(sim);
+}
+
+void Skeleton3D::remove_simulator() {
+ remove_child(simulator);
+ memdelete(simulator);
+}
+#endif // _DISABLE_DEPRECATED
+
void Skeleton3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- if (dirty) {
- notification(NOTIFICATION_UPDATE_SKELETON);
- }
+ _process_changed();
+ _make_modifiers_dirty();
+ force_update_all_dirty_bones();
+#ifndef DISABLE_DEPRECATED
+ setup_simulator();
+#endif // _DISABLE_DEPRECATED
+ } break;
+#ifndef DISABLE_DEPRECATED
+ case NOTIFICATION_EXIT_TREE: {
+ remove_simulator();
} break;
+#endif // _DISABLE_DEPRECATED
case NOTIFICATION_UPDATE_SKELETON: {
- RenderingServer *rs = RenderingServer::get_singleton();
- Bone *bonesptr = bones.ptrw();
+ // Update bone transforms to apply unprocessed poses.
+ force_update_all_dirty_bones();
+
+ updating = true;
+ Bone *bonesptr = bones.ptrw();
int len = bones.size();
- dirty = false;
- // Update bone transforms.
- force_update_all_bone_transforms();
+ // Process modifiers.
+ _find_modifiers();
+ LocalVector<Transform3D> current_bone_poses;
+ LocalVector<Vector3> current_pose_positions;
+ LocalVector<Quaternion> current_pose_rotations;
+ LocalVector<Vector3> current_pose_scales;
+ LocalVector<Transform3D> current_bone_global_poses;
+ if (!modifiers.is_empty()) {
+ // Store unmodified bone poses.
+ for (int i = 0; i < len; i++) {
+ current_bone_poses.push_back(bones[i].pose_cache);
+ current_pose_positions.push_back(bones[i].pose_position);
+ current_pose_rotations.push_back(bones[i].pose_rotation);
+ current_pose_scales.push_back(bones[i].pose_scale);
+ current_bone_global_poses.push_back(bones[i].global_pose);
+ }
+ _process_modifiers();
+ }
// Update skins.
+ RenderingServer *rs = RenderingServer::get_singleton();
for (SkinReference *E : skin_bindings) {
const Skin *skin = E->skin.operator->();
RID skeleton = E->skeleton;
@@ -322,74 +379,78 @@ void Skeleton3D::_notification(int p_what) {
for (uint32_t i = 0; i < bind_count; i++) {
uint32_t bone_index = E->skin_bone_indices_ptrs[i];
ERR_CONTINUE(bone_index >= (uint32_t)len);
- rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i));
+ rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].global_pose * skin->get_bind_pose(i));
}
}
- emit_signal(SceneStringNames::get_singleton()->pose_updated);
- } break;
-#ifndef _3D_DISABLED
- case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- // This is active only if the skeleton animates the physical bones
- // and the state of the bone is not active.
- if (animate_physical_bones) {
- for (int i = 0; i < bones.size(); i += 1) {
- if (bones[i].physical_bone) {
- if (bones[i].physical_bone->is_simulating_physics() == false) {
- bones[i].physical_bone->reset_to_rest_position();
- }
- }
+ if (!modifiers.is_empty()) {
+ // Restore unmodified bone poses.
+ for (int i = 0; i < len; i++) {
+ bonesptr[i].pose_cache = current_bone_poses[i];
+ bonesptr[i].pose_position = current_pose_positions[i];
+ bonesptr[i].pose_rotation = current_pose_rotations[i];
+ bonesptr[i].pose_scale = current_pose_scales[i];
+ bonesptr[i].global_pose = current_bone_global_poses[i];
}
}
+
+ updating = false;
+ is_update_needed = false;
} break;
- case NOTIFICATION_READY: {
- if (Engine::get_singleton()->is_editor_hint()) {
- set_physics_process_internal(true);
+ case NOTIFICATION_INTERNAL_PROCESS:
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ _find_modifiers();
+ if (!modifiers.is_empty()) {
+ _update_deferred();
}
} break;
-#endif // _3D_DISABLED
}
}
-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;
+void Skeleton3D::set_modifier_callback_mode_process(Skeleton3D::ModifierCallbackModeProcess p_mode) {
+ if (modifier_callback_mode_process == p_mode) {
+ return;
}
- _make_dirty();
+ modifier_callback_mode_process = p_mode;
+ _process_changed();
}
-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;
- _make_dirty();
+Skeleton3D::ModifierCallbackModeProcess Skeleton3D::get_modifier_callback_mode_process() const {
+ return modifier_callback_mode_process;
}
-Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
- return bones[p_bone].global_pose_override;
+void Skeleton3D::_process_changed() {
+ if (modifier_callback_mode_process == MODIFIER_CALLBACK_MODE_PROCESS_IDLE) {
+ set_process_internal(true);
+ set_physics_process_internal(false);
+ } else if (modifier_callback_mode_process == MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS) {
+ set_process_internal(false);
+ set_physics_process_internal(true);
+ }
+}
+
+void Skeleton3D::_make_modifiers_dirty() {
+ modifiers_dirty = true;
+ _update_deferred();
}
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());
- if (dirty) {
- const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
- }
- return bones[p_bone].pose_global;
+ const_cast<Skeleton3D *>(this)->force_update_all_dirty_bones();
+ return bones[p_bone].global_pose;
}
-Transform3D Skeleton3D::get_bone_global_pose_no_override(int p_bone) const {
+void Skeleton3D::set_bone_global_pose(int p_bone, const Transform3D &p_pose) {
const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
- if (dirty) {
- const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
+ ERR_FAIL_INDEX(p_bone, bone_size);
+
+ Transform3D pt;
+ if (bones[p_bone].parent >= 0) {
+ pt = get_bone_global_pose(bones[p_bone].parent);
}
- return bones[p_bone].pose_global_no_override;
+ Transform3D t = pt.affine_inverse() * p_pose;
+ set_bone_pose(p_bone, t);
}
void Skeleton3D::set_motion_scale(float p_motion_scale) {
@@ -548,7 +609,7 @@ Transform3D Skeleton3D::get_bone_global_rest(int p_bone) const {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
if (rest_dirty) {
- const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
+ const_cast<Skeleton3D *>(this)->force_update_all_bone_transforms();
}
return bones[p_bone].global_rest;
}
@@ -588,6 +649,19 @@ void Skeleton3D::clear_bones() {
// Posing api
+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;
+ if (is_inside_tree()) {
+ _make_dirty();
+ }
+}
+
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);
@@ -654,7 +728,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());
- ((Skeleton3D *)this)->bones.write[p_bone].update_pose_cache();
+ const_cast<Skeleton3D *>(this)->bones.write[p_bone].update_pose_cache();
return bones[p_bone].pose_cache;
}
@@ -662,11 +736,15 @@ void Skeleton3D::_make_dirty() {
if (dirty) {
return;
}
+ dirty = true;
+ _update_deferred();
+}
- if (is_inside_tree()) {
- notify_deferred_thread_group(NOTIFICATION_UPDATE_SKELETON);
+void Skeleton3D::_update_deferred() {
+ if (!is_update_needed && !updating && is_inside_tree()) {
+ is_update_needed = true;
+ notify_deferred_thread_group(NOTIFICATION_UPDATE_SKELETON); // It must never be called more than once in a single frame.
}
- dirty = true;
}
void Skeleton3D::localize_rests() {
@@ -687,173 +765,6 @@ void Skeleton3D::localize_rests() {
}
}
-void Skeleton3D::set_animate_physical_bones(bool p_enabled) {
- animate_physical_bones = p_enabled;
-
- if (Engine::get_singleton()->is_editor_hint() == false) {
- bool sim = false;
- for (int i = 0; i < bones.size(); i += 1) {
- if (bones[i].physical_bone) {
- bones[i].physical_bone->reset_physics_simulation_state();
- if (bones[i].physical_bone->is_simulating_physics()) {
- sim = true;
- }
- }
- }
- set_physics_process_internal(sim == false && p_enabled);
- }
-}
-
-bool Skeleton3D::get_animate_physical_bones() const {
- return animate_physical_bones;
-}
-
-void Skeleton3D::bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX(p_bone, bone_size);
- ERR_FAIL_COND(bones[p_bone].physical_bone);
- ERR_FAIL_NULL(p_physical_bone);
- bones.write[p_bone].physical_bone = p_physical_bone;
-
- _rebuild_physical_bones_cache();
-}
-
-void Skeleton3D::unbind_physical_bone_from_bone(int p_bone) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].physical_bone = nullptr;
-
- _rebuild_physical_bones_cache();
-}
-
-PhysicalBone3D *Skeleton3D::get_physical_bone(int p_bone) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
-
- return bones[p_bone].physical_bone;
-}
-
-PhysicalBone3D *Skeleton3D::get_physical_bone_parent(int p_bone) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
-
- if (bones[p_bone].cache_parent_physical_bone) {
- return bones[p_bone].cache_parent_physical_bone;
- }
-
- return _get_physical_bone_parent(p_bone);
-}
-
-PhysicalBone3D *Skeleton3D::_get_physical_bone_parent(int p_bone) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
-
- const int parent_bone = bones[p_bone].parent;
- if (0 > parent_bone) {
- return nullptr;
- }
-
- PhysicalBone3D *pb = bones[parent_bone].physical_bone;
- if (pb) {
- return pb;
- } else {
- return get_physical_bone_parent(parent_bone);
- }
-}
-
-void Skeleton3D::_rebuild_physical_bones_cache() {
- const int b_size = bones.size();
- for (int i = 0; i < b_size; ++i) {
- PhysicalBone3D *parent_pb = _get_physical_bone_parent(i);
- if (parent_pb != bones[i].cache_parent_physical_bone) {
- bones.write[i].cache_parent_physical_bone = parent_pb;
- if (bones[i].physical_bone) {
- bones[i].physical_bone->_on_bone_parent_changed();
- }
- }
- }
-}
-
-void _pb_stop_simulation(Node *p_node) {
- for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
- _pb_stop_simulation(p_node->get_child(i));
- }
-
- PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node);
- if (pb) {
- pb->set_simulate_physics(false);
- }
-}
-
-void Skeleton3D::physical_bones_stop_simulation() {
- _pb_stop_simulation(this);
- if (Engine::get_singleton()->is_editor_hint() == false && animate_physical_bones) {
- set_physics_process_internal(true);
- }
-}
-
-void _pb_start_simulation(const Skeleton3D *p_skeleton, Node *p_node, const Vector<int> &p_sim_bones) {
- for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
- _pb_start_simulation(p_skeleton, p_node->get_child(i), p_sim_bones);
- }
-
- PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node);
- if (pb) {
- if (p_sim_bones.is_empty()) { // If no bones is specified, activate ragdoll on full body.
- pb->set_simulate_physics(true);
- } else {
- for (int i = p_sim_bones.size() - 1; 0 <= i; --i) {
- if (p_sim_bones[i] == pb->get_bone_id() || p_skeleton->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) {
- pb->set_simulate_physics(true);
- break;
- }
- }
- }
- }
-}
-
-void Skeleton3D::physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones) {
- set_physics_process_internal(false);
-
- Vector<int> sim_bones;
- if (p_bones.size() > 0) {
- sim_bones.resize(p_bones.size());
- int c = 0;
- for (int i = sim_bones.size() - 1; 0 <= i; --i) {
- int bone_id = find_bone(p_bones[i]);
- if (bone_id != -1) {
- sim_bones.write[c++] = bone_id;
- }
- }
- sim_bones.resize(c);
- }
-
- _pb_start_simulation(this, this, sim_bones);
-}
-
-void _physical_bones_add_remove_collision_exception(bool p_add, Node *p_node, RID p_exception) {
- for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
- _physical_bones_add_remove_collision_exception(p_add, p_node->get_child(i), p_exception);
- }
-
- CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_node);
- if (co) {
- if (p_add) {
- PhysicsServer3D::get_singleton()->body_add_collision_exception(co->get_rid(), p_exception);
- } else {
- PhysicsServer3D::get_singleton()->body_remove_collision_exception(co->get_rid(), p_exception);
- }
- }
-}
-
-void Skeleton3D::physical_bones_add_collision_exception(RID p_exception) {
- _physical_bones_add_remove_collision_exception(true, this, p_exception);
-}
-
-void Skeleton3D::physical_bones_remove_collision_exception(RID p_exception) {
- _physical_bones_add_remove_collision_exception(false, this, p_exception);
-}
-
void Skeleton3D::_skin_changed() {
_make_dirty();
}
@@ -927,18 +838,23 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
}
void Skeleton3D::force_update_all_dirty_bones() {
- if (dirty) {
- const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
+ if (!dirty) {
+ return;
}
+ force_update_all_bone_transforms();
}
void Skeleton3D::force_update_all_bone_transforms() {
_update_process_order();
-
for (int i = 0; i < parentless_bones.size(); i++) {
force_update_bone_children_transforms(parentless_bones[i]);
}
rest_dirty = false;
+ dirty = false;
+ if (updating) {
+ return;
+ }
+ emit_signal(SceneStringNames::get_singleton()->pose_updated);
}
void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
@@ -961,32 +877,43 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
Transform3D pose = b.pose_cache;
if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global * pose;
- b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * pose;
+ b.global_pose = bonesptr[b.parent].global_pose * pose;
} else {
- b.pose_global = pose;
- b.pose_global_no_override = pose;
+ b.global_pose = pose;
}
} else {
if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global * b.rest;
- b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * b.rest;
+ b.global_pose = bonesptr[b.parent].global_pose * b.rest;
} else {
- b.pose_global = b.rest;
- b.pose_global_no_override = b.rest;
+ b.global_pose = b.rest;
}
}
if (rest_dirty) {
b.global_rest = b.parent >= 0 ? bonesptr[b.parent].global_rest * b.rest : b.rest;
}
+#ifndef DISABLE_DEPRECATED
+ if (bone_enabled) {
+ Transform3D pose = b.pose_cache;
+ if (b.parent >= 0) {
+ b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * pose;
+ } else {
+ b.pose_global_no_override = pose;
+ }
+ } else {
+ if (b.parent >= 0) {
+ b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * b.rest;
+ } else {
+ b.pose_global_no_override = b.rest;
+ }
+ }
if (b.global_pose_override_amount >= CMP_EPSILON) {
- b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount);
+ b.global_pose = b.global_pose.interpolate_with(b.global_pose_override, b.global_pose_override_amount);
}
-
if (b.global_pose_override_reset) {
b.global_pose_override_amount = 0.0;
}
+#endif // _DISABLE_DEPRECATED
// Add the bone's children to the list of bones to be processed.
int child_bone_size = b.child_bones.size();
@@ -998,6 +925,72 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
}
}
+void Skeleton3D::_find_modifiers() {
+ if (!modifiers_dirty) {
+ return;
+ }
+ modifiers.clear();
+ for (int i = 0; i < get_child_count(); i++) {
+ SkeletonModifier3D *c = Object::cast_to<SkeletonModifier3D>(get_child(i));
+ if (c) {
+ modifiers.push_back(c->get_instance_id());
+ }
+ }
+ modifiers_dirty = false;
+}
+
+void Skeleton3D::_process_modifiers() {
+ for (const ObjectID &oid : modifiers) {
+ Object *t_obj = ObjectDB::get_instance(oid);
+ if (!t_obj) {
+ continue;
+ }
+ SkeletonModifier3D *mod = cast_to<SkeletonModifier3D>(t_obj);
+ if (!mod) {
+ continue;
+ }
+ real_t influence = mod->get_influence();
+ if (influence < 1.0) {
+ LocalVector<Transform3D> old_poses;
+ for (int i = 0; i < get_bone_count(); i++) {
+ old_poses.push_back(get_bone_pose(i));
+ }
+ mod->process_modification();
+ LocalVector<Transform3D> new_poses;
+ for (int i = 0; i < get_bone_count(); i++) {
+ new_poses.push_back(get_bone_pose(i));
+ }
+ for (int i = 0; i < get_bone_count(); i++) {
+ if (old_poses[i] == new_poses[i]) {
+ continue; // Avoid unneeded calculation.
+ }
+ set_bone_pose(i, old_poses[i].interpolate_with(new_poses[i], influence));
+ }
+ } else {
+ mod->process_modification();
+ }
+ force_update_all_dirty_bones();
+ }
+}
+
+void Skeleton3D::add_child_notify(Node *p_child) {
+ if (Object::cast_to<SkeletonModifier3D>(p_child)) {
+ _make_modifiers_dirty();
+ }
+}
+
+void Skeleton3D::move_child_notify(Node *p_child) {
+ if (Object::cast_to<SkeletonModifier3D>(p_child)) {
+ _make_modifiers_dirty();
+ }
+}
+
+void Skeleton3D::remove_child_notify(Node *p_child) {
+ if (Object::cast_to<SkeletonModifier3D>(p_child)) {
+ _make_modifiers_dirty();
+ }
+}
+
void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone);
ClassDB::bind_method(D_METHOD("find_bone", "name"), &Skeleton3D::find_bone);
@@ -1028,6 +1021,7 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_bones"), &Skeleton3D::clear_bones);
ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton3D::get_bone_pose);
+ ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_pose);
ClassDB::bind_method(D_METHOD("set_bone_pose_position", "bone_idx", "position"), &Skeleton3D::set_bone_pose_position);
ClassDB::bind_method(D_METHOD("set_bone_pose_rotation", "bone_idx", "rotation"), &Skeleton3D::set_bone_pose_rotation);
ClassDB::bind_method(D_METHOD("set_bone_pose_scale", "bone_idx", "scale"), &Skeleton3D::set_bone_pose_scale);
@@ -1042,11 +1036,8 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_bone_enabled", "bone_idx"), &Skeleton3D::is_bone_enabled);
ClassDB::bind_method(D_METHOD("set_bone_enabled", "bone_idx", "enabled"), &Skeleton3D::set_bone_enabled, DEFVAL(true));
- ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override);
- ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("get_bone_global_pose_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_override);
ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton3D::get_bone_global_pose);
- ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_no_override);
+ ClassDB::bind_method(D_METHOD("set_bone_global_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_global_pose);
ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms);
ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms);
@@ -1057,27 +1048,125 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_show_rest_only", "enabled"), &Skeleton3D::set_show_rest_only);
ClassDB::bind_method(D_METHOD("is_show_rest_only"), &Skeleton3D::is_show_rest_only);
- ClassDB::bind_method(D_METHOD("set_animate_physical_bones", "enabled"), &Skeleton3D::set_animate_physical_bones);
- ClassDB::bind_method(D_METHOD("get_animate_physical_bones"), &Skeleton3D::get_animate_physical_bones);
-
- ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton3D::physical_bones_stop_simulation);
- ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &Skeleton3D::physical_bones_start_simulation_on, DEFVAL(Array()));
- ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton3D::physical_bones_add_collision_exception);
- ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton3D::physical_bones_remove_collision_exception);
+ ClassDB::bind_method(D_METHOD("set_modifier_callback_mode_process", "mode"), &Skeleton3D::set_modifier_callback_mode_process);
+ ClassDB::bind_method(D_METHOD("get_modifier_callback_mode_process"), &Skeleton3D::get_modifier_callback_mode_process);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "motion_scale", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater"), "set_motion_scale", "get_motion_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_rest_only"), "set_show_rest_only", "is_show_rest_only");
-#ifndef _3D_DISABLED
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones");
-#endif // _3D_DISABLED
+
+ ADD_GROUP("Modifier", "modifier_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "modifier_callback_mode_process", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_modifier_callback_mode_process", "get_modifier_callback_mode_process");
ADD_SIGNAL(MethodInfo("pose_updated"));
ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx")));
ADD_SIGNAL(MethodInfo("bone_enabled_changed", PropertyInfo(Variant::INT, "bone_idx")));
+ ADD_SIGNAL(MethodInfo("bone_list_changed"));
ADD_SIGNAL(MethodInfo("show_rest_only_changed"));
BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
+ BIND_ENUM_CONSTANT(MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS);
+ BIND_ENUM_CONSTANT(MODIFIER_CALLBACK_MODE_PROCESS_IDLE);
+
+#ifndef DISABLE_DEPRECATED
+ ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override);
+ ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_bone_global_pose_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_override);
+ ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_no_override);
+
+ ClassDB::bind_method(D_METHOD("set_animate_physical_bones", "enabled"), &Skeleton3D::set_animate_physical_bones);
+ ClassDB::bind_method(D_METHOD("get_animate_physical_bones"), &Skeleton3D::get_animate_physical_bones);
+ ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton3D::physical_bones_stop_simulation);
+ ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &Skeleton3D::physical_bones_start_simulation_on, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton3D::physical_bones_add_collision_exception);
+ ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton3D::physical_bones_remove_collision_exception);
+#endif // _DISABLE_DEPRECATED
+}
+
+#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;
+ }
+ _make_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;
+ _make_dirty();
+}
+
+Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
+ return bones[p_bone].global_pose_override;
+}
+
+Transform3D Skeleton3D::get_bone_global_pose_no_override(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();
+ return bones[p_bone].pose_global_no_override;
+}
+
+Node *Skeleton3D::get_simulator() {
+ return simulator;
+}
+
+void Skeleton3D::set_animate_physical_bones(bool p_enabled) {
+ PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
+ if (!sim) {
+ return;
+ }
+ sim->set_active(p_enabled);
+}
+
+bool Skeleton3D::get_animate_physical_bones() const {
+ PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
+ if (!sim) {
+ return false;
+ }
+ return sim->is_active();
+}
+
+void Skeleton3D::physical_bones_stop_simulation() {
+ PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
+ if (!sim) {
+ return;
+ }
+ sim->physical_bones_stop_simulation();
+ sim->set_active(false);
+}
+
+void Skeleton3D::physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones) {
+ PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
+ if (!sim) {
+ return;
+ }
+ sim->set_active(true);
+ sim->physical_bones_start_simulation_on(p_bones);
+}
+
+void Skeleton3D::physical_bones_add_collision_exception(RID p_exception) {
+ PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
+ if (!sim) {
+ return;
+ }
+ sim->physical_bones_add_collision_exception(p_exception);
+}
+
+void Skeleton3D::physical_bones_remove_collision_exception(RID p_exception) {
+ PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
+ if (!sim) {
+ return;
+ }
+ sim->physical_bones_remove_collision_exception(p_exception);
}
+#endif // _DISABLE_DEPRECATED
Skeleton3D::Skeleton3D() {
}
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index 979e2e52b7..5b6f60dbd4 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -36,8 +36,8 @@
typedef int BoneId;
-class PhysicalBone3D;
class Skeleton3D;
+class SkeletonModifier3D;
class SkinReference : public RefCounted {
GDCLASS(SkinReference, RefCounted)
@@ -66,61 +66,71 @@ public:
class Skeleton3D : public Node3D {
GDCLASS(Skeleton3D, Node3D);
+#ifndef DISABLE_DEPRECATED
+ Node *simulator = nullptr;
+ void setup_simulator();
+ void remove_simulator();
+#endif // _DISABLE_DEPRECATED
+
+public:
+ enum ModifierCallbackModeProcess {
+ MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS,
+ MODIFIER_CALLBACK_MODE_PROCESS_IDLE,
+ };
+
private:
friend class SkinReference;
+ void _update_deferred();
+ bool is_update_needed = false; // Is updating reserved?
+ bool updating = false; // Is updating now?
+
struct Bone {
String name;
- bool enabled;
int parent;
+ Vector<int> child_bones;
Transform3D rest;
Transform3D global_rest;
- _FORCE_INLINE_ void update_pose_cache() {
+ bool enabled;
+ Transform3D pose_cache;
+ bool pose_cache_dirty = true;
+ Vector3 pose_position;
+ Quaternion pose_rotation;
+ Vector3 pose_scale = Vector3(1, 1, 1);
+ Transform3D global_pose;
+
+ void update_pose_cache() {
if (pose_cache_dirty) {
pose_cache.basis.set_quaternion_scale(pose_rotation, pose_scale);
pose_cache.origin = pose_position;
pose_cache_dirty = false;
}
}
- bool pose_cache_dirty = true;
- Transform3D pose_cache;
- Vector3 pose_position;
- Quaternion pose_rotation;
- Vector3 pose_scale = Vector3(1, 1, 1);
- Transform3D pose_global;
+#ifndef DISABLE_DEPRECATED
Transform3D pose_global_no_override;
-
real_t global_pose_override_amount = 0.0;
bool global_pose_override_reset = false;
Transform3D global_pose_override;
-
- PhysicalBone3D *physical_bone = nullptr;
- PhysicalBone3D *cache_parent_physical_bone = nullptr;
-
- Vector<int> child_bones;
+#endif // _DISABLE_DEPRECATED
Bone() {
parent = -1;
+ child_bones = Vector<int>();
enabled = true;
+#ifndef DISABLE_DEPRECATED
global_pose_override_amount = 0;
global_pose_override_reset = false;
-#ifndef _3D_DISABLED
- physical_bone = nullptr;
- cache_parent_physical_bone = nullptr;
-#endif // _3D_DISABLED
- child_bones = Vector<int>();
+#endif // _DISABLE_DEPRECATED
}
};
HashSet<SkinReference *> skin_bindings;
-
void _skin_changed();
- bool animate_physical_bones = true;
Vector<Bone> bones;
bool process_order_dirty = false;
@@ -138,6 +148,15 @@ private:
void _update_process_order();
+ // To process modifiers.
+ ModifierCallbackModeProcess modifier_callback_mode_process = MODIFIER_CALLBACK_MODE_PROCESS_IDLE;
+ LocalVector<ObjectID> modifiers;
+ bool modifiers_dirty = false;
+ void _find_modifiers();
+ void _process_modifiers();
+ void _process_changed();
+ void _make_modifiers_dirty();
+
#ifndef DISABLE_DEPRECATED
void _add_bone_bind_compat_88791(const String &p_name);
@@ -152,12 +171,16 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ virtual void add_child_notify(Node *p_child) override;
+ virtual void move_child_notify(Node *p_child) override;
+ virtual void remove_child_notify(Node *p_child) override;
+
public:
enum {
NOTIFICATION_UPDATE_SKELETON = 50
};
- // skeleton creation api
+ // Skeleton creation API
uint64_t get_version() const;
int add_bone(const String &p_name);
int find_bone(const String &p_name) const;
@@ -179,8 +202,6 @@ public:
void set_bone_rest(int p_bone, const Transform3D &p_rest);
Transform3D get_bone_rest(int p_bone) const;
Transform3D get_bone_global_rest(int p_bone) const;
- Transform3D get_bone_global_pose(int p_bone) const;
- Transform3D get_bone_global_pose_no_override(int p_bone) const;
void set_bone_enabled(int p_bone, bool p_enabled);
bool is_bone_enabled(int p_bone) const;
@@ -192,26 +213,23 @@ public:
void set_motion_scale(float p_motion_scale);
float get_motion_scale() const;
- // posing api
-
- void set_bone_pose_position(int p_bone, const Vector3 &p_position);
- void set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation);
- void set_bone_pose_scale(int p_bone, const Vector3 &p_scale);
-
+ // Posing API
Transform3D get_bone_pose(int p_bone) const;
-
Vector3 get_bone_pose_position(int p_bone) const;
Quaternion get_bone_pose_rotation(int p_bone) const;
Vector3 get_bone_pose_scale(int p_bone) const;
+ void set_bone_pose(int p_bone, const Transform3D &p_pose);
+ void set_bone_pose_position(int p_bone, const Vector3 &p_position);
+ void set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation);
+ void set_bone_pose_scale(int p_bone, const Vector3 &p_scale);
+
+ Transform3D get_bone_global_pose(int p_bone) const;
+ void set_bone_global_pose(int p_bone, const Transform3D &p_pose);
void reset_bone_pose(int p_bone);
void reset_bone_poses();
- void clear_bones_global_pose_override();
- Transform3D get_bone_global_pose_override(int p_bone) const;
- void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false);
-
- void localize_rests(); // used for loaders and tools
+ void localize_rests(); // Used for loaders and tools.
Ref<Skin> create_skin_from_rest_transforms();
@@ -221,31 +239,29 @@ public:
void force_update_all_bone_transforms();
void force_update_bone_children_transforms(int bone_idx);
- // Physical bone API
+ void set_modifier_callback_mode_process(ModifierCallbackModeProcess p_mode);
+ ModifierCallbackModeProcess get_modifier_callback_mode_process() const;
+
+#ifndef DISABLE_DEPRECATED
+ Transform3D get_bone_global_pose_no_override(int p_bone) const;
+ void clear_bones_global_pose_override();
+ Transform3D get_bone_global_pose_override(int p_bone) const;
+ void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false);
+ Node *get_simulator();
void set_animate_physical_bones(bool p_enabled);
bool get_animate_physical_bones() const;
-
- void bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone);
- void unbind_physical_bone_from_bone(int p_bone);
-
- PhysicalBone3D *get_physical_bone(int p_bone);
- PhysicalBone3D *get_physical_bone_parent(int p_bone);
-
-private:
- /// This is a slow API, so it's cached
- PhysicalBone3D *_get_physical_bone_parent(int p_bone);
- void _rebuild_physical_bones_cache();
-
-public:
void physical_bones_stop_simulation();
void physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones);
void physical_bones_add_collision_exception(RID p_exception);
void physical_bones_remove_collision_exception(RID p_exception);
+#endif // _DISABLE_DEPRECATED
public:
Skeleton3D();
~Skeleton3D();
};
+VARIANT_ENUM_CAST(Skeleton3D::ModifierCallbackModeProcess);
+
#endif // SKELETON_3D_H
diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp
index 286268b4a2..9581ae58d8 100644
--- a/scene/3d/skeleton_ik_3d.cpp
+++ b/scene/3d/skeleton_ik_3d.cpp
@@ -30,8 +30,6 @@
#include "skeleton_ik_3d.h"
-#ifndef _3D_DISABLED
-
FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) {
for (int i = children.size() - 1; 0 <= i; --i) {
if (p_bone_id == children[i].bone) {
@@ -236,46 +234,21 @@ void FabrikInverseKinematic::set_goal(Task *p_task, const Transform3D &p_goal) {
p_task->goal_global_transform = p_goal;
}
-void FabrikInverseKinematic::make_goal(Task *p_task, const Transform3D &p_inverse_transf, real_t blending_delta) {
- if (blending_delta >= 0.99f) {
- // Update the end_effector (local transform) without blending
- p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform;
- } else {
- // End effector in local transform
- const Transform3D end_effector_pose(p_task->skeleton->get_bone_global_pose_no_override(p_task->end_effectors[0].tip_bone));
-
- // Update the end_effector (local transform) by blending with current pose
- p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta);
- }
+void FabrikInverseKinematic::make_goal(Task *p_task, const Transform3D &p_inverse_transf) {
+ // Update the end_effector (local transform) by blending with current pose
+ p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform;
}
-void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) {
- if (blending_delta <= 0.01f) {
- // Before skipping, make sure we undo the global pose overrides
- ChainItem *ci(&p_task->chain.chain_root);
- while (ci) {
- p_task->skeleton->set_bone_global_pose_override(ci->bone, ci->initial_transform, 0.0, false);
-
- if (!ci->children.is_empty()) {
- ci = &ci->children.write[0];
- } else {
- ci = nullptr;
- }
- }
-
- return; // Skip solving
- }
-
+void FabrikInverseKinematic::solve(Task *p_task, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) {
// Update the initial root transform so its synced with any animation changes
_update_chain(p_task->skeleton, &p_task->chain.chain_root);
- p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform3D(), 0.0, false);
Vector3 origin_pos = p_task->skeleton->get_bone_global_pose(p_task->chain.chain_root.bone).origin;
- make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse(), blending_delta);
+ make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse());
if (p_use_magnet && p_task->chain.middle_chain_item) {
- p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.lerp(p_magnet_position, blending_delta);
+ p_task->chain.magnet_position = p_magnet_position;
solve_simple(p_task, true, origin_pos);
}
solve_simple(p_task, false, origin_pos);
@@ -303,8 +276,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
// IK should not affect scale, so undo any scaling
new_bone_pose.basis.orthonormalize();
new_bone_pose.basis.scale(p_task->skeleton->get_bone_global_pose(ci->bone).basis.get_scale());
-
- p_task->skeleton->set_bone_global_pose_override(ci->bone, new_bone_pose, 1.0, true);
+ p_task->skeleton->set_bone_global_pose(ci->bone, Transform3D(new_bone_pose.basis, p_task->skeleton->get_bone_global_pose(ci->bone).origin));
if (!ci->children.is_empty()) {
ci = &ci->children.write[0];
@@ -319,7 +291,7 @@ void FabrikInverseKinematic::_update_chain(const Skeleton3D *p_sk, ChainItem *p_
return;
}
- p_chain_item->initial_transform = p_sk->get_bone_global_pose_no_override(p_chain_item->bone);
+ p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone);
p_chain_item->current_pos = p_chain_item->initial_transform.origin;
ChainItem *items = p_chain_item->children.ptrw();
@@ -329,8 +301,10 @@ void FabrikInverseKinematic::_update_chain(const Skeleton3D *p_sk, ChainItem *p_
}
void SkeletonIK3D::_validate_property(PropertyInfo &p_property) const {
+ SkeletonModifier3D::_validate_property(p_property);
+
if (p_property.name == "root_bone" || p_property.name == "tip_bone") {
- Skeleton3D *skeleton = get_parent_skeleton();
+ Skeleton3D *skeleton = get_skeleton();
if (skeleton) {
String names("--,");
for (int i = 0; i < skeleton->get_bone_count(); i++) {
@@ -356,9 +330,6 @@ void SkeletonIK3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tip_bone", "tip_bone"), &SkeletonIK3D::set_tip_bone);
ClassDB::bind_method(D_METHOD("get_tip_bone"), &SkeletonIK3D::get_tip_bone);
- ClassDB::bind_method(D_METHOD("set_interpolation", "interpolation"), &SkeletonIK3D::set_interpolation);
- ClassDB::bind_method(D_METHOD("get_interpolation"), &SkeletonIK3D::get_interpolation);
-
ClassDB::bind_method(D_METHOD("set_target_transform", "target"), &SkeletonIK3D::set_target_transform);
ClassDB::bind_method(D_METHOD("get_target_transform"), &SkeletonIK3D::get_target_transform);
@@ -388,7 +359,6 @@ void SkeletonIK3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "root_bone"), "set_root_bone", "get_root_bone");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "tip_bone"), "set_tip_bone", "get_tip_bone");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_interpolation", "get_interpolation");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "target", PROPERTY_HINT_NONE, "suffix:m"), "set_target_transform", "get_target_transform");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_tip_basis"), "set_override_tip_basis", "is_override_tip_basis");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_magnet"), "set_use_magnet", "is_using_magnet");
@@ -398,21 +368,21 @@ void SkeletonIK3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_iterations"), "set_max_iterations", "get_max_iterations");
}
+void SkeletonIK3D::_process_modification() {
+ if (!internal_active) {
+ return;
+ }
+ if (target_node_override_ref) {
+ reload_goal();
+ }
+ _solve_chain();
+}
+
void SkeletonIK3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- skeleton_ref = Object::cast_to<Skeleton3D>(get_parent());
- set_process_priority(1);
reload_chain();
} break;
-
- case NOTIFICATION_INTERNAL_PROCESS: {
- if (target_node_override_ref) {
- reload_goal();
- }
- _solve_chain();
- } break;
-
case NOTIFICATION_EXIT_TREE: {
stop();
} break;
@@ -445,14 +415,6 @@ StringName SkeletonIK3D::get_tip_bone() const {
return tip_bone;
}
-void SkeletonIK3D::set_interpolation(real_t p_interpolation) {
- interpolation = p_interpolation;
-}
-
-real_t SkeletonIK3D::get_interpolation() const {
- return interpolation;
-}
-
void SkeletonIK3D::set_target_transform(const Transform3D &p_target) {
target = p_target;
reload_goal();
@@ -505,33 +467,25 @@ void SkeletonIK3D::set_max_iterations(int p_iterations) {
}
Skeleton3D *SkeletonIK3D::get_parent_skeleton() const {
- return cast_to<Skeleton3D>(skeleton_ref.get_validated_object());
+ return get_skeleton();
}
bool SkeletonIK3D::is_running() {
- return is_processing_internal();
+ return internal_active;
}
void SkeletonIK3D::start(bool p_one_time) {
if (p_one_time) {
- set_process_internal(false);
-
- if (target_node_override_ref) {
- reload_goal();
- }
-
- _solve_chain();
+ internal_active = true;
+ SkeletonModifier3D::process_modification();
+ internal_active = false;
} else {
- set_process_internal(true);
+ internal_active = true;
}
}
void SkeletonIK3D::stop() {
- set_process_internal(false);
- Skeleton3D *skeleton = get_parent_skeleton();
- if (skeleton) {
- skeleton->clear_bones_global_pose_override();
- }
+ internal_active = false;
}
Transform3D SkeletonIK3D::_get_target_transform() {
@@ -551,7 +505,7 @@ void SkeletonIK3D::reload_chain() {
FabrikInverseKinematic::free_task(task);
task = nullptr;
- Skeleton3D *skeleton = get_parent_skeleton();
+ Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
}
@@ -575,7 +529,5 @@ void SkeletonIK3D::_solve_chain() {
if (!task) {
return;
}
- FabrikInverseKinematic::solve(task, interpolation, override_tip_basis, use_magnet, magnet_position);
+ FabrikInverseKinematic::solve(task, override_tip_basis, use_magnet, magnet_position);
}
-
-#endif // _3D_DISABLED
diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h
index 0a03e96905..eff018f2cc 100644
--- a/scene/3d/skeleton_ik_3d.h
+++ b/scene/3d/skeleton_ik_3d.h
@@ -31,9 +31,7 @@
#ifndef SKELETON_IK_3D_H
#define SKELETON_IK_3D_H
-#ifndef _3D_DISABLED
-
-#include "scene/3d/skeleton_3d.h"
+#include "scene/3d/skeleton_modifier_3d.h"
class FabrikInverseKinematic {
struct EndEffector {
@@ -111,18 +109,19 @@ public:
static void free_task(Task *p_task);
// The goal of chain should be always in local space
static void set_goal(Task *p_task, const Transform3D &p_goal);
- static void make_goal(Task *p_task, const Transform3D &p_inverse_transf, real_t blending_delta);
- static void solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position);
+ static void make_goal(Task *p_task, const Transform3D &p_inverse_transf);
+ static void solve(Task *p_task, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position);
static void _update_chain(const Skeleton3D *p_skeleton, ChainItem *p_chain_item);
};
-class SkeletonIK3D : public Node {
- GDCLASS(SkeletonIK3D, Node);
+class SkeletonIK3D : public SkeletonModifier3D {
+ GDCLASS(SkeletonIK3D, SkeletonModifier3D);
+
+ bool internal_active = false;
StringName root_bone;
StringName tip_bone;
- real_t interpolation = 1.0;
Transform3D target;
NodePath target_node_path_override;
bool override_tip_basis = true;
@@ -132,7 +131,6 @@ class SkeletonIK3D : public Node {
real_t min_distance = 0.01;
int max_iterations = 10;
- Variant skeleton_ref = Variant();
Variant target_node_override_ref = Variant();
FabrikInverseKinematic::Task *task = nullptr;
@@ -142,6 +140,8 @@ protected:
static void _bind_methods();
virtual void _notification(int p_what);
+ virtual void _process_modification() override;
+
public:
SkeletonIK3D();
virtual ~SkeletonIK3D();
@@ -152,9 +152,6 @@ public:
void set_tip_bone(const StringName &p_tip_bone);
StringName get_tip_bone() const;
- void set_interpolation(real_t p_interpolation);
- real_t get_interpolation() const;
-
void set_target_transform(const Transform3D &p_target);
const Transform3D &get_target_transform() const;
@@ -190,6 +187,4 @@ private:
void _solve_chain();
};
-#endif // _3D_DISABLED
-
#endif // SKELETON_IK_3D_H
diff --git a/scene/3d/skeleton_modifier_3d.cpp b/scene/3d/skeleton_modifier_3d.cpp
new file mode 100644
index 0000000000..96e3e33841
--- /dev/null
+++ b/scene/3d/skeleton_modifier_3d.cpp
@@ -0,0 +1,139 @@
+/**************************************************************************/
+/* skeleton_modifier_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "skeleton_modifier_3d.h"
+
+void SkeletonModifier3D::_validate_property(PropertyInfo &p_property) const {
+ //
+}
+
+PackedStringArray SkeletonModifier3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
+ if (skeleton_id.is_null()) {
+ warnings.push_back(RTR("Skeleton3D node not set! SkeletonModifier3D must be child of Skeleton3D or set a path to an external skeleton."));
+ }
+ return warnings;
+}
+
+/* Skeleton3D */
+
+Skeleton3D *SkeletonModifier3D::get_skeleton() const {
+ return Object::cast_to<Skeleton3D>(ObjectDB::get_instance(skeleton_id));
+}
+
+void SkeletonModifier3D::_update_skeleton_path() {
+ skeleton_id = ObjectID();
+
+ // Make sure parent is a Skeleton3D.
+ Skeleton3D *sk = Object::cast_to<Skeleton3D>(get_parent());
+ if (sk) {
+ skeleton_id = sk->get_instance_id();
+ }
+}
+
+void SkeletonModifier3D::_update_skeleton() {
+ if (!is_inside_tree()) {
+ return;
+ }
+ Skeleton3D *old_sk = get_skeleton();
+ _update_skeleton_path();
+ Skeleton3D *new_sk = get_skeleton();
+ if (old_sk != new_sk) {
+ _skeleton_changed(old_sk, new_sk);
+ }
+ update_configuration_warnings();
+}
+
+void SkeletonModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) {
+ //
+}
+
+/* Process */
+
+void SkeletonModifier3D::set_active(bool p_active) {
+ if (active == p_active) {
+ return;
+ }
+ active = p_active;
+ _set_active(active);
+}
+
+bool SkeletonModifier3D::is_active() const {
+ return active;
+}
+
+void SkeletonModifier3D::_set_active(bool p_active) {
+ //
+}
+
+void SkeletonModifier3D::set_influence(real_t p_influence) {
+ influence = p_influence;
+}
+
+real_t SkeletonModifier3D::get_influence() const {
+ return influence;
+}
+
+void SkeletonModifier3D::process_modification() {
+ if (!active) {
+ return;
+ }
+ _process_modification();
+ emit_signal(SNAME("modification_processed"));
+}
+
+void SkeletonModifier3D::_process_modification() {
+ //
+}
+
+void SkeletonModifier3D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_PARENTED: {
+ _update_skeleton();
+ } break;
+ }
+}
+
+void SkeletonModifier3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_active", "active"), &SkeletonModifier3D::set_active);
+ ClassDB::bind_method(D_METHOD("is_active"), &SkeletonModifier3D::is_active);
+
+ ClassDB::bind_method(D_METHOD("set_influence", "influence"), &SkeletonModifier3D::set_influence);
+ ClassDB::bind_method(D_METHOD("get_influence"), &SkeletonModifier3D::get_influence);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "influence", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_influence", "get_influence");
+
+ ADD_SIGNAL(MethodInfo("modification_processed"));
+}
+
+SkeletonModifier3D::SkeletonModifier3D() {
+}
diff --git a/scene/3d/skeleton_modifier_3d.h b/scene/3d/skeleton_modifier_3d.h
new file mode 100644
index 0000000000..25c09f3b93
--- /dev/null
+++ b/scene/3d/skeleton_modifier_3d.h
@@ -0,0 +1,81 @@
+/**************************************************************************/
+/* skeleton_modifier_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef SKELETON_MODIFIER_3D_H
+#define SKELETON_MODIFIER_3D_H
+
+#include "scene/3d/node_3d.h"
+
+#include "scene/3d/skeleton_3d.h"
+#include "scene/animation/animation_mixer.h"
+
+class SkeletonModifier3D : public Node3D {
+ GDCLASS(SkeletonModifier3D, Node3D);
+
+ void rebind();
+
+protected:
+ bool active = true;
+ real_t influence = 1.0;
+
+ // Cache them for the performance reason since finding node with NodePath is slow.
+ ObjectID skeleton_id;
+
+ void _update_skeleton();
+ void _update_skeleton_path();
+
+ virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new);
+
+ void _validate_property(PropertyInfo &p_property) const;
+ void _notification(int p_what);
+ static void _bind_methods();
+
+ virtual void _set_active(bool p_active);
+
+ virtual void _process_modification();
+
+public:
+ virtual PackedStringArray get_configuration_warnings() const override;
+ virtual bool has_process() const { return false; } // Return true if modifier needs to modify bone pose without external animation such as physics, jiggle and etc.
+
+ void set_active(bool p_active);
+ bool is_active() const;
+
+ void set_influence(real_t p_influence);
+ real_t get_influence() const;
+
+ Skeleton3D *get_skeleton() const;
+
+ void process_modification();
+
+ SkeletonModifier3D();
+};
+
+#endif // SKELETON_MODIFIER_3D_H
diff --git a/scene/3d/xr_body_modifier_3d.cpp b/scene/3d/xr_body_modifier_3d.cpp
index ac648d66d0..0099784a05 100644
--- a/scene/3d/xr_body_modifier_3d.cpp
+++ b/scene/3d/xr_body_modifier_3d.cpp
@@ -38,9 +38,6 @@ void XRBodyModifier3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_body_tracker", "tracker_name"), &XRBodyModifier3D::set_body_tracker);
ClassDB::bind_method(D_METHOD("get_body_tracker"), &XRBodyModifier3D::get_body_tracker);
- ClassDB::bind_method(D_METHOD("set_target", "target"), &XRBodyModifier3D::set_target);
- ClassDB::bind_method(D_METHOD("get_target"), &XRBodyModifier3D::get_target);
-
ClassDB::bind_method(D_METHOD("set_body_update", "body_update"), &XRBodyModifier3D::set_body_update);
ClassDB::bind_method(D_METHOD("get_body_update"), &XRBodyModifier3D::get_body_update);
@@ -51,7 +48,6 @@ void XRBodyModifier3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_show_when_tracked"), &XRBodyModifier3D::get_show_when_tracked);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "body_tracker", PROPERTY_HINT_ENUM_SUGGESTION, "/user/body"), "set_body_tracker", "get_body_tracker");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_target", "get_target");
ADD_PROPERTY(PropertyInfo(Variant::INT, "body_update", PROPERTY_HINT_FLAGS, "Upper Body,Lower Body,Hands"), "set_body_update", "get_body_update");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_when_tracked"), "set_show_when_tracked", "get_show_when_tracked");
@@ -73,18 +69,6 @@ StringName XRBodyModifier3D::get_body_tracker() const {
return tracker_name;
}
-void XRBodyModifier3D::set_target(const NodePath &p_target) {
- target = p_target;
-
- if (is_inside_tree()) {
- _get_joint_data();
- }
-}
-
-NodePath XRBodyModifier3D::get_target() const {
- return target;
-}
-
void XRBodyModifier3D::set_body_update(BitField<BodyUpdate> p_body_update) {
body_update = p_body_update;
}
@@ -110,20 +94,6 @@ bool XRBodyModifier3D::get_show_when_tracked() const {
return show_when_tracked;
}
-Skeleton3D *XRBodyModifier3D::get_skeleton() {
- if (!has_node(target)) {
- return nullptr;
- }
-
- Node *node = get_node(target);
- if (!node) {
- return nullptr;
- }
-
- Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
- return skeleton;
-}
-
void XRBodyModifier3D::_get_joint_data() {
// Table of Godot Humanoid bone names.
static const String bone_names[XRBodyTracker::JOINT_MAX] = {
@@ -281,7 +251,7 @@ void XRBodyModifier3D::_get_joint_data() {
}
}
-void XRBodyModifier3D::_update_skeleton() {
+void XRBodyModifier3D::_process_modification() {
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
@@ -379,6 +349,10 @@ void XRBodyModifier3D::_tracker_changed(const StringName &p_tracker_name, const
}
}
+void XRBodyModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) {
+ _get_joint_data();
+}
+
void XRBodyModifier3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@@ -388,10 +362,7 @@ void XRBodyModifier3D::_notification(int p_what) {
xr_server->connect("body_tracker_updated", callable_mp(this, &XRBodyModifier3D::_tracker_changed));
xr_server->connect("body_tracker_removed", callable_mp(this, &XRBodyModifier3D::_tracker_changed).bind(Ref<XRBodyTracker>()));
}
-
_get_joint_data();
-
- set_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
XRServer *xr_server = XRServer::get_singleton();
@@ -400,17 +371,11 @@ void XRBodyModifier3D::_notification(int p_what) {
xr_server->disconnect("body_tracker_updated", callable_mp(this, &XRBodyModifier3D::_tracker_changed));
xr_server->disconnect("body_tracker_removed", callable_mp(this, &XRBodyModifier3D::_tracker_changed).bind(Ref<XRBodyTracker>()));
}
-
- set_process_internal(false);
-
for (int i = 0; i < XRBodyTracker::JOINT_MAX; i++) {
joints[i].bone = -1;
joints[i].parent_joint = -1;
}
} break;
- case NOTIFICATION_INTERNAL_PROCESS: {
- _update_skeleton();
- } break;
default: {
} break;
}
diff --git a/scene/3d/xr_body_modifier_3d.h b/scene/3d/xr_body_modifier_3d.h
index 89ac69b6b0..03b1c07d53 100644
--- a/scene/3d/xr_body_modifier_3d.h
+++ b/scene/3d/xr_body_modifier_3d.h
@@ -31,7 +31,7 @@
#ifndef XR_BODY_MODIFIER_3D_H
#define XR_BODY_MODIFIER_3D_H
-#include "scene/3d/node_3d.h"
+#include "scene/3d/skeleton_modifier_3d.h"
#include "servers/xr/xr_body_tracker.h"
class Skeleton3D;
@@ -41,8 +41,8 @@ class Skeleton3D;
data from an XRBodyTracker instance.
*/
-class XRBodyModifier3D : public Node3D {
- GDCLASS(XRBodyModifier3D, Node3D);
+class XRBodyModifier3D : public SkeletonModifier3D {
+ GDCLASS(XRBodyModifier3D, SkeletonModifier3D);
public:
enum BodyUpdate {
@@ -60,9 +60,6 @@ public:
void set_body_tracker(const StringName &p_tracker_name);
StringName get_body_tracker() const;
- void set_target(const NodePath &p_target);
- NodePath get_target() const;
-
void set_body_update(BitField<BodyUpdate> p_body_update);
BitField<BodyUpdate> get_body_update() const;
@@ -77,6 +74,9 @@ public:
protected:
static void _bind_methods();
+ virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override;
+ virtual void _process_modification() override;
+
private:
struct JointData {
int bone = -1;
@@ -84,15 +84,12 @@ private:
};
StringName tracker_name = "/user/body";
- NodePath target;
BitField<BodyUpdate> body_update = BODY_UPDATE_UPPER_BODY | BODY_UPDATE_LOWER_BODY | BODY_UPDATE_HANDS;
BoneUpdate bone_update = BONE_UPDATE_FULL;
bool show_when_tracked = true;
JointData joints[XRBodyTracker::JOINT_MAX];
- Skeleton3D *get_skeleton();
void _get_joint_data();
- void _update_skeleton();
void _tracker_changed(const StringName &p_tracker_name, const Ref<XRBodyTracker> &p_tracker);
};
diff --git a/scene/3d/xr_hand_modifier_3d.cpp b/scene/3d/xr_hand_modifier_3d.cpp
index 1e1449b54b..7fecb53008 100644
--- a/scene/3d/xr_hand_modifier_3d.cpp
+++ b/scene/3d/xr_hand_modifier_3d.cpp
@@ -30,7 +30,6 @@
#include "xr_hand_modifier_3d.h"
-#include "scene/3d/skeleton_3d.h"
#include "servers/xr/xr_pose.h"
#include "servers/xr_server.h"
@@ -38,14 +37,10 @@ void XRHandModifier3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hand_tracker", "tracker_name"), &XRHandModifier3D::set_hand_tracker);
ClassDB::bind_method(D_METHOD("get_hand_tracker"), &XRHandModifier3D::get_hand_tracker);
- ClassDB::bind_method(D_METHOD("set_target", "target"), &XRHandModifier3D::set_target);
- ClassDB::bind_method(D_METHOD("get_target"), &XRHandModifier3D::get_target);
-
ClassDB::bind_method(D_METHOD("set_bone_update", "bone_update"), &XRHandModifier3D::set_bone_update);
ClassDB::bind_method(D_METHOD("get_bone_update"), &XRHandModifier3D::get_bone_update);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "hand_tracker", PROPERTY_HINT_ENUM_SUGGESTION, "/user/left,/user/right"), "set_hand_tracker", "get_hand_tracker");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_target", "get_target");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update");
BIND_ENUM_CONSTANT(BONE_UPDATE_FULL);
@@ -61,18 +56,6 @@ StringName XRHandModifier3D::get_hand_tracker() const {
return tracker_name;
}
-void XRHandModifier3D::set_target(const NodePath &p_target) {
- target = p_target;
-
- if (is_inside_tree()) {
- _get_joint_data();
- }
-}
-
-NodePath XRHandModifier3D::get_target() const {
- return target;
-}
-
void XRHandModifier3D::set_bone_update(BoneUpdate p_bone_update) {
ERR_FAIL_INDEX(p_bone_update, BONE_UPDATE_MAX);
bone_update = p_bone_update;
@@ -82,21 +65,11 @@ XRHandModifier3D::BoneUpdate XRHandModifier3D::get_bone_update() const {
return bone_update;
}
-Skeleton3D *XRHandModifier3D::get_skeleton() {
- if (!has_node(target)) {
- return nullptr;
- }
-
- Node *node = get_node(target);
- if (!node) {
- return nullptr;
+void XRHandModifier3D::_get_joint_data() {
+ if (!is_inside_tree()) {
+ return;
}
- Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
- return skeleton;
-}
-
-void XRHandModifier3D::_get_joint_data() {
// Table of bone names for different rig types.
static const String bone_names[XRHandTracker::HAND_JOINT_MAX] = {
"Palm",
@@ -197,7 +170,7 @@ void XRHandModifier3D::_get_joint_data() {
}
}
-void XRHandModifier3D::_update_skeleton() {
+void XRHandModifier3D::_process_modification() {
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
@@ -276,6 +249,10 @@ void XRHandModifier3D::_tracker_changed(StringName p_tracker_name, const Ref<XRH
}
}
+void XRHandModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) {
+ _get_joint_data();
+}
+
void XRHandModifier3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@@ -287,8 +264,6 @@ void XRHandModifier3D::_notification(int p_what) {
}
_get_joint_data();
-
- set_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
XRServer *xr_server = XRServer::get_singleton();
@@ -298,16 +273,11 @@ void XRHandModifier3D::_notification(int p_what) {
xr_server->disconnect("hand_tracker_removed", callable_mp(this, &XRHandModifier3D::_tracker_changed).bind(Ref<XRHandTracker>()));
}
- set_process_internal(false);
-
for (int i = 0; i < XRHandTracker::HAND_JOINT_MAX; i++) {
joints[i].bone = -1;
joints[i].parent_joint = -1;
}
} break;
- case NOTIFICATION_INTERNAL_PROCESS: {
- _update_skeleton();
- } break;
default: {
} break;
}
diff --git a/scene/3d/xr_hand_modifier_3d.h b/scene/3d/xr_hand_modifier_3d.h
index 2bc30d42d4..9f7ce45c9d 100644
--- a/scene/3d/xr_hand_modifier_3d.h
+++ b/scene/3d/xr_hand_modifier_3d.h
@@ -31,18 +31,16 @@
#ifndef XR_HAND_MODIFIER_3D_H
#define XR_HAND_MODIFIER_3D_H
-#include "scene/3d/node_3d.h"
+#include "scene/3d/skeleton_modifier_3d.h"
#include "servers/xr/xr_hand_tracker.h"
-class Skeleton3D;
-
/**
The XRHandModifier3D node drives a hand skeleton using hand tracking
data from an XRHandTracking instance.
*/
-class XRHandModifier3D : public Node3D {
- GDCLASS(XRHandModifier3D, Node3D);
+class XRHandModifier3D : public SkeletonModifier3D {
+ GDCLASS(XRHandModifier3D, SkeletonModifier3D);
public:
enum BoneUpdate {
@@ -54,9 +52,6 @@ public:
void set_hand_tracker(const StringName &p_tracker_name);
StringName get_hand_tracker() const;
- void set_target(const NodePath &p_target);
- NodePath get_target() const;
-
void set_bone_update(BoneUpdate p_bone_update);
BoneUpdate get_bone_update() const;
@@ -65,6 +60,9 @@ public:
protected:
static void _bind_methods();
+ virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override;
+ virtual void _process_modification() override;
+
private:
struct JointData {
int bone = -1;
@@ -72,13 +70,10 @@ private:
};
StringName tracker_name = "/user/left";
- NodePath target;
BoneUpdate bone_update = BONE_UPDATE_FULL;
JointData joints[XRHandTracker::HAND_JOINT_MAX];
- Skeleton3D *get_skeleton();
void _get_joint_data();
- void _update_skeleton();
void _tracker_changed(StringName p_tracker_name, const Ref<XRHandTracker> &p_tracker);
};
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index 22f919ad36..757ac68e46 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -42,6 +42,7 @@
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_3d.h"
+#include "scene/3d/skeleton_modifier_3d.h"
#endif // _3D_DISABLED
#ifdef TOOLS_ENABLED
@@ -484,10 +485,6 @@ void AnimationMixer::set_callback_mode_process(AnimationCallbackModeProcess p_mo
if (was_active) {
set_active(true);
}
-
-#ifdef TOOLS_ENABLED
- emit_signal(SNAME("mixer_updated"));
-#endif // TOOLS_ENABLED
}
AnimationMixer::AnimationCallbackModeProcess AnimationMixer::get_callback_mode_process() const {
@@ -496,9 +493,7 @@ AnimationMixer::AnimationCallbackModeProcess AnimationMixer::get_callback_mode_p
void AnimationMixer::set_callback_mode_method(AnimationCallbackModeMethod p_mode) {
callback_mode_method = p_mode;
-#ifdef TOOLS_ENABLED
emit_signal(SNAME("mixer_updated"));
-#endif // TOOLS_ENABLED
}
AnimationMixer::AnimationCallbackModeMethod AnimationMixer::get_callback_mode_method() const {
@@ -507,9 +502,7 @@ AnimationMixer::AnimationCallbackModeMethod AnimationMixer::get_callback_mode_me
void AnimationMixer::set_callback_mode_discrete(AnimationCallbackModeDiscrete p_mode) {
callback_mode_discrete = p_mode;
-#ifdef TOOLS_ENABLED
emit_signal(SNAME("mixer_updated"));
-#endif // TOOLS_ENABLED
}
AnimationMixer::AnimationCallbackModeDiscrete AnimationMixer::get_callback_mode_discrete() const {
@@ -930,6 +923,7 @@ void AnimationMixer::_process_animation(double p_delta, bool p_update_only) {
_blend_process(p_delta, p_update_only);
_blend_apply();
_blend_post_process();
+ emit_signal(SNAME("mixer_applied"));
};
clear_animation_instances();
}
@@ -2228,19 +2222,16 @@ void AnimationMixer::_bind_methods() {
ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationMixer::advance);
GDVIRTUAL_BIND(_post_process_key_value, "animation", "track", "value", "object_id", "object_sub_idx");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deterministic"), "set_deterministic", "is_deterministic");
+ /* ---- Capture feature ---- */
+ ClassDB::bind_method(D_METHOD("capture", "name", "duration", "trans_type", "ease_type"), &AnimationMixer::capture, DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN));
/* ---- Reset on save ---- */
ClassDB::bind_method(D_METHOD("set_reset_on_save_enabled", "enabled"), &AnimationMixer::set_reset_on_save_enabled);
ClassDB::bind_method(D_METHOD("is_reset_on_save_enabled"), &AnimationMixer::is_reset_on_save_enabled);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_on_save", PROPERTY_HINT_NONE, ""), "set_reset_on_save_enabled", "is_reset_on_save_enabled");
-
- /* ---- Capture feature ---- */
- ClassDB::bind_method(D_METHOD("capture", "name", "duration", "trans_type", "ease_type"), &AnimationMixer::capture, DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN));
-
- ADD_SIGNAL(MethodInfo("mixer_updated")); // For updating dummy player.
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deterministic"), "set_deterministic", "is_deterministic");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_on_save", PROPERTY_HINT_NONE, ""), "set_reset_on_save_enabled", "is_reset_on_save_enabled");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_node"), "set_root_node", "get_root_node");
ADD_GROUP("Root Motion", "root_motion_");
@@ -2270,6 +2261,8 @@ void AnimationMixer::_bind_methods() {
ADD_SIGNAL(MethodInfo(SNAME("animation_finished"), PropertyInfo(Variant::STRING_NAME, "anim_name")));
ADD_SIGNAL(MethodInfo(SNAME("animation_started"), PropertyInfo(Variant::STRING_NAME, "anim_name")));
ADD_SIGNAL(MethodInfo(SNAME("caches_cleared")));
+ ADD_SIGNAL(MethodInfo(SNAME("mixer_applied")));
+ ADD_SIGNAL(MethodInfo(SNAME("mixer_updated"))); // For updating dummy player.
ClassDB::bind_method(D_METHOD("_reset"), &AnimationMixer::reset);
ClassDB::bind_method(D_METHOD("_restore", "backup"), &AnimationMixer::restore);
diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h
index 682246f9ab..ed291bfe63 100644
--- a/scene/animation/animation_mixer.h
+++ b/scene/animation/animation_mixer.h
@@ -341,6 +341,8 @@ protected:
/* ---- Blending processor ---- */
virtual void _process_animation(double p_delta, bool p_update_only = false);
+
+ // For post process with retrieved key value during blending.
virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, ObjectID, int);
@@ -377,7 +379,6 @@ protected:
#ifndef DISABLE_DEPRECATED
virtual Variant _post_process_key_value_bind_compat_86687(const Ref<Animation> &p_anim, int p_track, Variant p_value, Object *p_object, int p_object_idx = -1);
-
static void _bind_compatibility_methods();
#endif // DISABLE_DEPRECATED
@@ -436,7 +437,7 @@ public:
void make_animation_instance(const StringName &p_name, const PlaybackInfo p_playback_info);
void clear_animation_instances();
virtual void advance(double p_time);
- virtual void clear_caches(); ///< must be called by hand if an animation was modified after added
+ virtual void clear_caches(); // Must be called by hand if an animation was modified after added.
/* ---- Capture feature ---- */
void capture(const StringName &p_name, double p_duration, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN);
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 2df1e81e25..4880a0f6ed 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -815,10 +815,7 @@ void AnimationTree::set_animation_player(const NodePath &p_path) {
remove_animation_library(animation_libraries[0].name);
}
}
-#ifdef TOOLS_ENABLED
emit_signal(SNAME("animation_player_changed")); // Needs to unpin AnimationPlayerEditor.
- emit_signal(SNAME("mixer_updated"));
-#endif // TOOLS_ENABLED
_setup_animation_player();
notify_property_list_changed();
}
@@ -964,9 +961,7 @@ void AnimationTree::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "advance_expression_base_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node"), "set_advance_expression_base_node", "get_advance_expression_base_node");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player");
-#ifdef TOOLS_ENABLED
ADD_SIGNAL(MethodInfo(SNAME("animation_player_changed")));
-#endif // TOOLS_ENABLED
}
AnimationTree::AnimationTree() {
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 62bb14459d..f1a8c2f6ae 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -241,10 +241,6 @@ PackedStringArray Control::get_configuration_warnings() const {
warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."));
}
- if (get_z_index() != 0) {
- warnings.push_back(RTR("Changing the Z index of a control only affects the drawing order, not the input event handling order."));
- }
-
return warnings;
}
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 11c200064e..beb2583b61 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -3780,7 +3780,7 @@ void Node::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_thread_messages", PROPERTY_HINT_FLAGS, "Process,Physics Process"), "set_process_thread_messages", "get_process_thread_messages");
ADD_GROUP("Physics Interpolation", "physics_interpolation_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_interpolation_mode", PROPERTY_HINT_ENUM, "Inherit,Off,On"), "set_physics_interpolation_mode", "get_physics_interpolation_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_interpolation_mode", PROPERTY_HINT_ENUM, "Inherit,On,Off"), "set_physics_interpolation_mode", "get_physics_interpolation_mode");
ADD_GROUP("Auto Translate", "auto_translate_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "auto_translate_mode", PROPERTY_HINT_ENUM, "Inherit,Always,Disabled"), "set_auto_translate_mode", "get_auto_translate_mode");
@@ -3833,7 +3833,7 @@ Node::Node() {
data.unhandled_input = false;
data.unhandled_key_input = false;
- data.physics_interpolated = false;
+ data.physics_interpolated = true;
data.parent_owned = false;
data.in_constructor = true;
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index f645bb5e73..752cfe2288 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -253,6 +253,7 @@
#include "scene/3d/node_3d.h"
#include "scene/3d/occluder_instance_3d.h"
#include "scene/3d/path_3d.h"
+#include "scene/3d/physical_bone_simulator_3d.h"
#include "scene/3d/physics/animatable_body_3d.h"
#include "scene/3d/physics/area_3d.h"
#include "scene/3d/physics/character_body_3d.h"
@@ -277,6 +278,7 @@
#include "scene/3d/remote_transform_3d.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/3d/skeleton_ik_3d.h"
+#include "scene/3d/skeleton_modifier_3d.h"
#include "scene/3d/soft_body_3d.h"
#include "scene/3d/sprite_3d.h"
#include "scene/3d/visible_on_screen_notifier_3d.h"
@@ -586,6 +588,7 @@ void register_scene_types() {
GDREGISTER_CLASS(CPUParticles3D);
GDREGISTER_CLASS(Marker3D);
GDREGISTER_CLASS(RootMotionView);
+ GDREGISTER_ABSTRACT_CLASS(SkeletonModifier3D);
OS::get_singleton()->yield(); // may take time to init
@@ -598,6 +601,7 @@ void register_scene_types() {
GDREGISTER_CLASS(CharacterBody3D);
GDREGISTER_CLASS(SpringArm3D);
+ GDREGISTER_CLASS(PhysicalBoneSimulator3D);
GDREGISTER_CLASS(PhysicalBone3D);
GDREGISTER_CLASS(SoftBody3D);
diff --git a/scene/resources/skeleton_profile.cpp b/scene/resources/skeleton_profile.cpp
index 24ed480289..2c1d3d4a4c 100644
--- a/scene/resources/skeleton_profile.cpp
+++ b/scene/resources/skeleton_profile.cpp
@@ -68,7 +68,7 @@ bool SkeletonProfile::_set(const StringName &p_path, const Variant &p_value) {
} else if (what == "group") {
set_group(which, p_value);
} else if (what == "require") {
- set_require(which, p_value);
+ set_required(which, p_value);
} else {
return false;
}
@@ -113,7 +113,7 @@ bool SkeletonProfile::_get(const StringName &p_path, Variant &r_ret) const {
} else if (what == "group") {
r_ret = get_group(which);
} else if (what == "require") {
- r_ret = is_require(which);
+ r_ret = is_required(which);
} else {
return false;
}
@@ -299,7 +299,7 @@ SkeletonProfile::TailDirection SkeletonProfile::get_tail_direction(int p_bone_id
return bones[p_bone_idx].tail_direction;
}
-void SkeletonProfile::set_tail_direction(int p_bone_idx, const TailDirection p_tail_direction) {
+void SkeletonProfile::set_tail_direction(int p_bone_idx, TailDirection p_tail_direction) {
if (is_read_only) {
return;
}
@@ -328,7 +328,7 @@ Transform3D SkeletonProfile::get_reference_pose(int p_bone_idx) const {
return bones[p_bone_idx].reference_pose;
}
-void SkeletonProfile::set_reference_pose(int p_bone_idx, const Transform3D p_reference_pose) {
+void SkeletonProfile::set_reference_pose(int p_bone_idx, const Transform3D &p_reference_pose) {
if (is_read_only) {
return;
}
@@ -342,7 +342,7 @@ Vector2 SkeletonProfile::get_handle_offset(int p_bone_idx) const {
return bones[p_bone_idx].handle_offset;
}
-void SkeletonProfile::set_handle_offset(int p_bone_idx, const Vector2 p_handle_offset) {
+void SkeletonProfile::set_handle_offset(int p_bone_idx, const Vector2 &p_handle_offset) {
if (is_read_only) {
return;
}
@@ -365,17 +365,17 @@ void SkeletonProfile::set_group(int p_bone_idx, const StringName &p_group) {
emit_signal("profile_updated");
}
-bool SkeletonProfile::is_require(int p_bone_idx) const {
+bool SkeletonProfile::is_required(int p_bone_idx) const {
ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), false);
- return bones[p_bone_idx].require;
+ return bones[p_bone_idx].required;
}
-void SkeletonProfile::set_require(int p_bone_idx, const bool p_require) {
+void SkeletonProfile::set_required(int p_bone_idx, bool p_required) {
if (is_read_only) {
return;
}
ERR_FAIL_INDEX(p_bone_idx, bones.size());
- bones.write[p_bone_idx].require = p_require;
+ bones.write[p_bone_idx].required = p_required;
emit_signal("profile_updated");
}
@@ -432,6 +432,9 @@ void SkeletonProfile::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_group", "bone_idx"), &SkeletonProfile::get_group);
ClassDB::bind_method(D_METHOD("set_group", "bone_idx", "group"), &SkeletonProfile::set_group);
+ ClassDB::bind_method(D_METHOD("is_required", "bone_idx"), &SkeletonProfile::is_required);
+ ClassDB::bind_method(D_METHOD("set_required", "bone_idx", "required"), &SkeletonProfile::set_required);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "root_bone", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_root_bone", "get_root_bone");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "scale_base_bone", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_scale_base_bone", "get_scale_base_bone");
@@ -478,14 +481,14 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() {
bones.write[1].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0);
bones.write[1].handle_offset = Vector2(0.5, 0.5);
bones.write[1].group = "Body";
- bones.write[1].require = true;
+ bones.write[1].required = true;
bones.write[2].bone_name = "Spine";
bones.write[2].bone_parent = "Hips";
bones.write[2].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0);
bones.write[2].handle_offset = Vector2(0.5, 0.43);
bones.write[2].group = "Body";
- bones.write[2].require = true;
+ bones.write[2].required = true;
bones.write[3].bone_name = "Chest";
bones.write[3].bone_parent = "Spine";
@@ -506,7 +509,7 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() {
bones.write[5].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0);
bones.write[5].handle_offset = Vector2(0.5, 0.23);
bones.write[5].group = "Body";
- bones.write[5].require = false;
+ bones.write[5].required = false;
bones.write[6].bone_name = "Head";
bones.write[6].bone_parent = "Neck";
@@ -514,7 +517,7 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() {
bones.write[6].reference_pose = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0);
bones.write[6].handle_offset = Vector2(0.5, 0.18);
bones.write[6].group = "Body";
- bones.write[6].require = true;
+ bones.write[6].required = true;
bones.write[7].bone_name = "LeftEye";
bones.write[7].bone_parent = "Head";
@@ -539,21 +542,21 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() {
bones.write[10].reference_pose = Transform3D(0, 1, 0, 0, 0, 1, 1, 0, 0, 0.05, 0.1, 0);
bones.write[10].handle_offset = Vector2(0.55, 0.235);
bones.write[10].group = "Body";
- bones.write[10].require = true;
+ bones.write[10].required = true;
bones.write[11].bone_name = "LeftUpperArm";
bones.write[11].bone_parent = "LeftShoulder";
bones.write[11].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.05, 0);
bones.write[11].handle_offset = Vector2(0.6, 0.24);
bones.write[11].group = "Body";
- bones.write[11].require = true;
+ bones.write[11].required = true;
bones.write[12].bone_name = "LeftLowerArm";
bones.write[12].bone_parent = "LeftUpperArm";
bones.write[12].reference_pose = Transform3D(0, 0, -1, 0, 1, 0, 1, 0, 0, 0, 0.25, 0);
bones.write[12].handle_offset = Vector2(0.7, 0.24);
bones.write[12].group = "Body";
- bones.write[12].require = true;
+ bones.write[12].required = true;
bones.write[13].bone_name = "LeftHand";
bones.write[13].bone_parent = "LeftLowerArm";
@@ -562,7 +565,7 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() {
bones.write[13].reference_pose = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, 0, 0.25, 0);
bones.write[13].handle_offset = Vector2(0.82, 0.235);
bones.write[13].group = "Body";
- bones.write[13].require = true;
+ bones.write[13].required = true;
bones.write[14].bone_name = "LeftThumbMetacarpal";
bones.write[14].bone_parent = "LeftHand";
@@ -659,21 +662,21 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() {
bones.write[29].reference_pose = Transform3D(0, -1, 0, 0, 0, 1, -1, 0, 0, -0.05, 0.1, 0);
bones.write[29].handle_offset = Vector2(0.45, 0.235);
bones.write[29].group = "Body";
- bones.write[29].require = true;
+ bones.write[29].required = true;
bones.write[30].bone_name = "RightUpperArm";
bones.write[30].bone_parent = "RightShoulder";
bones.write[30].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.05, 0);
bones.write[30].handle_offset = Vector2(0.4, 0.24);
bones.write[30].group = "Body";
- bones.write[30].require = true;
+ bones.write[30].required = true;
bones.write[31].bone_name = "RightLowerArm";
bones.write[31].bone_parent = "RightUpperArm";
bones.write[31].reference_pose = Transform3D(0, 0, 1, 0, 1, 0, -1, 0, 0, 0, 0.25, 0);
bones.write[31].handle_offset = Vector2(0.3, 0.24);
bones.write[31].group = "Body";
- bones.write[31].require = true;
+ bones.write[31].required = true;
bones.write[32].bone_name = "RightHand";
bones.write[32].bone_parent = "RightLowerArm";
@@ -682,7 +685,7 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() {
bones.write[32].reference_pose = Transform3D(0, 0, -1, 0, 1, 0, 1, 0, 0, 0, 0.25, 0);
bones.write[32].handle_offset = Vector2(0.18, 0.235);
bones.write[32].group = "Body";
- bones.write[32].require = true;
+ bones.write[32].required = true;
bones.write[33].bone_name = "RightThumbMetacarpal";
bones.write[33].bone_parent = "RightHand";
@@ -779,21 +782,21 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() {
bones.write[48].reference_pose = Transform3D(-1, 0, 0, 0, -1, 0, 0, 0, 1, 0.1, 0, 0);
bones.write[48].handle_offset = Vector2(0.549, 0.49);
bones.write[48].group = "Body";
- bones.write[48].require = true;
+ bones.write[48].required = true;
bones.write[49].bone_name = "LeftLowerLeg";
bones.write[49].bone_parent = "LeftUpperLeg";
bones.write[49].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.375, 0);
bones.write[49].handle_offset = Vector2(0.548, 0.683);
bones.write[49].group = "Body";
- bones.write[49].require = true;
+ bones.write[49].required = true;
bones.write[50].bone_name = "LeftFoot";
bones.write[50].bone_parent = "LeftLowerLeg";
bones.write[50].reference_pose = Transform3D(-1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0.375, 0);
bones.write[50].handle_offset = Vector2(0.545, 0.9);
bones.write[50].group = "Body";
- bones.write[50].require = true;
+ bones.write[50].required = true;
bones.write[51].bone_name = "LeftToes";
bones.write[51].bone_parent = "LeftFoot";
@@ -806,21 +809,21 @@ SkeletonProfileHumanoid::SkeletonProfileHumanoid() {
bones.write[52].reference_pose = Transform3D(-1, 0, 0, 0, -1, 0, 0, 0, 1, -0.1, 0, 0);
bones.write[52].handle_offset = Vector2(0.451, 0.49);
bones.write[52].group = "Body";
- bones.write[52].require = true;
+ bones.write[52].required = true;
bones.write[53].bone_name = "RightLowerLeg";
bones.write[53].bone_parent = "RightUpperLeg";
bones.write[53].reference_pose = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0.375, 0);
bones.write[53].handle_offset = Vector2(0.452, 0.683);
bones.write[53].group = "Body";
- bones.write[53].require = true;
+ bones.write[53].required = true;
bones.write[54].bone_name = "RightFoot";
bones.write[54].bone_parent = "RightLowerLeg";
bones.write[54].reference_pose = Transform3D(-1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0.375, 0);
bones.write[54].handle_offset = Vector2(0.455, 0.9);
bones.write[54].group = "Body";
- bones.write[54].require = true;
+ bones.write[54].required = true;
bones.write[55].bone_name = "RightToes";
bones.write[55].bone_parent = "RightFoot";
diff --git a/scene/resources/skeleton_profile.h b/scene/resources/skeleton_profile.h
index 143f495c61..b5a3bce940 100644
--- a/scene/resources/skeleton_profile.h
+++ b/scene/resources/skeleton_profile.h
@@ -61,7 +61,7 @@ protected:
Transform3D reference_pose;
Vector2 handle_offset;
StringName group;
- bool require = false;
+ bool required = false;
};
StringName root_bone;
@@ -104,22 +104,22 @@ public:
void set_bone_parent(int p_bone_idx, const StringName &p_bone_parent);
TailDirection get_tail_direction(int p_bone_idx) const;
- void set_tail_direction(int p_bone_idx, const TailDirection p_tail_direction);
+ void set_tail_direction(int p_bone_idx, TailDirection p_tail_direction);
StringName get_bone_tail(int p_bone_idx) const;
void set_bone_tail(int p_bone_idx, const StringName &p_bone_tail);
Transform3D get_reference_pose(int p_bone_idx) const;
- void set_reference_pose(int p_bone_idx, const Transform3D p_reference_pose);
+ void set_reference_pose(int p_bone_idx, const Transform3D &p_reference_pose);
Vector2 get_handle_offset(int p_bone_idx) const;
- void set_handle_offset(int p_bone_idx, const Vector2 p_handle_offset);
+ void set_handle_offset(int p_bone_idx, const Vector2 &p_handle_offset);
StringName get_group(int p_bone_idx) const;
void set_group(int p_bone_idx, const StringName &p_group);
- bool is_require(int p_bone_idx) const;
- void set_require(int p_bone_idx, const bool p_require);
+ bool is_required(int p_bone_idx) const;
+ void set_required(int p_bone_idx, bool p_required);
bool has_bone(const StringName &p_bone_name);