diff --git a/doc/classes/AnimationMixer.xml b/doc/classes/AnimationMixer.xml index 9ccaa385db5..a77e9e28c69 100644 --- a/doc/classes/AnimationMixer.xml +++ b/doc/classes/AnimationMixer.xml @@ -80,7 +80,7 @@ Returns the first [AnimationLibrary] with key [param name] or [code]null[/code] if not found. - To get the [AnimationPlayer]'s global animation library, use [code]get_animation_library("")[/code]. + To get the [AnimationMixer]'s global animation library, use [code]get_animation_library("")[/code]. @@ -239,14 +239,14 @@ - Returns [code]true[/code] if the [AnimationPlayer] stores an [Animation] with key [param name]. + Returns [code]true[/code] if the [AnimationMixer] stores an [Animation] with key [param name]. - Returns [code]true[/code] if the [AnimationPlayer] stores an [AnimationLibrary] with key [param name]. + Returns [code]true[/code] if the [AnimationMixer] stores an [AnimationLibrary] with key [param name]. @@ -333,9 +333,14 @@ Notifies when the caches have been cleared, either automatically, or manually via [method clear_caches]. + + + Notifies when the blending result related have been applied to the target objects. + + - Editor only. Notifies when the property have been updated to update dummy [AnimationPlayer] in animation player editor. + Notifies when the property related process have been updated. diff --git a/doc/classes/BoneAttachment3D.xml b/doc/classes/BoneAttachment3D.xml index 227f6817cc3..bafa463f82d 100644 --- a/doc/classes/BoneAttachment3D.xml +++ b/doc/classes/BoneAttachment3D.xml @@ -52,6 +52,7 @@ Whether the BoneAttachment3D node will override the bone pose of the bone it is attached to. When set to [code]true[/code], the BoneAttachment3D node can change the pose of the bone. When set to [code]false[/code], the BoneAttachment3D will always be set to the bone's transform. + [b]Note:[/b] This override performs interruptively in the skeleton update process using signals due to the old design. It may cause unintended behavior when used at the same time with [SkeletonModifier3D]. diff --git a/doc/classes/PhysicalBoneSimulator3D.xml b/doc/classes/PhysicalBoneSimulator3D.xml new file mode 100644 index 00000000000..149993e1e29 --- /dev/null +++ b/doc/classes/PhysicalBoneSimulator3D.xml @@ -0,0 +1,49 @@ + + + + Node that can be the parent of [PhysicalBone3D] and can apply the simulation results to [Skeleton3D]. + + + Node that can be the parent of [PhysicalBone3D] and can apply the simulation results to [Skeleton3D]. + + + + + + + + Returns a boolean that indicates whether the [PhysicalBoneSimulator3D] is running and simulating. + + + + + + + Adds a collision exception to the physical bone. + Works just like the [RigidBody3D] node. + + + + + + + Removes a collision exception to the physical bone. + Works just like the [RigidBody3D] node. + + + + + + + Tells the [PhysicalBone3D] nodes in the Skeleton to start simulating and reacting to the physics world. + Optionally, a list of bone names can be passed-in, allowing only the passed-in bones to be simulated. + + + + + + Tells the [PhysicalBone3D] nodes in the Skeleton to stop simulating. + + + + diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml index c9ad204247b..17c93af6520 100644 --- a/doc/classes/Skeleton3D.xml +++ b/doc/classes/Skeleton3D.xml @@ -27,7 +27,7 @@ Clear all the bones in this skeleton. - + Removes the global pose override on all bones in the skeleton. @@ -58,6 +58,11 @@ Force updates the bone transform for the bone at [param bone_idx] and all of its children. + + + + + @@ -76,16 +81,17 @@ Returns the overall transform of the specified bone, with respect to the skeleton. Being relative to the skeleton frame, this is not the actual "global" transform of the bone. + [b]Note:[/b] This is the global pose you set to the skeleton in the process, the final global pose can get overridden by modifiers in the deferred process, if you want to access the final global pose, use [signal SkeletonModifier3D.modification_processed]. - + Returns the overall transform of the specified bone, with respect to the skeleton, but without any global pose overrides. Being relative to the skeleton frame, this is not the actual "global" transform of the bone. - + @@ -119,6 +125,7 @@ Returns the pose transform of the specified bone. + [b]Note:[/b] This is the pose you set to the skeleton in the process, the final pose can get overridden by modifiers in the deferred process, if you want to access the final pose, use [signal SkeletonModifier3D.modification_processed]. @@ -176,7 +183,7 @@ Returns all bones in the skeleton to their rest poses. - + @@ -184,7 +191,7 @@ Works just like the [RigidBody3D] node. - + @@ -192,7 +199,7 @@ Works just like the [RigidBody3D] node. - + @@ -200,7 +207,7 @@ Optionally, a list of bone names can be passed-in, allowing only the passed-in bones to be simulated. - + Tells the [PhysicalBone3D] nodes in the Skeleton to stop simulating. @@ -226,6 +233,12 @@ Sets all bone poses to rests. + + + + + + @@ -234,7 +247,16 @@ Disables the pose for the bone at [param bone_idx] if [code]false[/code], enables the bone pose if [code]true[/code]. - + + + + + + Sets the global pose transform, [param pose], for the bone at [param bone_idx]. + [b]Note:[/b] If other bone poses have been changed, this method executes an update process and will cause performance to deteriorate. If you know that multiple global poses will be applied, consider using [method set_bone_pose] with precalculation. + + + @@ -251,6 +273,7 @@ + Sets the bone name, [param name], for the bone at [param bone_idx]. @@ -262,6 +285,14 @@ [b]Note:[/b] [param parent_idx] must be less than [param bone_idx]. + + + + + + Sets the pose transform, [param pose], for the bone at [param bone_idx]. + + @@ -303,7 +334,8 @@ - + + Sets the processing timing for the Modifier. Multiplies the 3D position track animation. @@ -320,6 +352,10 @@ Emitted when the bone at [param bone_idx] is toggled with [method set_bone_enabled]. Use [method is_bone_enabled] to check the new value. + + + + @@ -342,5 +378,11 @@ Notification received when this skeleton's pose needs to be updated. This notification is received [i]before[/i] the related [signal pose_updated] signal. + + Set a flag to process modification during physics frames (see [constant Node.NOTIFICATION_INTERNAL_PHYSICS_PROCESS]). + + + Set a flag to process modification during process frames (see [constant Node.NOTIFICATION_INTERNAL_PROCESS]). + diff --git a/doc/classes/SkeletonIK3D.xml b/doc/classes/SkeletonIK3D.xml index d1f96adec24..46b3d797955 100644 --- a/doc/classes/SkeletonIK3D.xml +++ b/doc/classes/SkeletonIK3D.xml @@ -1,10 +1,10 @@ - + A node used to rotate all bones of a [Skeleton3D] bone chain a way that places the end bone at a desired 3D position. - SkeletonIK3D is used to rotate all bones of a [Skeleton3D] bone chain a way that places the end bone at a desired 3D position. A typical scenario for IK in games is to place a character's feet on the ground or a character's hands on a currently held object. SkeletonIK uses FabrikInverseKinematic internally to solve the bone chain and applies the results to the [Skeleton3D] [code]bones_global_pose_override[/code] property for all affected bones in the chain. If fully applied, this overwrites any bone transform from [Animation]s or bone custom poses set by users. The applied amount can be controlled with the [member interpolation] property. + SkeletonIK3D is used to rotate all bones of a [Skeleton3D] bone chain a way that places the end bone at a desired 3D position. A typical scenario for IK in games is to place a character's feet on the ground or a character's hands on a currently held object. SkeletonIK uses FabrikInverseKinematic internally to solve the bone chain and applies the results to the [Skeleton3D] [code]bones_global_pose_override[/code] property for all affected bones in the chain. If fully applied, this overwrites any bone transform from [Animation]s or bone custom poses set by users. The applied amount can be controlled with the [member SkeletonModifier3D.influence] property. [codeblock] # Apply IK effect automatically on every new frame (not the current) skeleton_ik_node.start() @@ -16,13 +16,13 @@ skeleton_ik_node.stop() # Apply full IK effect - skeleton_ik_node.set_interpolation(1.0) + skeleton_ik_node.set_influence(1.0) # Apply half IK effect - skeleton_ik_node.set_interpolation(0.5) + skeleton_ik_node.set_influence(0.5) # Apply zero IK effect (a value at or below 0.01 also removes bones_global_pose_override on Skeleton) - skeleton_ik_node.set_interpolation(0.0) + skeleton_ik_node.set_influence(0.0) [/codeblock] @@ -56,9 +56,6 @@ - - Interpolation value for how much the IK results are applied to the current skeleton bone chain. A value of [code]1.0[/code] will overwrite all skeleton bone transforms completely while a value of [code]0.0[/code] will visually disable the SkeletonIK. A value at or below [code]0.01[/code] also calls [method Skeleton3D.clear_bones_global_pose_override]. - Secondary target position (first is [member target] property or [member target_node]) for the IK chain. Use magnet position (pole target) to control the bending of the IK chain. Only works if the bone chain has more than 2 bones. The middle chain bone position will be linearly interpolated with the magnet position. diff --git a/doc/classes/SkeletonModifier3D.xml b/doc/classes/SkeletonModifier3D.xml new file mode 100644 index 00000000000..fab33750ea9 --- /dev/null +++ b/doc/classes/SkeletonModifier3D.xml @@ -0,0 +1,29 @@ + + + + A Node that may modify Skeleton3D's bone. + + + [SkeletonModifier3D] retrieves a target [Skeleton3D] by having a [Skeleton3D] parent. + If there is [AnimationMixer], modification always performs after playback process of the [AnimationMixer]. + + + + + + If [code]true[/code], the [SkeletonModifier3D] will be processing. + + + Sets the influence of the modification. + [b]Note:[/b] This value is used by [Skeleton3D] to blend, so the [SkeletonModifier3D] should always apply only 100% of the result without interpolation. + + + + + + Notifies when the modification have been finished. + [b]Note:[/b] If you want to get the modified bone pose by the modifier, you must use [method Skeleton3D.get_bone_pose] or [method Skeleton3D.get_bone_global_pose] at the moment this signal is fired. + + + + diff --git a/doc/classes/XRBodyModifier3D.xml b/doc/classes/XRBodyModifier3D.xml index cb1caf12a90..49a226c1060 100644 --- a/doc/classes/XRBodyModifier3D.xml +++ b/doc/classes/XRBodyModifier3D.xml @@ -1,5 +1,5 @@ - + A node for driving body meshes from [XRBodyTracker] data. @@ -24,9 +24,6 @@ If true then the nodes visibility is determined by whether tracking data is available. - - A [NodePath] to a [Skeleton3D] to animate. - diff --git a/doc/classes/XRHandModifier3D.xml b/doc/classes/XRHandModifier3D.xml index 3192913b876..9ff27bb9820 100644 --- a/doc/classes/XRHandModifier3D.xml +++ b/doc/classes/XRHandModifier3D.xml @@ -1,5 +1,5 @@ - + A node for driving hand meshes from [XRHandTracker] data. @@ -18,9 +18,6 @@ The name of the [XRHandTracker] registered with [XRServer] to obtain the hand tracking data from. - - A [NodePath] to a [Skeleton3D] to animate. - diff --git a/editor/icons/BoneAttachment3D.svg b/editor/icons/BoneAttachment3D.svg index 2cc0adb9061..ec515bc4231 100644 --- a/editor/icons/BoneAttachment3D.svg +++ b/editor/icons/BoneAttachment3D.svg @@ -1 +1,20 @@ - + + + + + + + diff --git a/editor/icons/PhysicalBoneSimulator3D.svg b/editor/icons/PhysicalBoneSimulator3D.svg new file mode 100644 index 00000000000..583c3773284 --- /dev/null +++ b/editor/icons/PhysicalBoneSimulator3D.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/icons/SkeletonModifier3D.svg b/editor/icons/SkeletonModifier3D.svg new file mode 100644 index 00000000000..e3505a5b8b2 --- /dev/null +++ b/editor/icons/SkeletonModifier3D.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/icons/XRBodyModifier3D.svg b/editor/icons/XRBodyModifier3D.svg new file mode 100644 index 00000000000..7e3fcff8085 --- /dev/null +++ b/editor/icons/XRBodyModifier3D.svg @@ -0,0 +1 @@ + diff --git a/editor/icons/XRHandModifier3D.svg b/editor/icons/XRHandModifier3D.svg new file mode 100644 index 00000000000..60578acb4f7 --- /dev/null +++ b/editor/icons/XRHandModifier3D.svg @@ -0,0 +1 @@ + diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index a7c64b79db1..cb4963dacf2 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -850,7 +850,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref &ano ti->set_text(0, F->get()); ti->set_selectable(0, false); ti->set_editable(0, false); - ti->set_icon(0, get_editor_theme_icon(SNAME("BoneAttachment3D"))); + ti->set_icon(0, get_editor_theme_icon(SNAME("Bone"))); } else { ti = parenthood[accum]; } @@ -861,7 +861,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref &ano ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); ti->set_text(0, concat); ti->set_checked(0, anode->is_path_filtered(path)); - ti->set_icon(0, get_editor_theme_icon(SNAME("BoneAttachment3D"))); + ti->set_icon(0, get_editor_theme_icon(SNAME("Bone"))); ti->set_metadata(0, path); } else { diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp index 848989d8e90..4c02d82caa3 100644 --- a/editor/plugins/bone_map_editor_plugin.cpp +++ b/editor/plugins/bone_map_editor_plugin.cpp @@ -196,7 +196,7 @@ void BonePicker::create_bones_tree(Skeleton3D *p_skeleton) { items.insert(-1, root); - Ref bone_icon = get_editor_theme_icon(SNAME("BoneAttachment3D")); + Ref bone_icon = get_editor_theme_icon(SNAME("Bone")); Vector bones_to_process = p_skeleton->get_parentless_bones(); bool is_first = true; diff --git a/editor/plugins/gizmos/physics_bone_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/physics_bone_3d_gizmo_plugin.cpp index b15f9bec3f9..94d6be22f49 100644 --- a/editor/plugins/gizmos/physics_bone_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/physics_bone_3d_gizmo_plugin.cpp @@ -61,17 +61,17 @@ void PhysicalBone3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { return; } - Skeleton3D *sk(physical_bone->find_skeleton_parent()); - if (!sk) { + PhysicalBoneSimulator3D *sm(physical_bone->get_simulator()); + if (!sm) { return; } - PhysicalBone3D *pb(sk->get_physical_bone(physical_bone->get_bone_id())); + PhysicalBone3D *pb(sm->get_physical_bone(physical_bone->get_bone_id())); if (!pb) { return; } - PhysicalBone3D *pbp(sk->get_physical_bone_parent(physical_bone->get_bone_id())); + PhysicalBone3D *pbp(sm->get_physical_bone_parent(physical_bone->get_bone_id())); if (!pbp) { return; } diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp index 30fb731fc82..f6a5c58acfa 100644 --- a/editor/plugins/root_motion_editor_plugin.cpp +++ b/editor/plugins/root_motion_editor_plugin.cpp @@ -129,7 +129,7 @@ void EditorPropertyRootMotion::_node_assign() { if (skeleton) { HashMap items; items.insert(-1, ti); - Ref bone_icon = get_editor_theme_icon(SNAME("BoneAttachment3D")); + Ref bone_icon = get_editor_theme_icon(SNAME("Bone")); Vector bones_to_process = skeleton->get_parentless_bones(); while (bones_to_process.size() > 0) { int current_bone_idx = bones_to_process[0]; diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index e0c5f9e596e..1bd6b4e5b1a 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -377,6 +377,11 @@ void Skeleton3DEditor::create_physical_skeleton() { bones_infos.resize(bone_count); ur->create_action(TTR("Create physical bones"), UndoRedo::MERGE_ALL); + + PhysicalBoneSimulator3D *simulator = memnew(PhysicalBoneSimulator3D); + ur->add_do_method(skeleton, "add_child", simulator); + ur->add_do_method(simulator, "set_owner", owner); + ur->add_do_method(simulator, "set_name", "PhysicalBoneSimulator3D"); for (int bone_id = 0; bone_count > bone_id; ++bone_id) { const int parent = skeleton->get_bone_parent(bone_id); @@ -395,7 +400,7 @@ void Skeleton3DEditor::create_physical_skeleton() { if (collision_shape) { bones_infos.write[parent].physical_bone = physical_bone; - ur->add_do_method(skeleton, "add_child", physical_bone); + ur->add_do_method(simulator, "add_child", physical_bone); ur->add_do_method(physical_bone, "set_owner", owner); ur->add_do_method(collision_shape, "set_owner", owner); ur->add_do_property(physical_bone, "bone_name", skeleton->get_bone_name(parent)); @@ -409,12 +414,13 @@ void Skeleton3DEditor::create_physical_skeleton() { ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, collision_shape); ur->add_do_reference(physical_bone); - ur->add_undo_method(skeleton, "remove_child", physical_bone); + ur->add_undo_method(simulator, "remove_child", physical_bone); } } } } } + ur->add_undo_method(skeleton, "remove_child", simulator); ur->commit_action(); } @@ -670,7 +676,7 @@ void Skeleton3DEditor::update_joint_tree() { items.insert(-1, root); - Ref bone_icon = get_editor_theme_icon(SNAME("BoneAttachment3D")); + Ref bone_icon = get_editor_theme_icon(SNAME("Bone")); Vector bones_to_process = skeleton->get_parentless_bones(); while (bones_to_process.size() > 0) { diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected index 2416f05ee40..bb10b422dc8 100644 --- a/misc/extension_api_validation/4.2-stable.expected +++ b/misc/extension_api_validation/4.2-stable.expected @@ -266,3 +266,16 @@ GH-88014 Validate extension JSON: API was removed: classes/VisualShaderNodeComment Removed VisualShaderNodeComment, which is replaced by VisualShaderNodeFrame. + + +GH-87888 +-------- +Validate extension JSON: API was removed: classes/OpenXRHand/methods/get_hand_skeleton +Validate extension JSON: API was removed: classes/OpenXRHand/methods/set_hand_skeleton +Validate extension JSON: API was removed: classes/OpenXRHand/properties/hand_skeleton +Validate extension JSON: API was removed: classes/Skeleton3D/properties/animate_physical_bones +Validate extension JSON: API was removed: classes/SkeletonIK3D/methods/get_interpolation +Validate extension JSON: API was removed: classes/SkeletonIK3D/methods/set_interpolation +Validate extension JSON: API was removed: classes/SkeletonIK3D/properties/interpolation + +These base class is changed to SkeletonModifier3D which is processed by Skeleton3D with the assumption that it is Skeleton3D's child. diff --git a/modules/openxr/doc_classes/OpenXRHand.xml b/modules/openxr/doc_classes/OpenXRHand.xml index 23d932ddd75..9cc548dd6f2 100644 --- a/modules/openxr/doc_classes/OpenXRHand.xml +++ b/modules/openxr/doc_classes/OpenXRHand.xml @@ -1,5 +1,5 @@ - + Node supporting hand and finger tracking in OpenXR. @@ -18,14 +18,11 @@ Specifies whether this node tracks the left or right hand of the player. - - Set a [Skeleton3D] node for which the pose positions will be updated. - Set the motion range (if supported) limiting the hand motion. - Set the type of skeleton rig the [member hand_skeleton] is compliant with. + Set the type of skeleton rig the parent [Skeleton3D] is compliant with. diff --git a/modules/openxr/scene/openxr_hand.cpp b/modules/openxr/scene/openxr_hand.cpp index 2a4104f6eef..f20d1f8e19b 100644 --- a/modules/openxr/scene/openxr_hand.cpp +++ b/modules/openxr/scene/openxr_hand.cpp @@ -40,9 +40,6 @@ void OpenXRHand::_bind_methods() { ClassDB::bind_method(D_METHOD("set_hand", "hand"), &OpenXRHand::set_hand); ClassDB::bind_method(D_METHOD("get_hand"), &OpenXRHand::get_hand); - ClassDB::bind_method(D_METHOD("set_hand_skeleton", "hand_skeleton"), &OpenXRHand::set_hand_skeleton); - ClassDB::bind_method(D_METHOD("get_hand_skeleton"), &OpenXRHand::get_hand_skeleton); - ClassDB::bind_method(D_METHOD("set_motion_range", "motion_range"), &OpenXRHand::set_motion_range); ClassDB::bind_method(D_METHOD("get_motion_range"), &OpenXRHand::get_motion_range); @@ -54,7 +51,6 @@ void OpenXRHand::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "hand", PROPERTY_HINT_ENUM, "Left,Right"), "set_hand", "get_hand"); ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_range", PROPERTY_HINT_ENUM, "Unobstructed,Conform to controller"), "set_motion_range", "get_motion_range"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "hand_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_hand_skeleton", "get_hand_skeleton"); ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton_rig", PROPERTY_HINT_ENUM, "OpenXR,Humanoid"), "set_skeleton_rig", "get_skeleton_rig"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update"); @@ -90,12 +86,6 @@ OpenXRHand::Hands OpenXRHand::get_hand() const { return hand; } -void OpenXRHand::set_hand_skeleton(const NodePath &p_hand_skeleton) { - hand_skeleton = p_hand_skeleton; - - // TODO if inside tree call _get_bones() -} - void OpenXRHand::set_motion_range(MotionRange p_motion_range) { ERR_FAIL_INDEX(p_motion_range, MOTION_RANGE_MAX); motion_range = p_motion_range; @@ -107,10 +97,6 @@ OpenXRHand::MotionRange OpenXRHand::get_motion_range() const { return motion_range; } -NodePath OpenXRHand::get_hand_skeleton() const { - return hand_skeleton; -} - void OpenXRHand::_set_motion_range() { if (!hand_tracking_ext) { return; @@ -152,20 +138,6 @@ OpenXRHand::BoneUpdate OpenXRHand::get_bone_update() const { return bone_update; } -Skeleton3D *OpenXRHand::get_skeleton() { - if (!has_node(hand_skeleton)) { - return nullptr; - } - - Node *node = get_node(hand_skeleton); - if (!node) { - return nullptr; - } - - Skeleton3D *skeleton = Object::cast_to(node); - return skeleton; -} - void OpenXRHand::_get_joint_data() { // Table of bone names for different rig types. static const String bone_names[SKELETON_RIG_MAX][XR_HAND_JOINT_COUNT_EXT] = { @@ -290,7 +262,7 @@ void OpenXRHand::_get_joint_data() { } } -void OpenXRHand::_update_skeleton() { +void OpenXRHand::_process_modification() { if (openxr_api == nullptr || !openxr_api->is_initialized()) { return; } else if (hand_tracking_ext == nullptr || !hand_tracking_ext->get_active()) { @@ -395,21 +367,14 @@ void OpenXRHand::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { _get_joint_data(); - - set_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { - set_process_internal(false); - // reset for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) { joints[i].bone = -1; joints[i].parent_joint = -1; } } break; - case NOTIFICATION_INTERNAL_PROCESS: { - _update_skeleton(); - } break; default: { } break; } diff --git a/modules/openxr/scene/openxr_hand.h b/modules/openxr/scene/openxr_hand.h index 4c77e7277c8..fc0a994f488 100644 --- a/modules/openxr/scene/openxr_hand.h +++ b/modules/openxr/scene/openxr_hand.h @@ -31,16 +31,15 @@ #ifndef OPENXR_HAND_H #define OPENXR_HAND_H -#include "scene/3d/node_3d.h" -#include "scene/3d/skeleton_3d.h" +#include "scene/3d/skeleton_modifier_3d.h" #include class OpenXRAPI; class OpenXRHandTrackingExtension; -class OpenXRHand : public Node3D { - GDCLASS(OpenXRHand, Node3D); +class OpenXRHand : public SkeletonModifier3D { + GDCLASS(OpenXRHand, SkeletonModifier3D); public: enum Hands { // Deprecated, need to change this to OpenXRInterface::Hands. @@ -86,13 +85,13 @@ private: void _set_motion_range(); - Skeleton3D *get_skeleton(); void _get_joint_data(); - void _update_skeleton(); protected: static void _bind_methods(); + virtual void _process_modification() override; + public: OpenXRHand(); @@ -102,9 +101,6 @@ public: void set_motion_range(MotionRange p_motion_range); MotionRange get_motion_range() const; - void set_hand_skeleton(const NodePath &p_hand_skeleton); - NodePath get_hand_skeleton() const; - void set_skeleton_rig(SkeletonRig p_skeleton_rig); SkeletonRig get_skeleton_rig() const; diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index 76e89f24d8c..221200284dc 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 1bf44c2756b..ec0f7344d79 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/physical_bone_simulator_3d.cpp b/scene/3d/physical_bone_simulator_3d.cpp new file mode 100644 index 00000000000..aba052165c4 --- /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(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(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(p_node->get_child(i)); + if (!pb) { + continue; + } + _pb_stop_simulation(pb); + } + PhysicalBone3D *pb = Object::cast_to(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 &p_sim_bones) { + PhysicalBoneSimulator3D *ps = Object::cast_to(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(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 &p_sim_bones) { + for (int i = p_node->get_child_count() - 1; i >= 0; --i) { + PhysicalBone3D *pb = Object::cast_to(p_node->get_child(i)); + if (!pb) { + continue; + } + _pb_start_simulation(p_simulator, pb, p_sim_bones); + } + PhysicalBone3D *pb = Object::cast_to(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 &p_bones) { + simulating = true; + _reset_physical_bones_state(); + + _pose_updated(); + + Vector 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(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 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 00000000000..ee900e0e773 --- /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 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 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 &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 10c1fceb949..c6be2a9da8d 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 *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 *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(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(get_parent()); + if (sim) { + simulator_id = sim->get_instance_id(); + return; + } +#ifndef DISABLE_DEPRECATED + Skeleton3D *sk = cast_to(get_parent()); + if (sk) { + PhysicalBoneSimulator3D *ssim = cast_to(sk->get_simulator()); + if (ssim) { + simulator_id = ssim->get_instance_id(); + } + } +#endif // _DISABLE_DEPRECATED +} + +PhysicalBoneSimulator3D *PhysicalBone3D::get_simulator() const { + return Object::cast_to(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 953400da2f1..4765e415722 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 f0ffb7b2d5e..17dfbb4516a 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(); + // 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 current_bone_poses; + LocalVector current_pose_positions; + LocalVector current_pose_rotations; + LocalVector current_pose_scales; + LocalVector 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 + 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_INTERNAL_PROCESS: 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(); - } - } - } + _find_modifiers(); + if (!modifiers.is_empty()) { + _update_deferred(); } } break; - case NOTIFICATION_READY: { - if (Engine::get_singleton()->is_editor_hint()) { - set_physics_process_internal(true); - } - } 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(this)->notification(NOTIFICATION_UPDATE_SKELETON); - } - return bones[p_bone].pose_global; + const_cast(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(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(this)->notification(NOTIFICATION_UPDATE_SKELETON); + const_cast(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(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; } - - if (is_inside_tree()) { - notify_deferred_thread_group(NOTIFICATION_UPDATE_SKELETON); - } dirty = true; + _update_deferred(); +} + +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. + } } 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(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 &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(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 &p_bones) { - set_physics_process_internal(false); - - Vector 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(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 Skeleton3D::register_skin(const Ref &p_skin) { } void Skeleton3D::force_update_all_dirty_bones() { - if (dirty) { - const_cast(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; } - 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); +#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.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(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(t_obj); + if (!mod) { + continue; + } + real_t influence = mod->get_influence(); + if (influence < 1.0) { + LocalVector old_poses; + for (int i = 0; i < get_bone_count(); i++) { + old_poses.push_back(get_bone_pose(i)); + } + mod->process_modification(); + LocalVector 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(p_child)) { + _make_modifiers_dirty(); + } +} + +void Skeleton3D::move_child_notify(Node *p_child) { + if (Object::cast_to(p_child)) { + _make_modifiers_dirty(); + } +} + +void Skeleton3D::remove_child_notify(Node *p_child) { + if (Object::cast_to(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,28 +1048,127 @@ 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::BOOL, "modifier_reset_on_save", PROPERTY_HINT_NONE, ""), "set_modifier_reset_on_save_enabled", "is_modifier_reset_on_save_enabled"); + 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(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(simulator); + if (!sim) { + return; + } + sim->set_active(p_enabled); +} + +bool Skeleton3D::get_animate_physical_bones() const { + PhysicalBoneSimulator3D *sim = cast_to(simulator); + if (!sim) { + return false; + } + return sim->is_active(); +} + +void Skeleton3D::physical_bones_stop_simulation() { + PhysicalBoneSimulator3D *sim = cast_to(simulator); + if (!sim) { + return; + } + sim->physical_bones_stop_simulation(); + sim->set_active(false); +} + +void Skeleton3D::physical_bones_start_simulation_on(const TypedArray &p_bones) { + PhysicalBoneSimulator3D *sim = cast_to(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(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(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 979e2e52b72..5b6f60dbd46 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 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 child_bones; +#endif // _DISABLE_DEPRECATED Bone() { parent = -1; + child_bones = Vector(); 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(); +#endif // _DISABLE_DEPRECATED } }; HashSet skin_bindings; - void _skin_changed(); - bool animate_physical_bones = true; Vector 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 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 - + // 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_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; + 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 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 &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 286268b4a20..9581ae58d8c 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(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(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 0a03e96905d..eff018f2ccd 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 00000000000..96e3e33841f --- /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(ObjectDB::get_instance(skeleton_id)); +} + +void SkeletonModifier3D::_update_skeleton_path() { + skeleton_id = ObjectID(); + + // Make sure parent is a Skeleton3D. + Skeleton3D *sk = Object::cast_to(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 00000000000..25c09f3b931 --- /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 ac648d66d03..0099784a05b 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 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(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())); } - _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())); } - - 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 89ac69b6b0d..03b1c07d53d 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 p_body_update); BitField 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 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 &p_tracker); }; diff --git a/scene/3d/xr_hand_modifier_3d.cpp b/scene/3d/xr_hand_modifier_3d.cpp index 1e1449b54ba..7fecb530084 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; - } - - Skeleton3D *skeleton = Object::cast_to(node); - return skeleton; -} - void XRHandModifier3D::_get_joint_data() { + if (!is_inside_tree()) { + return; + } + // 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 Refdisconnect("hand_tracker_removed", callable_mp(this, &XRHandModifier3D::_tracker_changed).bind(Ref())); } - 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 2bc30d42d49..9f7ce45c9dd 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 &p_tracker); }; diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index 22f919ad36a..757ac68e461 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, "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"); - - /* ---- 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::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 682246f9ab9..ed291bfe63d 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 &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 &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, int, Variant, ObjectID, int); @@ -377,7 +379,6 @@ protected: #ifndef DISABLE_DEPRECATED virtual Variant _post_process_key_value_bind_compat_86687(const Ref &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 2df1e81e258..4880a0f6ed7 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/register_scene_types.cpp b/scene/register_scene_types.cpp index f645bb5e73c..752cfe2288e 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);