diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index aa751de5f24..9a6fdc33f73 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -20,6 +20,14 @@
[b]Note:[/b] Bone names should be unique, non empty, and cannot include the [code]:[/code] and [code]/[/code] characters.
+
+
+
+
+ Manually advance the child [SkeletonModifier3D]s by the specified time (in seconds).
+ [b]Note:[/b] The [param delta] is temporarily accumulated in the [Skeleton3D], and the deferred process uses the accumulated value to process the modification.
+
+
@@ -420,5 +428,8 @@
Set a flag to process modification during process frames (see [constant Node.NOTIFICATION_INTERNAL_PROCESS]).
+
+ Do not process modification. Use [method advance] to process the modification manually.
+
diff --git a/doc/classes/SkeletonModifier3D.xml b/doc/classes/SkeletonModifier3D.xml
index 7e9814b16f8..be75365105b 100644
--- a/doc/classes/SkeletonModifier3D.xml
+++ b/doc/classes/SkeletonModifier3D.xml
@@ -12,13 +12,22 @@
https://godotengine.org/article/design-of-the-skeleton-modifier-3d/
-
+
Override this virtual method to implement a custom skeleton modifier. You should do things like get the [Skeleton3D]'s current pose and apply the pose here.
[method _process_modification] must not apply [member influence] to bone poses because the [Skeleton3D] automatically applies influence to all bone poses set by the modifier.
+
+
+
+
+ Override this virtual method to implement a custom skeleton modifier. You should do things like get the [Skeleton3D]'s current pose and apply the pose here.
+ [method _process_modification_with_delta] must not apply [member influence] to bone poses because the [Skeleton3D] automatically applies influence to all bone poses set by the modifier.
+ [param delta] is passed from parent [Skeleton3D]. See also [method Skeleton3D.advance].
+
+
diff --git a/scene/3d/look_at_modifier_3d.cpp b/scene/3d/look_at_modifier_3d.cpp
index 1046cb67061..c355abfbb75 100644
--- a/scene/3d/look_at_modifier_3d.cpp
+++ b/scene/3d/look_at_modifier_3d.cpp
@@ -487,7 +487,7 @@ void LookAtModifier3D::_bind_methods() {
BIND_ENUM_CONSTANT(ORIGIN_FROM_EXTERNAL_NODE);
}
-void LookAtModifier3D::_process_modification() {
+void LookAtModifier3D::_process_modification(double p_delta) {
if (!is_inside_tree()) {
return;
}
@@ -570,13 +570,7 @@ void LookAtModifier3D::_process_modification() {
// Do time-based interpolation.
if (remaining > 0) {
- double delta = 0.0;
- if (skeleton->get_modifier_callback_mode_process() == Skeleton3D::MODIFIER_CALLBACK_MODE_PROCESS_IDLE) {
- delta = get_process_delta_time();
- } else {
- delta = get_physics_process_delta_time();
- }
- remaining = MAX(0, remaining - time_step * delta);
+ remaining = MAX(0, remaining - time_step * p_delta);
if (is_flippable) {
// Interpolate through the rest same as AnimationTree blending for preventing to penetrate the bone into the body.
Quaternion rest = skeleton->get_bone_rest(bone).basis.get_rotation_quaternion();
diff --git a/scene/3d/look_at_modifier_3d.h b/scene/3d/look_at_modifier_3d.h
index 7a06d5a886a..b8800636ee2 100644
--- a/scene/3d/look_at_modifier_3d.h
+++ b/scene/3d/look_at_modifier_3d.h
@@ -107,7 +107,7 @@ protected:
static void _bind_methods();
- virtual void _process_modification() override;
+ virtual void _process_modification(double p_delta) override;
public:
void set_bone_name(const String &p_bone_name);
diff --git a/scene/3d/physics/physical_bone_simulator_3d.cpp b/scene/3d/physics/physical_bone_simulator_3d.cpp
index 491a3073732..fe3cfecb463 100644
--- a/scene/3d/physics/physical_bone_simulator_3d.cpp
+++ b/scene/3d/physics/physical_bone_simulator_3d.cpp
@@ -364,7 +364,7 @@ void PhysicalBoneSimulator3D::set_bone_global_pose(int p_bone, const Transform3D
bones.write[p_bone].global_pose = p_pose;
}
-void PhysicalBoneSimulator3D::_process_modification() {
+void PhysicalBoneSimulator3D::_process_modification(double p_delta) {
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
diff --git a/scene/3d/physics/physical_bone_simulator_3d.h b/scene/3d/physics/physical_bone_simulator_3d.h
index cbeea22e2ee..f18024b34d7 100644
--- a/scene/3d/physics/physical_bone_simulator_3d.h
+++ b/scene/3d/physics/physical_bone_simulator_3d.h
@@ -72,7 +72,7 @@ protected:
void _pose_updated();
void _bone_pose_updated(Skeleton3D *skeleton, int p_bone_id);
- virtual void _process_modification() override;
+ virtual void _process_modification(double p_delta) override;
virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override;
diff --git a/scene/3d/retarget_modifier_3d.cpp b/scene/3d/retarget_modifier_3d.cpp
index 6530eb00bde..925e5134139 100644
--- a/scene/3d/retarget_modifier_3d.cpp
+++ b/scene/3d/retarget_modifier_3d.cpp
@@ -370,7 +370,7 @@ void RetargetModifier3D::_retarget_pose() {
}
}
-void RetargetModifier3D::_process_modification() {
+void RetargetModifier3D::_process_modification(double p_delta) {
if (use_global_pose) {
_retarget_global_pose();
} else {
diff --git a/scene/3d/retarget_modifier_3d.h b/scene/3d/retarget_modifier_3d.h
index a8f2ebb9735..f1e4c3e31a7 100644
--- a/scene/3d/retarget_modifier_3d.h
+++ b/scene/3d/retarget_modifier_3d.h
@@ -96,7 +96,7 @@ protected:
virtual void remove_child_notify(Node *p_child) override;
virtual void _set_active(bool p_active) override;
- virtual void _process_modification() override;
+ virtual void _process_modification(double p_delta) override;
public:
virtual PackedStringArray get_configuration_warnings() const override;
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 52dcd0ed50d..0ca3e1678e0 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -357,7 +357,9 @@ void Skeleton3D::_notification(int p_what) {
// Store dirty flags for global bone poses.
bone_global_pose_dirty_backup = bone_global_pose_dirty;
- _process_modifiers();
+ if (update_flags & UPDATE_FLAG_MODIFIER) {
+ _process_modifiers();
+ }
}
// Abort if pose is not changed.
@@ -438,13 +440,20 @@ void Skeleton3D::_notification(int p_what) {
updating = false;
update_flags = UPDATE_FLAG_NONE;
} break;
- case NOTIFICATION_INTERNAL_PROCESS:
- case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- _find_modifiers();
- if (!modifiers.is_empty()) {
- _update_deferred(UPDATE_FLAG_MODIFIER);
- }
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ advance(get_process_delta_time());
} break;
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ advance(get_physics_process_delta_time());
+ } break;
+ }
+}
+
+void Skeleton3D::advance(double p_delta) {
+ _find_modifiers();
+ if (!modifiers.is_empty()) {
+ update_delta += p_delta; // Accumulate delta for manual advance as it needs to process in deferred update.
+ _update_deferred(UPDATE_FLAG_MODIFIER);
}
}
@@ -467,6 +476,9 @@ void Skeleton3D::_process_changed() {
} else if (modifier_callback_mode_process == MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS) {
set_process_internal(false);
set_physics_process_internal(true);
+ } else {
+ set_process_internal(false);
+ set_physics_process_internal(false);
}
}
@@ -1194,7 +1206,7 @@ void Skeleton3D::_process_modifiers() {
for (int i = 0; i < get_bone_count(); i++) {
old_poses.push_back(get_bone_pose(i));
}
- mod->process_modification();
+ mod->process_modification(update_delta);
LocalVector new_poses;
for (int i = 0; i < get_bone_count(); i++) {
new_poses.push_back(get_bone_pose(i));
@@ -1206,10 +1218,11 @@ void Skeleton3D::_process_modifiers() {
set_bone_pose(i, old_poses[i].interpolate_with(new_poses[i], influence));
}
} else {
- mod->process_modification();
+ mod->process_modification(update_delta);
}
force_update_all_dirty_bones();
}
+ update_delta = 0; // Reset accumulated delta.
}
void Skeleton3D::add_child_notify(Node *p_child) {
@@ -1297,11 +1310,13 @@ void Skeleton3D::_bind_methods() {
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);
+ ClassDB::bind_method(D_METHOD("advance", "delta"), &Skeleton3D::advance);
+
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");
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_PROPERTY(PropertyInfo(Variant::INT, "modifier_callback_mode_process", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_modifier_callback_mode_process", "get_modifier_callback_mode_process");
ADD_SIGNAL(MethodInfo("rest_updated"));
ADD_SIGNAL(MethodInfo("pose_updated"));
@@ -1313,6 +1328,7 @@ void Skeleton3D::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
BIND_ENUM_CONSTANT(MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS);
BIND_ENUM_CONSTANT(MODIFIER_CALLBACK_MODE_PROCESS_IDLE);
+ BIND_ENUM_CONSTANT(MODIFIER_CALLBACK_MODE_PROCESS_MANUAL);
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override);
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index c8e7c3ca7ae..6e630437e1b 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -79,6 +79,7 @@ public:
enum ModifierCallbackModeProcess {
MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS,
MODIFIER_CALLBACK_MODE_PROCESS_IDLE,
+ MODIFIER_CALLBACK_MODE_PROCESS_MANUAL,
};
private:
@@ -93,6 +94,7 @@ private:
void _update_deferred(UpdateFlag p_update_flag = UPDATE_FLAG_POSE);
uint8_t update_flags = UPDATE_FLAG_NONE;
bool updating = false; // Is updating now?
+ double update_delta = 0.0;
struct Bone {
String name;
@@ -294,6 +296,8 @@ public:
void set_modifier_callback_mode_process(ModifierCallbackModeProcess p_mode);
ModifierCallbackModeProcess get_modifier_callback_mode_process() const;
+ void advance(double p_delta);
+
#ifndef DISABLE_DEPRECATED
Transform3D get_bone_global_pose_no_override(int p_bone) const;
void clear_bones_global_pose_override();
diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp
index 2476e7d5cd4..3c3c3990e04 100644
--- a/scene/3d/skeleton_ik_3d.cpp
+++ b/scene/3d/skeleton_ik_3d.cpp
@@ -366,7 +366,7 @@ void SkeletonIK3D::_bind_methods() {
#endif
}
-void SkeletonIK3D::_process_modification() {
+void SkeletonIK3D::_process_modification(double p_delta) {
if (!internal_active) {
return;
}
@@ -485,7 +485,7 @@ bool SkeletonIK3D::is_running() {
void SkeletonIK3D::start(bool p_one_time) {
if (p_one_time) {
internal_active = true;
- SkeletonModifier3D::process_modification();
+ SkeletonModifier3D::process_modification(0);
internal_active = false;
} else {
internal_active = true;
diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h
index 0dc3ce62f97..3bad4de9809 100644
--- a/scene/3d/skeleton_ik_3d.h
+++ b/scene/3d/skeleton_ik_3d.h
@@ -144,7 +144,7 @@ protected:
static void _bind_methods();
virtual void _notification(int p_what);
- virtual void _process_modification() override;
+ virtual void _process_modification(double p_delta) override;
public:
SkeletonIK3D();
diff --git a/scene/3d/skeleton_modifier_3d.cpp b/scene/3d/skeleton_modifier_3d.cpp
index 48ee0e53d4c..ccbc65066c1 100644
--- a/scene/3d/skeleton_modifier_3d.cpp
+++ b/scene/3d/skeleton_modifier_3d.cpp
@@ -113,16 +113,23 @@ real_t SkeletonModifier3D::get_influence() const {
return influence;
}
-void SkeletonModifier3D::process_modification() {
+void SkeletonModifier3D::process_modification(double p_delta) {
if (!active) {
return;
}
- _process_modification();
+ _process_modification(p_delta);
emit_signal(SNAME("modification_processed"));
}
-void SkeletonModifier3D::_process_modification() {
- GDVIRTUAL_CALL(_process_modification);
+void SkeletonModifier3D::_process_modification(double p_delta) {
+ if (GDVIRTUAL_CALL(_process_modification_with_delta, p_delta)) {
+ return;
+ }
+#ifndef DISABLE_DEPRECATED
+ if (GDVIRTUAL_CALL(_process_modification)) {
+ return;
+ }
+#endif // DISABLE_DEPRECATED
}
void SkeletonModifier3D::_notification(int p_what) {
@@ -151,7 +158,10 @@ void SkeletonModifier3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "influence", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_influence", "get_influence");
ADD_SIGNAL(MethodInfo("modification_processed"));
+ GDVIRTUAL_BIND(_process_modification_with_delta, "delta");
+#ifndef DISABLE_DEPRECATED
GDVIRTUAL_BIND(_process_modification);
+#endif
BIND_ENUM_CONSTANT(BONE_AXIS_PLUS_X);
BIND_ENUM_CONSTANT(BONE_AXIS_MINUS_X);
diff --git a/scene/3d/skeleton_modifier_3d.h b/scene/3d/skeleton_modifier_3d.h
index c82cbb212f5..7710539c11e 100644
--- a/scene/3d/skeleton_modifier_3d.h
+++ b/scene/3d/skeleton_modifier_3d.h
@@ -68,8 +68,12 @@ protected:
virtual void _set_active(bool p_active);
- virtual void _process_modification();
+ virtual void _process_modification(double p_delta);
+ // TODO: In Godot 5, should obsolete old GDVIRTUAL0(_process_modification); and replace it with _process_modification_with_delta as GDVIRTUAL1(_process_modification, double).
+ GDVIRTUAL1(_process_modification_with_delta, double);
+#ifndef DISABLE_DEPRECATED
GDVIRTUAL0(_process_modification);
+#endif
public:
virtual PackedStringArray get_configuration_warnings() const override;
@@ -83,7 +87,7 @@ public:
Skeleton3D *get_skeleton() const;
- void process_modification();
+ void process_modification(double p_delta);
// Utility APIs.
static Vector3 get_vector_from_bone_axis(BoneAxis p_axis);
diff --git a/scene/3d/spring_bone_simulator_3d.cpp b/scene/3d/spring_bone_simulator_3d.cpp
index 1350d54c6e1..cf2e672289e 100644
--- a/scene/3d/spring_bone_simulator_3d.cpp
+++ b/scene/3d/spring_bone_simulator_3d.cpp
@@ -1496,7 +1496,7 @@ void SpringBoneSimulator3D::_set_active(bool p_active) {
}
}
-void SpringBoneSimulator3D::_process_modification() {
+void SpringBoneSimulator3D::_process_modification(double p_delta) {
if (!is_inside_tree()) {
return;
}
@@ -1514,10 +1514,9 @@ void SpringBoneSimulator3D::_process_modification() {
}
#endif //TOOLS_ENABLED
- double delta = skeleton->get_modifier_callback_mode_process() == Skeleton3D::MODIFIER_CALLBACK_MODE_PROCESS_IDLE ? skeleton->get_process_delta_time() : skeleton->get_physics_process_delta_time();
for (int i = 0; i < settings.size(); i++) {
_init_joints(skeleton, settings[i]);
- _process_joints(delta, skeleton, settings[i]->joints, get_valid_collision_instance_ids(i), settings[i]->cached_center, settings[i]->cached_inverted_center, settings[i]->cached_inverted_center.basis.get_rotation_quaternion());
+ _process_joints(p_delta, skeleton, settings[i]->joints, get_valid_collision_instance_ids(i), settings[i]->cached_center, settings[i]->cached_inverted_center, settings[i]->cached_inverted_center.basis.get_rotation_quaternion());
}
}
diff --git a/scene/3d/spring_bone_simulator_3d.h b/scene/3d/spring_bone_simulator_3d.h
index e01ab1deee7..d2398b080ac 100644
--- a/scene/3d/spring_bone_simulator_3d.h
+++ b/scene/3d/spring_bone_simulator_3d.h
@@ -153,7 +153,7 @@ protected:
static void _bind_methods();
virtual void _set_active(bool p_active) override;
- virtual void _process_modification() override;
+ virtual void _process_modification(double p_delta) override;
void _init_joints(Skeleton3D *p_skeleton, SpringBone3DSetting *p_setting);
void _process_joints(double p_delta, Skeleton3D *p_skeleton, Vector &p_joints, const LocalVector &p_collisions, const Transform3D &p_center_transform, const Transform3D &p_inverted_center_transform, const Quaternion &p_inverted_center_rotation);
diff --git a/scene/3d/xr/xr_body_modifier_3d.cpp b/scene/3d/xr/xr_body_modifier_3d.cpp
index 9cdf732de65..80f00d8bdc4 100644
--- a/scene/3d/xr/xr_body_modifier_3d.cpp
+++ b/scene/3d/xr/xr_body_modifier_3d.cpp
@@ -238,7 +238,7 @@ void XRBodyModifier3D::_get_joint_data() {
}
}
-void XRBodyModifier3D::_process_modification() {
+void XRBodyModifier3D::_process_modification(double p_delta) {
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
diff --git a/scene/3d/xr/xr_body_modifier_3d.h b/scene/3d/xr/xr_body_modifier_3d.h
index 6fde212d6a7..123daf89402 100644
--- a/scene/3d/xr/xr_body_modifier_3d.h
+++ b/scene/3d/xr/xr_body_modifier_3d.h
@@ -71,7 +71,7 @@ protected:
static void _bind_methods();
virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override;
- virtual void _process_modification() override;
+ virtual void _process_modification(double p_delta) override;
private:
struct JointData {
diff --git a/scene/3d/xr/xr_hand_modifier_3d.cpp b/scene/3d/xr/xr_hand_modifier_3d.cpp
index 7a44f1ad3b5..872e169fa14 100644
--- a/scene/3d/xr/xr_hand_modifier_3d.cpp
+++ b/scene/3d/xr/xr_hand_modifier_3d.cpp
@@ -183,7 +183,7 @@ void XRHandModifier3D::_get_joint_data() {
}
}
-void XRHandModifier3D::_process_modification() {
+void XRHandModifier3D::_process_modification(double p_delta) {
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
diff --git a/scene/3d/xr/xr_hand_modifier_3d.h b/scene/3d/xr/xr_hand_modifier_3d.h
index 5d70c1f5bcf..e3075618724 100644
--- a/scene/3d/xr/xr_hand_modifier_3d.h
+++ b/scene/3d/xr/xr_hand_modifier_3d.h
@@ -62,7 +62,7 @@ protected:
static void _bind_methods();
virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override;
- virtual void _process_modification() override;
+ virtual void _process_modification(double p_delta) override;
private:
struct JointData {