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 {