Skeleton3D: Add SkeletonModifier / Deprecate Override / Separate PB

This commit is contained in:
Silc Lizard (Tokage) Renew 2024-01-28 09:11:43 +09:00
parent f6a78f83aa
commit 04dd299cba
42 changed files with 1530 additions and 681 deletions

View File

@ -80,7 +80,7 @@
<param index="0" name="name" type="StringName" />
<description>
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].
</description>
</method>
<method name="get_animation_library_list" qualifiers="const">
@ -239,14 +239,14 @@
<return type="bool" />
<param index="0" name="name" type="StringName" />
<description>
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].
</description>
</method>
<method name="has_animation_library" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="StringName" />
<description>
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].
</description>
</method>
<method name="remove_animation_library">
@ -333,9 +333,14 @@
Notifies when the caches have been cleared, either automatically, or manually via [method clear_caches].
</description>
</signal>
<signal name="mixer_applied">
<description>
Notifies when the blending result related have been applied to the target objects.
</description>
</signal>
<signal name="mixer_updated">
<description>
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.
</description>
</signal>
</signals>

View File

@ -52,6 +52,7 @@
</member>
<member name="override_pose" type="bool" setter="set_override_pose" getter="get_override_pose" default="false">
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].
</member>
</members>
</class>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PhysicalBoneSimulator3D" inherits="SkeletonModifier3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Node that can be the parent of [PhysicalBone3D] and can apply the simulation results to [Skeleton3D].
</brief_description>
<description>
Node that can be the parent of [PhysicalBone3D] and can apply the simulation results to [Skeleton3D].
</description>
<tutorials>
</tutorials>
<methods>
<method name="is_simulating_physics" qualifiers="const">
<return type="bool" />
<description>
Returns a boolean that indicates whether the [PhysicalBoneSimulator3D] is running and simulating.
</description>
</method>
<method name="physical_bones_add_collision_exception">
<return type="void" />
<param index="0" name="exception" type="RID" />
<description>
Adds a collision exception to the physical bone.
Works just like the [RigidBody3D] node.
</description>
</method>
<method name="physical_bones_remove_collision_exception">
<return type="void" />
<param index="0" name="exception" type="RID" />
<description>
Removes a collision exception to the physical bone.
Works just like the [RigidBody3D] node.
</description>
</method>
<method name="physical_bones_start_simulation">
<return type="void" />
<param index="0" name="bones" type="StringName[]" default="[]" />
<description>
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.
</description>
</method>
<method name="physical_bones_stop_simulation">
<return type="void" />
<description>
Tells the [PhysicalBone3D] nodes in the Skeleton to stop simulating.
</description>
</method>
</methods>
</class>

View File

@ -27,7 +27,7 @@
Clear all the bones in this skeleton.
</description>
</method>
<method name="clear_bones_global_pose_override">
<method name="clear_bones_global_pose_override" deprecated="">
<return type="void" />
<description>
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.
</description>
</method>
<method name="get_animate_physical_bones" qualifiers="const" deprecated="">
<return type="bool" />
<description>
</description>
</method>
<method name="get_bone_children" qualifiers="const">
<return type="PackedInt32Array" />
<param index="0" name="bone_idx" type="int" />
@ -76,16 +81,17 @@
<param index="0" name="bone_idx" type="int" />
<description>
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].
</description>
</method>
<method name="get_bone_global_pose_no_override" qualifiers="const">
<method name="get_bone_global_pose_no_override" qualifiers="const" deprecated="">
<return type="Transform3D" />
<param index="0" name="bone_idx" type="int" />
<description>
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.
</description>
</method>
<method name="get_bone_global_pose_override" qualifiers="const">
<method name="get_bone_global_pose_override" qualifiers="const" deprecated="">
<return type="Transform3D" />
<param index="0" name="bone_idx" type="int" />
<description>
@ -119,6 +125,7 @@
<param index="0" name="bone_idx" type="int" />
<description>
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].
</description>
</method>
<method name="get_bone_pose_position" qualifiers="const">
@ -176,7 +183,7 @@
Returns all bones in the skeleton to their rest poses.
</description>
</method>
<method name="physical_bones_add_collision_exception">
<method name="physical_bones_add_collision_exception" deprecated="">
<return type="void" />
<param index="0" name="exception" type="RID" />
<description>
@ -184,7 +191,7 @@
Works just like the [RigidBody3D] node.
</description>
</method>
<method name="physical_bones_remove_collision_exception">
<method name="physical_bones_remove_collision_exception" deprecated="">
<return type="void" />
<param index="0" name="exception" type="RID" />
<description>
@ -192,7 +199,7 @@
Works just like the [RigidBody3D] node.
</description>
</method>
<method name="physical_bones_start_simulation">
<method name="physical_bones_start_simulation" deprecated="">
<return type="void" />
<param index="0" name="bones" type="StringName[]" default="[]" />
<description>
@ -200,7 +207,7 @@
Optionally, a list of bone names can be passed-in, allowing only the passed-in bones to be simulated.
</description>
</method>
<method name="physical_bones_stop_simulation">
<method name="physical_bones_stop_simulation" deprecated="">
<return type="void" />
<description>
Tells the [PhysicalBone3D] nodes in the Skeleton to stop simulating.
@ -226,6 +233,12 @@
Sets all bone poses to rests.
</description>
</method>
<method name="set_animate_physical_bones" deprecated="">
<return type="void" />
<param index="0" name="enabled" type="bool" />
<description>
</description>
</method>
<method name="set_bone_enabled">
<return type="void" />
<param index="0" name="bone_idx" type="int" />
@ -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].
</description>
</method>
<method name="set_bone_global_pose_override">
<method name="set_bone_global_pose">
<return type="void" />
<param index="0" name="bone_idx" type="int" />
<param index="1" name="pose" type="Transform3D" />
<description>
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.
</description>
</method>
<method name="set_bone_global_pose_override" deprecated="">
<return type="void" />
<param index="0" name="bone_idx" type="int" />
<param index="1" name="pose" type="Transform3D" />
@ -251,6 +273,7 @@
<param index="0" name="bone_idx" type="int" />
<param index="1" name="name" type="String" />
<description>
Sets the bone name, [param name], for the bone at [param bone_idx].
</description>
</method>
<method name="set_bone_parent">
@ -262,6 +285,14 @@
[b]Note:[/b] [param parent_idx] must be less than [param bone_idx].
</description>
</method>
<method name="set_bone_pose">
<return type="void" />
<param index="0" name="bone_idx" type="int" />
<param index="1" name="pose" type="Transform3D" />
<description>
Sets the pose transform, [param pose], for the bone at [param bone_idx].
</description>
</method>
<method name="set_bone_pose_position">
<return type="void" />
<param index="0" name="bone_idx" type="int" />
@ -303,7 +334,8 @@
</method>
</methods>
<members>
<member name="animate_physical_bones" type="bool" setter="set_animate_physical_bones" getter="get_animate_physical_bones" default="true">
<member name="modifier_callback_mode_process" type="int" setter="set_modifier_callback_mode_process" getter="get_modifier_callback_mode_process" enum="Skeleton3D.ModifierCallbackModeProcess" default="1">
Sets the processing timing for the Modifier.
</member>
<member name="motion_scale" type="float" setter="set_motion_scale" getter="get_motion_scale" default="1.0">
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.
</description>
</signal>
<signal name="bone_list_changed">
<description>
</description>
</signal>
<signal name="bone_pose_changed">
<param index="0" name="bone_idx" type="int" />
<description>
@ -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.
</constant>
<constant name="MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS" value="0" enum="ModifierCallbackModeProcess">
Set a flag to process modification during physics frames (see [constant Node.NOTIFICATION_INTERNAL_PHYSICS_PROCESS]).
</constant>
<constant name="MODIFIER_CALLBACK_MODE_PROCESS_IDLE" value="1" enum="ModifierCallbackModeProcess">
Set a flag to process modification during process frames (see [constant Node.NOTIFICATION_INTERNAL_PROCESS]).
</constant>
</constants>
</class>

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="SkeletonIK3D" inherits="Node" deprecated="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<class name="SkeletonIK3D" inherits="SkeletonModifier3D" deprecated="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
A node used to rotate all bones of a [Skeleton3D] bone chain a way that places the end bone at a desired 3D position.
</brief_description>
<description>
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]
</description>
<tutorials>
@ -56,9 +56,6 @@
</method>
</methods>
<members>
<member name="interpolation" type="float" setter="set_interpolation" getter="get_interpolation" default="1.0">
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].
</member>
<member name="magnet" type="Vector3" setter="set_magnet_position" getter="get_magnet_position" default="Vector3(0, 0, 0)">
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.
</member>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="SkeletonModifier3D" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
A Node that may modify Skeleton3D's bone.
</brief_description>
<description>
[SkeletonModifier3D] retrieves a target [Skeleton3D] by having a [Skeleton3D] parent.
If there is [AnimationMixer], modification always performs after playback process of the [AnimationMixer].
</description>
<tutorials>
</tutorials>
<members>
<member name="active" type="bool" setter="set_active" getter="is_active" default="true">
If [code]true[/code], the [SkeletonModifier3D] will be processing.
</member>
<member name="influence" type="float" setter="set_influence" getter="get_influence" default="1.0">
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.
</member>
</members>
<signals>
<signal name="modification_processed">
<description>
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.
</description>
</signal>
</signals>
</class>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="XRBodyModifier3D" inherits="Node3D" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<class name="XRBodyModifier3D" inherits="SkeletonModifier3D" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
A node for driving body meshes from [XRBodyTracker] data.
</brief_description>
@ -24,9 +24,6 @@
<member name="show_when_tracked" type="bool" setter="set_show_when_tracked" getter="get_show_when_tracked" default="true">
If true then the nodes visibility is determined by whether tracking data is available.
</member>
<member name="target" type="NodePath" setter="set_target" getter="get_target" default="NodePath(&quot;&quot;)">
A [NodePath] to a [Skeleton3D] to animate.
</member>
</members>
<constants>
<constant name="BODY_UPDATE_UPPER_BODY" value="1" enum="BodyUpdate" is_bitfield="true">

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="XRHandModifier3D" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<class name="XRHandModifier3D" inherits="SkeletonModifier3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
A node for driving hand meshes from [XRHandTracker] data.
</brief_description>
@ -18,9 +18,6 @@
<member name="hand_tracker" type="StringName" setter="set_hand_tracker" getter="get_hand_tracker" default="&amp;&quot;/user/left&quot;">
The name of the [XRHandTracker] registered with [XRServer] to obtain the hand tracking data from.
</member>
<member name="target" type="NodePath" setter="set_target" getter="get_target" default="NodePath(&quot;&quot;)">
A [NodePath] to a [Skeleton3D] to animate.
</member>
</members>
<constants>
<constant name="BONE_UPDATE_FULL" value="0" enum="BoneUpdate">

View File

@ -1 +1,20 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4.824 8.384a2.466 2.466 0 1 0-1.705 4.496 2.466 2.466 0 1 0 4.496-1.705l3.56-3.56a2.466 2.466 0 1 0 1.705-4.496 2.466 2.466 0 1 0-4.496 1.705z" fill="#fc7f7f"/></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<path fill="#FC7F7F" d="M9.264,8.125L8.119,9.27c-0.219,0.228-0.212,0.589,0.014,0.81c0.222,0.213,0.574,0.213,0.794,0l0.169-0.168
v4.912h4.909l-0.167,0.165c-0.219,0.229-0.212,0.591,0.015,0.81c0.222,0.214,0.573,0.214,0.794,0l1.145-1.145
c0.224-0.224,0.224-0.584,0-0.809l-1.145-1.143c-0.226-0.221-0.588-0.215-0.809,0.014c-0.213,0.221-0.213,0.573,0,0.795l0.167,0.168
h-2.957l3.195-3.196v0.237c0,0.317,0.256,0.573,0.572,0.573c0.315,0,0.571-0.256,0.571-0.573V9.102c0-0.315-0.256-0.571-0.571-0.571
h-1.619c-0.316,0-0.572,0.256-0.572,0.571c0,0.316,0.256,0.572,0.572,0.572h0.237l-3.194,3.194V9.911l0.167,0.168
c0.228,0.218,0.59,0.213,0.81-0.015c0.214-0.223,0.214-0.572,0-0.795l-1.144-1.145C9.848,7.903,9.486,7.903,9.264,8.125L9.264,8.125
z"/>
<path fill="#FC7F7F" d="M7.615,11.175l0.326-0.326c-0.119-0.06-0.23-0.135-0.328-0.229c-0.524-0.511-0.538-1.349-0.035-1.871
l1.155-1.155c0.5-0.499,1.367-0.497,1.865-0.003l0.3,0.3l0.276-0.276c0.399,0.266,0.849,0.393,1.296,0.405
c0.211-0.142,0.453-0.24,0.726-0.24h0.399c0.391-0.186,0.741-0.467,0.998-0.854c0.754-1.134,0.446-2.665-0.688-3.419
c-0.309-0.205-0.66-0.338-1.026-0.389c-0.188-1.349-1.433-2.291-2.782-2.103c-1.349,0.188-2.29,1.433-2.103,2.782
c0.051,0.367,0.184,0.717,0.389,1.026l-3.56,3.56C3.69,7.63,2.159,7.938,1.405,9.072c-0.754,1.134-0.446,2.664,0.688,3.419
c0.308,0.204,0.659,0.338,1.026,0.389c0.188,1.349,1.433,2.29,2.782,2.103c1.349-0.188,2.291-1.433,2.103-2.781
C7.953,11.834,7.82,11.483,7.615,11.175z"/>
</svg>

Before

Width:  |  Height:  |  Size: 260 B

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1 @@
<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><path d="m15.209 5.934c-.018.574-.181 1.149-.521 1.662-.594.891-1.588 1.424-2.661 1.424-.343 0-.681-.055-1.002-.162l-2.169 2.169c.061.185.104.374.132.565.117.844-.102 1.684-.616 2.363-.515.681-1.263 1.12-2.107 1.236-.113.016-.224.02-.335.023.469.557 1.201.872 1.975.765 1.2-.167 2.036-1.274 1.87-2.473-.045-.326-.163-.639-.346-.913l3.164-3.163c1.008.67 2.369.396 3.039-.612.625-.939.422-2.176-.423-2.884z"/><path d="m13.452 4.173c-.005.599-.173 1.204-.529 1.738-.593.893-1.588 1.426-2.66 1.426-.344 0-.682-.055-1.003-.162l-2.169 2.169c.062.185.105.374.132.565.118.844-.101 1.684-.615 2.363-.514.681-1.262 1.12-2.107 1.237-.09.013-.179.014-.269.019.47.5 1.165.774 1.896.673 1.2-.166 2.037-1.274 1.87-2.472-.045-.327-.164-.64-.346-.913l3.165-3.164c1.008.67 2.368.396 3.039-.612.619-.931.423-2.157-.404-2.867z"/><path d="m8.432.09c-.651-.004-1.27.283-1.689.78l1.397 2.02-1.57.6-3.164 3.163c-1.008-.67-2.369-.396-3.039.612-.67 1.007-.396 2.369.611 3.039.274.183.586.301.912.346.167 1.199 1.274 2.036 2.473 1.87 1.199-.167 2.036-1.274 1.869-2.473-.044-.326-.163-.639-.345-.913l3.164-3.164c1.008.67 2.369.396 3.039-.612.671-1.008.397-2.368-.61-3.039-.274-.182-.587-.3-.913-.345-.152-1.068-1.056-1.867-2.135-1.884zm-8.285 1.573.854 4.291 1.242-.467zm2.457-1.549.854 4.291 1.241-.466zm2.854-.114.632 3.124 1.242-.466z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1 @@
<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#b56d6d"><path d="m8.445 9.308v-3.001c1.613-.944 2.156-3.016 1.213-4.631-.603-1.033-1.709-1.67-2.905-1.676h-3.385c-1.869.008-3.377 1.532-3.368 3.401.005 1.197.643 2.301 1.676 2.906v3.001c0 .934.758 1.692 1.692 1.692h3.385c.935 0 1.692-.758 1.692-1.692zm-4.23-5.077h1.692v.846h-1.692zm-2.539-.846c0-.468.378-.846.847-.846.468 0 .846.378.846.846 0 .467-.378.846-.846.846-.469 0-.847-.379-.847-.846zm5.923 5.923h-.846v-.847h-.846v.846h-1.692v-.846h-.847v.846h-.846v-3.384h.846v.846h.847v-.846h1.692v.846h.846v-.846h.846zm-.846-5.923c0-.468.378-.846.846-.846s.846.378.846.846c0 .467-.378.846-.846.846s-.846-.379-.846-.846z"/><path d="m15.294 8.617c-.198-.132-.424-.219-.658-.251-.122-.867-.921-1.471-1.787-1.351s-1.472.921-1.353 1.788c.034.235.119.46.251.658l-2.286 2.286c-.729-.484-1.712-.287-2.195.443-.486.727-.287 1.71.441 2.194.198.131.423.217.659.25.121.865.922 1.471 1.787 1.35.866-.12 1.471-.919 1.351-1.786-.034-.235-.118-.46-.25-.658l2.286-2.286c.728.483 1.712.285 2.195-.443.484-.727.287-1.711-.441-2.194z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><path d="m4 12v1c0 .553.448 1 1 1-.552 0-1 .447-1 1v1h1v-1h1v1h1v-1c0-.553-.448-1-1-1 .552 0 1-.447 1-1v-1h-1v1h-1v-1zm5 0v4h1v-1h1v1h1v-1c-.001-.176-.049-.348-.137-.5.088-.152.136-.324.137-.5v-1c0-.553-.447-1-1-1zm1 1h1v1h-1z"/><path d="m12.091 5.286h-3.384v-.715h.722c.396 0 .714-.319.714-.714v-2.143c0-.394-.319-.714-.714-.714h-2.857c-.395 0-.715.32-.715.714v2.143c0 .395.319.714.715.714h.707v.707l-3.279.008c-.395 0-.714.32-.714.714 0 .395.319.714.714.714h2.04l-.005 3.573c0 .395.32.713.715.713.394 0 .714-.318.714-.713v-1.662h1.073v1.662c0 .395.321.713.716.713.393 0 .713-.318.713-.713v-3.573h2.125c.395 0 .715-.32.715-.714s-.321-.714-.715-.714zm-3.384-3.027h.8v1.598h-.8zm-2.228 1.598v-1.598h.8v1.598z"/></g></svg>

After

Width:  |  Height:  |  Size: 856 B

View File

@ -0,0 +1 @@
<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#fc7f7f"><path d="m4 12v1c0 .553.448 1 1 1-.552 0-1 .447-1 1v1h1v-1h1v1h1v-1c0-.553-.448-1-1-1 .552 0 1-.447 1-1v-1h-1v1h-1v-1zm5 0v4h1v-1h1v1h1v-1c-.001-.176-.049-.348-.137-.5.088-.152.136-.324.137-.5v-1c0-.553-.447-1-1-1zm1 1h1v1h-1z"/><path d="m12.375 4.948c-.38-.186-.839-.028-1.025.353l-.916 1.88.688-3.795c.076-.414-.201-.813-.617-.89-.417-.075-.814.2-.891.616l-.575 3.166-.091-.641v-3.869c0-.426-.343-.768-.767-.768s-.766.342-.766.768v3.869 1.146l-.666-3.671c-.076-.416-.475-.693-.891-.617-.417.076-.693.475-.617.89l.745 4.111-1.254-.902c-.37-.344-.949-.322-1.293.047-.344.371-.323.949.048 1.293l3.161 3.066h3.065c.551 0 1.202-1.314 1.202-1.314l1.813-3.712c.185-.383.028-.841-.353-1.026z"/></g></svg>

After

Width:  |  Height:  |  Size: 834 B

View File

@ -850,7 +850,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &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<AnimationNode> &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 {

View File

@ -196,7 +196,7 @@ void BonePicker::create_bones_tree(Skeleton3D *p_skeleton) {
items.insert(-1, root);
Ref<Texture> bone_icon = get_editor_theme_icon(SNAME("BoneAttachment3D"));
Ref<Texture> bone_icon = get_editor_theme_icon(SNAME("Bone"));
Vector<int> bones_to_process = p_skeleton->get_parentless_bones();
bool is_first = true;

View File

@ -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;
}

View File

@ -129,7 +129,7 @@ void EditorPropertyRootMotion::_node_assign() {
if (skeleton) {
HashMap<int, TreeItem *> items;
items.insert(-1, ti);
Ref<Texture> bone_icon = get_editor_theme_icon(SNAME("BoneAttachment3D"));
Ref<Texture> bone_icon = get_editor_theme_icon(SNAME("Bone"));
Vector<int> bones_to_process = skeleton->get_parentless_bones();
while (bones_to_process.size() > 0) {
int current_bone_idx = bones_to_process[0];

View File

@ -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<Texture> bone_icon = get_editor_theme_icon(SNAME("BoneAttachment3D"));
Ref<Texture> bone_icon = get_editor_theme_icon(SNAME("Bone"));
Vector<int> bones_to_process = skeleton->get_parentless_bones();
while (bones_to_process.size() > 0) {

View File

@ -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.

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRHand" inherits="Node3D" deprecated="Use [XRHandModifier3D] instead." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<class name="OpenXRHand" inherits="SkeletonModifier3D" deprecated="Use [XRHandModifier3D] instead." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Node supporting hand and finger tracking in OpenXR.
</brief_description>
@ -18,14 +18,11 @@
<member name="hand" type="int" setter="set_hand" getter="get_hand" enum="OpenXRHand.Hands" default="0">
Specifies whether this node tracks the left or right hand of the player.
</member>
<member name="hand_skeleton" type="NodePath" setter="set_hand_skeleton" getter="get_hand_skeleton" default="NodePath(&quot;&quot;)">
Set a [Skeleton3D] node for which the pose positions will be updated.
</member>
<member name="motion_range" type="int" setter="set_motion_range" getter="get_motion_range" enum="OpenXRHand.MotionRange" default="0">
Set the motion range (if supported) limiting the hand motion.
</member>
<member name="skeleton_rig" type="int" setter="set_skeleton_rig" getter="get_skeleton_rig" enum="OpenXRHand.SkeletonRig" default="0">
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.
</member>
</members>
<constants>

View File

@ -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<Skeleton3D>(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;
}

View File

@ -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 <openxr/openxr.h>
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;

View File

@ -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) {

View File

@ -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();

View File

@ -0,0 +1,396 @@
/**************************************************************************/
/* physical_bone_simulator_3d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "physical_bone_simulator_3d.h"
void PhysicalBoneSimulator3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) {
if (p_old) {
if (p_old->is_connected(SNAME("bone_list_changed"), callable_mp(this, &PhysicalBoneSimulator3D::_bone_list_changed))) {
p_old->disconnect(SNAME("bone_list_changed"), callable_mp(this, &PhysicalBoneSimulator3D::_bone_list_changed));
}
if (p_old->is_connected(SNAME("pose_updated"), callable_mp(this, &PhysicalBoneSimulator3D::_pose_updated))) {
p_old->disconnect(SNAME("pose_updated"), callable_mp(this, &PhysicalBoneSimulator3D::_pose_updated));
}
}
if (p_new) {
if (!p_new->is_connected(SNAME("bone_list_changed"), callable_mp(this, &PhysicalBoneSimulator3D::_bone_list_changed))) {
p_new->connect(SNAME("bone_list_changed"), callable_mp(this, &PhysicalBoneSimulator3D::_bone_list_changed));
}
if (!p_new->is_connected(SNAME("pose_updated"), callable_mp(this, &PhysicalBoneSimulator3D::_pose_updated))) {
p_new->connect(SNAME("pose_updated"), callable_mp(this, &PhysicalBoneSimulator3D::_pose_updated));
}
}
_bone_list_changed();
}
void PhysicalBoneSimulator3D::_bone_list_changed() {
bones.clear();
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
}
for (int i = 0; i < skeleton->get_bone_count(); i++) {
SimulatedBone sb;
sb.parent = skeleton->get_bone_parent(i);
sb.child_bones = skeleton->get_bone_children(i);
bones.push_back(sb);
}
_rebuild_physical_bones_cache();
_pose_updated();
}
void PhysicalBoneSimulator3D::_pose_updated() {
Skeleton3D *skeleton = get_skeleton();
if (!skeleton || simulating) {
return;
}
ERR_FAIL_COND(skeleton->get_bone_count() != bones.size());
for (int i = 0; i < skeleton->get_bone_count(); i++) {
bones.write[i].global_pose = skeleton->get_bone_global_pose(i);
}
}
void PhysicalBoneSimulator3D::_set_active(bool p_active) {
if (!Engine::get_singleton()->is_editor_hint()) {
_reset_physical_bones_state();
}
}
void PhysicalBoneSimulator3D::_reset_physical_bones_state() {
for (int i = 0; i < bones.size(); i += 1) {
if (bones[i].physical_bone) {
bones[i].physical_bone->reset_physics_simulation_state();
}
}
}
bool PhysicalBoneSimulator3D::is_simulating_physics() const {
return simulating;
}
int PhysicalBoneSimulator3D::find_bone(const String &p_name) const {
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return -1;
}
return skeleton->find_bone(p_name);
}
String PhysicalBoneSimulator3D::get_bone_name(int p_bone) const {
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return String();
}
return skeleton->get_bone_name(p_bone);
}
int PhysicalBoneSimulator3D::get_bone_count() const {
return bones.size();
}
bool PhysicalBoneSimulator3D::is_bone_parent_of(int p_bone, int p_parent_bone_id) const {
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return false;
}
return skeleton->is_bone_parent_of(p_bone, p_parent_bone_id);
}
void PhysicalBoneSimulator3D::bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
ERR_FAIL_COND(bones[p_bone].physical_bone);
ERR_FAIL_NULL(p_physical_bone);
bones.write[p_bone].physical_bone = p_physical_bone;
_rebuild_physical_bones_cache();
}
void PhysicalBoneSimulator3D::unbind_physical_bone_from_bone(int p_bone) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
bones.write[p_bone].physical_bone = nullptr;
_rebuild_physical_bones_cache();
}
PhysicalBone3D *PhysicalBoneSimulator3D::get_physical_bone(int p_bone) {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
return bones[p_bone].physical_bone;
}
PhysicalBone3D *PhysicalBoneSimulator3D::get_physical_bone_parent(int p_bone) {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
if (bones[p_bone].cache_parent_physical_bone) {
return bones[p_bone].cache_parent_physical_bone;
}
return _get_physical_bone_parent(p_bone);
}
PhysicalBone3D *PhysicalBoneSimulator3D::_get_physical_bone_parent(int p_bone) {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
const int parent_bone = bones[p_bone].parent;
if (parent_bone < 0) {
return nullptr;
}
PhysicalBone3D *pb = bones[parent_bone].physical_bone;
if (pb) {
return pb;
} else {
return get_physical_bone_parent(parent_bone);
}
}
void PhysicalBoneSimulator3D::_rebuild_physical_bones_cache() {
const int b_size = bones.size();
for (int i = 0; i < b_size; ++i) {
PhysicalBone3D *parent_pb = _get_physical_bone_parent(i);
if (parent_pb != bones[i].cache_parent_physical_bone) {
bones.write[i].cache_parent_physical_bone = parent_pb;
if (bones[i].physical_bone) {
bones[i].physical_bone->_on_bone_parent_changed();
}
}
}
}
#ifndef DISABLE_DEPRECATED
void _pb_stop_simulation_compat(Node *p_node) {
PhysicalBoneSimulator3D *ps = Object::cast_to<PhysicalBoneSimulator3D>(p_node);
if (ps) {
return; // Prevent conflict.
}
for (int i = p_node->get_child_count() - 1; i >= 0; --i) {
_pb_stop_simulation_compat(p_node->get_child(i));
}
PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node);
if (pb) {
pb->set_simulate_physics(false);
}
}
#endif // _DISABLE_DEPRECATED
void _pb_stop_simulation(Node *p_node) {
for (int i = p_node->get_child_count() - 1; i >= 0; --i) {
PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node->get_child(i));
if (!pb) {
continue;
}
_pb_stop_simulation(pb);
}
PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node);
if (pb) {
pb->set_simulate_physics(false);
}
}
void PhysicalBoneSimulator3D::physical_bones_stop_simulation() {
simulating = false;
_reset_physical_bones_state();
#ifndef DISABLE_DEPRECATED
if (is_compat) {
Skeleton3D *sk = get_skeleton();
if (sk) {
_pb_stop_simulation_compat(sk);
}
} else {
_pb_stop_simulation(this);
}
#else
_pb_stop_simulation(this);
#endif // _DISABLE_DEPRECATED
}
#ifndef DISABLE_DEPRECATED
void _pb_start_simulation_compat(const PhysicalBoneSimulator3D *p_simulator, Node *p_node, const Vector<int> &p_sim_bones) {
PhysicalBoneSimulator3D *ps = Object::cast_to<PhysicalBoneSimulator3D>(p_node);
if (ps) {
return; // Prevent conflict.
}
for (int i = p_node->get_child_count() - 1; i >= 0; --i) {
_pb_start_simulation_compat(p_simulator, p_node->get_child(i), p_sim_bones);
}
PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node);
if (pb) {
if (p_sim_bones.is_empty()) { // If no bones are specified, activate ragdoll on full body.
pb->set_simulate_physics(true);
} else {
for (int i = p_sim_bones.size() - 1; i >= 0; --i) {
if (p_sim_bones[i] == pb->get_bone_id() || p_simulator->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) {
pb->set_simulate_physics(true);
break;
}
}
}
}
}
#endif // _DISABLE_DEPRECATED
void _pb_start_simulation(const PhysicalBoneSimulator3D *p_simulator, Node *p_node, const Vector<int> &p_sim_bones) {
for (int i = p_node->get_child_count() - 1; i >= 0; --i) {
PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node->get_child(i));
if (!pb) {
continue;
}
_pb_start_simulation(p_simulator, pb, p_sim_bones);
}
PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node);
if (pb) {
if (p_sim_bones.is_empty()) { // If no bones are specified, activate ragdoll on full body.
pb->set_simulate_physics(true);
} else {
for (int i = p_sim_bones.size() - 1; i >= 0; --i) {
if (p_sim_bones[i] == pb->get_bone_id() || p_simulator->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) {
pb->set_simulate_physics(true);
break;
}
}
}
}
}
void PhysicalBoneSimulator3D::physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones) {
simulating = true;
_reset_physical_bones_state();
_pose_updated();
Vector<int> sim_bones;
if (p_bones.size() > 0) {
sim_bones.resize(p_bones.size());
int c = 0;
for (int i = sim_bones.size() - 1; i >= 0; --i) {
int bone_id = find_bone(p_bones[i]);
if (bone_id != -1) {
sim_bones.write[c++] = bone_id;
}
}
sim_bones.resize(c);
}
#ifndef DISABLE_DEPRECATED
if (is_compat) {
Skeleton3D *sk = get_skeleton();
if (sk) {
_pb_start_simulation_compat(this, sk, sim_bones);
}
} else {
_pb_start_simulation(this, this, sim_bones);
}
#else
_pb_start_simulation(this, this, sim_bones);
#endif // _DISABLE_DEPRECATED
}
void _physical_bones_add_remove_collision_exception(bool p_add, Node *p_node, RID p_exception) {
for (int i = p_node->get_child_count() - 1; i >= 0; --i) {
_physical_bones_add_remove_collision_exception(p_add, p_node->get_child(i), p_exception);
}
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_node);
if (co) {
if (p_add) {
PhysicsServer3D::get_singleton()->body_add_collision_exception(co->get_rid(), p_exception);
} else {
PhysicsServer3D::get_singleton()->body_remove_collision_exception(co->get_rid(), p_exception);
}
}
}
void PhysicalBoneSimulator3D::physical_bones_add_collision_exception(RID p_exception) {
_physical_bones_add_remove_collision_exception(true, this, p_exception);
}
void PhysicalBoneSimulator3D::physical_bones_remove_collision_exception(RID p_exception) {
_physical_bones_add_remove_collision_exception(false, this, p_exception);
}
Transform3D PhysicalBoneSimulator3D::get_bone_global_pose(int p_bone) const {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
return bones[p_bone].global_pose;
}
void PhysicalBoneSimulator3D::set_bone_global_pose(int p_bone, const Transform3D &p_pose) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
bones.write[p_bone].global_pose = p_pose;
}
void PhysicalBoneSimulator3D::_process_modification() {
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
}
if (!enabled) {
for (int i = 0; i < bones.size(); i++) {
if (bones[i].physical_bone) {
if (bones[i].physical_bone->is_simulating_physics() == false) {
bones[i].physical_bone->reset_to_rest_position();
}
}
}
} else {
ERR_FAIL_COND(skeleton->get_bone_count() != bones.size());
LocalVector<Transform3D> local_poses;
for (int i = 0; i < skeleton->get_bone_count(); i++) {
Transform3D pt;
if (skeleton->get_bone_parent(i) >= 0) {
pt = get_bone_global_pose(skeleton->get_bone_parent(i));
}
local_poses.push_back(pt.affine_inverse() * bones[i].global_pose);
}
for (int i = 0; i < skeleton->get_bone_count(); i++) {
skeleton->set_bone_pose_position(i, local_poses[i].origin);
skeleton->set_bone_pose_rotation(i, local_poses[i].basis.get_rotation_quaternion());
skeleton->set_bone_pose_scale(i, local_poses[i].basis.get_scale());
}
}
}
void PhysicalBoneSimulator3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_simulating_physics"), &PhysicalBoneSimulator3D::is_simulating_physics);
ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &PhysicalBoneSimulator3D::physical_bones_stop_simulation);
ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &PhysicalBoneSimulator3D::physical_bones_start_simulation_on, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &PhysicalBoneSimulator3D::physical_bones_add_collision_exception);
ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &PhysicalBoneSimulator3D::physical_bones_remove_collision_exception);
}
PhysicalBoneSimulator3D::PhysicalBoneSimulator3D() {
}

View File

@ -0,0 +1,110 @@
/**************************************************************************/
/* physical_bone_simulator_3d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef PHYSICAL_BONE_SIMULATOR_3D_H
#define PHYSICAL_BONE_SIMULATOR_3D_H
#include "scene/3d/skeleton_modifier_3d.h"
#include "scene/3d/physics/physical_bone_3d.h"
class PhysicalBone3D;
class PhysicalBoneSimulator3D : public SkeletonModifier3D {
GDCLASS(PhysicalBoneSimulator3D, SkeletonModifier3D);
bool simulating = false;
bool enabled = true;
struct SimulatedBone {
int parent;
Vector<int> child_bones;
Transform3D global_pose;
PhysicalBone3D *physical_bone = nullptr;
PhysicalBone3D *cache_parent_physical_bone = nullptr;
SimulatedBone() {
parent = -1;
global_pose = Transform3D();
physical_bone = nullptr;
cache_parent_physical_bone = nullptr;
}
};
Vector<SimulatedBone> bones;
/// This is a slow API, so it's cached
PhysicalBone3D *_get_physical_bone_parent(int p_bone);
void _rebuild_physical_bones_cache();
void _reset_physical_bones_state();
protected:
static void _bind_methods();
virtual void _set_active(bool p_active) override;
void _bone_list_changed();
void _pose_updated();
virtual void _process_modification() override;
virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override;
public:
#ifndef DISABLE_DEPRECATED
bool is_compat = false;
#endif // _DISABLE_DEPRECATED
bool is_simulating_physics() const;
int find_bone(const String &p_name) const;
String get_bone_name(int p_bone) const;
int get_bone_count() const;
bool is_bone_parent_of(int p_bone_id, int p_parent_bone_id) const;
Transform3D get_bone_global_pose(int p_bone) const;
void set_bone_global_pose(int p_bone, const Transform3D &p_pose);
void bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone);
void unbind_physical_bone_from_bone(int p_bone);
PhysicalBone3D *get_physical_bone(int p_bone);
PhysicalBone3D *get_physical_bone_parent(int p_bone);
void physical_bones_stop_simulation();
void physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones);
void physical_bones_add_collision_exception(RID p_exception);
void physical_bones_remove_collision_exception(RID p_exception);
PhysicalBoneSimulator3D();
};
#endif // PHYSICAL_BONE_SIMULATOR_3D_H

View File

@ -29,6 +29,9 @@
/**************************************************************************/
#include "physical_bone_3d.h"
#ifndef DISABLE_DEPRECATED
#include "scene/3d/skeleton_3d.h"
#endif //_DISABLE_DEPRECATED
bool PhysicalBone3D::JointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
return false;
@ -89,15 +92,14 @@ void PhysicalBone3D::reset_physics_simulation_state() {
}
void PhysicalBone3D::reset_to_rest_position() {
if (parent_skeleton) {
Transform3D new_transform = parent_skeleton->get_global_transform();
PhysicalBoneSimulator3D *simulator = get_simulator();
Skeleton3D *skeleton = get_skeleton();
if (simulator && skeleton) {
if (bone_id == -1) {
new_transform *= body_offset;
set_global_transform((skeleton->get_global_transform() * body_offset).orthonormalized());
} else {
new_transform *= parent_skeleton->get_bone_global_pose(bone_id) * body_offset;
set_global_transform((skeleton->get_global_transform() * simulator->get_bone_global_pose(bone_id) * body_offset).orthonormalized());
}
new_transform.orthonormalize();
set_global_transform(new_transform);
}
}
@ -734,15 +736,14 @@ bool PhysicalBone3D::_get(const StringName &p_name, Variant &r_ret) const {
}
void PhysicalBone3D::_get_property_list(List<PropertyInfo> *p_list) const {
Skeleton3D *parent = find_skeleton_parent(get_parent());
if (parent) {
Skeleton3D *skeleton = get_skeleton();
if (skeleton) {
String names;
for (int i = 0; i < parent->get_bone_count(); i++) {
for (int i = 0; i < skeleton->get_bone_count(); i++) {
if (i > 0) {
names += ",";
}
names += parent->get_bone_name(i);
names += skeleton->get_bone_name(i);
}
p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("bone_name"), PROPERTY_HINT_ENUM, names));
@ -758,7 +759,8 @@ void PhysicalBone3D::_get_property_list(List<PropertyInfo> *p_list) const {
void PhysicalBone3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
parent_skeleton = find_skeleton_parent(get_parent());
case NOTIFICATION_PARENTED:
_update_simulator_path();
update_bone_id();
reset_to_rest_position();
reset_physics_simulation_state();
@ -768,13 +770,13 @@ void PhysicalBone3D::_notification(int p_what) {
break;
case NOTIFICATION_EXIT_TREE: {
if (parent_skeleton) {
PhysicalBoneSimulator3D *simulator = get_simulator();
if (simulator) {
if (-1 != bone_id) {
parent_skeleton->unbind_physical_bone_from_bone(bone_id);
simulator->unbind_physical_bone_from_bone(bone_id);
bone_id = -1;
}
}
parent_skeleton = nullptr;
PhysicsServer3D::get_singleton()->joint_clear(joint);
} break;
@ -818,10 +820,12 @@ void PhysicalBone3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) {
Transform3D global_transform(p_state->get_transform());
// Update skeleton
if (parent_skeleton) {
// Update simulator
PhysicalBoneSimulator3D *simulator = get_simulator();
Skeleton3D *skeleton = get_skeleton();
if (simulator && skeleton) {
if (-1 != bone_id) {
parent_skeleton->set_bone_global_pose_override(bone_id, parent_skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse), 1.0, true);
simulator->set_bone_global_pose(bone_id, skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse));
}
}
}
@ -916,14 +920,6 @@ void PhysicalBone3D::_bind_methods() {
BIND_ENUM_CONSTANT(JOINT_TYPE_6DOF);
}
Skeleton3D *PhysicalBone3D::find_skeleton_parent(Node *p_parent) {
if (!p_parent) {
return nullptr;
}
Skeleton3D *s = Object::cast_to<Skeleton3D>(p_parent);
return s ? s : find_skeleton_parent(p_parent->get_parent());
}
void PhysicalBone3D::_update_joint_offset() {
_fix_joint_offset();
@ -938,18 +934,20 @@ void PhysicalBone3D::_update_joint_offset() {
void PhysicalBone3D::_fix_joint_offset() {
// Clamp joint origin to bone origin
if (parent_skeleton) {
PhysicalBoneSimulator3D *simulator = get_simulator();
if (simulator) {
joint_offset.origin = body_offset.affine_inverse().origin;
}
}
void PhysicalBone3D::_reload_joint() {
if (!parent_skeleton) {
PhysicalBoneSimulator3D *simulator = get_simulator();
if (!simulator || !simulator->get_skeleton()) {
PhysicsServer3D::get_singleton()->joint_clear(joint);
return;
}
PhysicalBone3D *body_a = parent_skeleton->get_physical_bone_parent(bone_id);
PhysicalBone3D *body_a = simulator->get_physical_bone_parent(bone_id);
if (!body_a) {
PhysicsServer3D::get_singleton()->joint_clear(joint);
return;
@ -1041,6 +1039,36 @@ void PhysicalBone3D::_on_bone_parent_changed() {
_reload_joint();
}
void PhysicalBone3D::_update_simulator_path() {
simulator_id = ObjectID();
PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(get_parent());
if (sim) {
simulator_id = sim->get_instance_id();
return;
}
#ifndef DISABLE_DEPRECATED
Skeleton3D *sk = cast_to<Skeleton3D>(get_parent());
if (sk) {
PhysicalBoneSimulator3D *ssim = cast_to<PhysicalBoneSimulator3D>(sk->get_simulator());
if (ssim) {
simulator_id = ssim->get_instance_id();
}
}
#endif // _DISABLE_DEPRECATED
}
PhysicalBoneSimulator3D *PhysicalBone3D::get_simulator() const {
return Object::cast_to<PhysicalBoneSimulator3D>(ObjectDB::get_instance(simulator_id));
}
Skeleton3D *PhysicalBone3D::get_skeleton() const {
PhysicalBoneSimulator3D *simulator = get_simulator();
if (simulator) {
return simulator->get_skeleton();
}
return nullptr;
}
#ifdef TOOLS_ENABLED
void PhysicalBone3D::_set_gizmo_move_joint(bool p_move_joint) {
gizmo_move_joint = p_move_joint;
@ -1059,10 +1087,6 @@ const PhysicalBone3D::JointData *PhysicalBone3D::get_joint_data() const {
return joint_data;
}
Skeleton3D *PhysicalBone3D::find_skeleton_parent() {
return find_skeleton_parent(this);
}
void PhysicalBone3D::set_joint_type(JointType p_joint_type) {
if (p_joint_type == get_joint_type()) {
return;
@ -1269,21 +1293,22 @@ PhysicalBone3D::~PhysicalBone3D() {
}
void PhysicalBone3D::update_bone_id() {
if (!parent_skeleton) {
PhysicalBoneSimulator3D *simulator = get_simulator();
if (!simulator) {
return;
}
const int new_bone_id = parent_skeleton->find_bone(bone_name);
const int new_bone_id = simulator->find_bone(bone_name);
if (new_bone_id != bone_id) {
if (-1 != bone_id) {
// Assert the unbind from old node
parent_skeleton->unbind_physical_bone_from_bone(bone_id);
simulator->unbind_physical_bone_from_bone(bone_id);
}
bone_id = new_bone_id;
parent_skeleton->bind_physical_bone_to_bone(bone_id, this);
simulator->bind_physical_bone_to_bone(bone_id, this);
_fix_joint_offset();
reset_physics_simulation_state();
@ -1292,10 +1317,12 @@ void PhysicalBone3D::update_bone_id() {
void PhysicalBone3D::update_offset() {
#ifdef TOOLS_ENABLED
if (parent_skeleton) {
Transform3D bone_transform(parent_skeleton->get_global_transform());
PhysicalBoneSimulator3D *simulator = get_simulator();
Skeleton3D *skeleton = get_skeleton();
if (simulator && skeleton) {
Transform3D bone_transform(skeleton->get_global_transform());
if (-1 != bone_id) {
bone_transform *= parent_skeleton->get_bone_global_pose(bone_id);
bone_transform *= simulator->get_bone_global_pose(bone_id);
}
if (gizmo_move_joint) {
@ -1309,7 +1336,7 @@ void PhysicalBone3D::update_offset() {
}
void PhysicalBone3D::_start_physics_simulation() {
if (_internal_simulate_physics || !parent_skeleton) {
if (_internal_simulate_physics || !simulator_id.is_valid()) {
return;
}
reset_to_rest_position();
@ -1323,10 +1350,9 @@ void PhysicalBone3D::_start_physics_simulation() {
}
void PhysicalBone3D::_stop_physics_simulation() {
if (!parent_skeleton) {
return;
}
if (parent_skeleton->get_animate_physical_bones()) {
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());
@ -1337,9 +1363,9 @@ void PhysicalBone3D::_stop_physics_simulation() {
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;
}

View File

@ -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;

View File

@ -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<Transform3D> current_bone_poses;
LocalVector<Vector3> current_pose_positions;
LocalVector<Quaternion> current_pose_rotations;
LocalVector<Vector3> current_pose_scales;
LocalVector<Transform3D> current_bone_global_poses;
if (!modifiers.is_empty()) {
// Store unmodified bone poses.
for (int i = 0; i < len; i++) {
current_bone_poses.push_back(bones[i].pose_cache);
current_pose_positions.push_back(bones[i].pose_position);
current_pose_rotations.push_back(bones[i].pose_rotation);
current_pose_scales.push_back(bones[i].pose_scale);
current_bone_global_poses.push_back(bones[i].global_pose);
}
_process_modifiers();
}
// Update skins.
RenderingServer *rs = RenderingServer::get_singleton();
for (SkinReference *E : skin_bindings) {
const Skin *skin = E->skin.operator->();
RID skeleton = E->skeleton;
@ -322,74 +379,78 @@ void Skeleton3D::_notification(int p_what) {
for (uint32_t i = 0; i < bind_count; i++) {
uint32_t bone_index = E->skin_bone_indices_ptrs[i];
ERR_CONTINUE(bone_index >= (uint32_t)len);
rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i));
rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].global_pose * skin->get_bind_pose(i));
}
}
emit_signal(SceneStringNames::get_singleton()->pose_updated);
} break;
#ifndef _3D_DISABLED
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()) {
}
}
void Skeleton3D::set_modifier_callback_mode_process(Skeleton3D::ModifierCallbackModeProcess p_mode) {
if (modifier_callback_mode_process == p_mode) {
return;
}
modifier_callback_mode_process = p_mode;
_process_changed();
}
Skeleton3D::ModifierCallbackModeProcess Skeleton3D::get_modifier_callback_mode_process() const {
return modifier_callback_mode_process;
}
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);
}
} 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;
}
_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;
void Skeleton3D::_make_modifiers_dirty() {
modifiers_dirty = true;
_update_deferred();
}
Transform3D Skeleton3D::get_bone_global_pose(int p_bone) const {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
if (dirty) {
const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
}
return bones[p_bone].pose_global;
const_cast<Skeleton3D *>(this)->force_update_all_dirty_bones();
return bones[p_bone].global_pose;
}
Transform3D Skeleton3D::get_bone_global_pose_no_override(int p_bone) const {
void Skeleton3D::set_bone_global_pose(int p_bone, const Transform3D &p_pose) {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
if (dirty) {
const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
ERR_FAIL_INDEX(p_bone, bone_size);
Transform3D pt;
if (bones[p_bone].parent >= 0) {
pt = get_bone_global_pose(bones[p_bone].parent);
}
return bones[p_bone].pose_global_no_override;
Transform3D t = pt.affine_inverse() * p_pose;
set_bone_pose(p_bone, t);
}
void Skeleton3D::set_motion_scale(float p_motion_scale) {
@ -548,7 +609,7 @@ Transform3D Skeleton3D::get_bone_global_rest(int p_bone) const {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
if (rest_dirty) {
const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
const_cast<Skeleton3D *>(this)->force_update_all_bone_transforms();
}
return bones[p_bone].global_rest;
}
@ -588,6 +649,19 @@ void Skeleton3D::clear_bones() {
// Posing api
void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
bones.write[p_bone].pose_position = p_pose.origin;
bones.write[p_bone].pose_rotation = p_pose.basis.get_rotation_quaternion();
bones.write[p_bone].pose_scale = p_pose.basis.get_scale();
bones.write[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
_make_dirty();
}
}
void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
@ -654,7 +728,7 @@ void Skeleton3D::reset_bone_poses() {
Transform3D Skeleton3D::get_bone_pose(int p_bone) const {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
((Skeleton3D *)this)->bones.write[p_bone].update_pose_cache();
const_cast<Skeleton3D *>(this)->bones.write[p_bone].update_pose_cache();
return bones[p_bone].pose_cache;
}
@ -662,11 +736,15 @@ void Skeleton3D::_make_dirty() {
if (dirty) {
return;
}
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<PhysicalBone3D>(p_node);
if (pb) {
pb->set_simulate_physics(false);
}
}
void Skeleton3D::physical_bones_stop_simulation() {
_pb_stop_simulation(this);
if (Engine::get_singleton()->is_editor_hint() == false && animate_physical_bones) {
set_physics_process_internal(true);
}
}
void _pb_start_simulation(const Skeleton3D *p_skeleton, Node *p_node, const Vector<int> &p_sim_bones) {
for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
_pb_start_simulation(p_skeleton, p_node->get_child(i), p_sim_bones);
}
PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node);
if (pb) {
if (p_sim_bones.is_empty()) { // If no bones is specified, activate ragdoll on full body.
pb->set_simulate_physics(true);
} else {
for (int i = p_sim_bones.size() - 1; 0 <= i; --i) {
if (p_sim_bones[i] == pb->get_bone_id() || p_skeleton->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) {
pb->set_simulate_physics(true);
break;
}
}
}
}
}
void Skeleton3D::physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones) {
set_physics_process_internal(false);
Vector<int> sim_bones;
if (p_bones.size() > 0) {
sim_bones.resize(p_bones.size());
int c = 0;
for (int i = sim_bones.size() - 1; 0 <= i; --i) {
int bone_id = find_bone(p_bones[i]);
if (bone_id != -1) {
sim_bones.write[c++] = bone_id;
}
}
sim_bones.resize(c);
}
_pb_start_simulation(this, this, sim_bones);
}
void _physical_bones_add_remove_collision_exception(bool p_add, Node *p_node, RID p_exception) {
for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
_physical_bones_add_remove_collision_exception(p_add, p_node->get_child(i), p_exception);
}
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_node);
if (co) {
if (p_add) {
PhysicsServer3D::get_singleton()->body_add_collision_exception(co->get_rid(), p_exception);
} else {
PhysicsServer3D::get_singleton()->body_remove_collision_exception(co->get_rid(), p_exception);
}
}
}
void Skeleton3D::physical_bones_add_collision_exception(RID p_exception) {
_physical_bones_add_remove_collision_exception(true, this, p_exception);
}
void Skeleton3D::physical_bones_remove_collision_exception(RID p_exception) {
_physical_bones_add_remove_collision_exception(false, this, p_exception);
}
void Skeleton3D::_skin_changed() {
_make_dirty();
}
@ -927,18 +838,23 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
}
void Skeleton3D::force_update_all_dirty_bones() {
if (dirty) {
const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
if (!dirty) {
return;
}
force_update_all_bone_transforms();
}
void Skeleton3D::force_update_all_bone_transforms() {
_update_process_order();
for (int i = 0; i < parentless_bones.size(); i++) {
force_update_bone_children_transforms(parentless_bones[i]);
}
rest_dirty = false;
dirty = false;
if (updating) {
return;
}
emit_signal(SceneStringNames::get_singleton()->pose_updated);
}
void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
@ -961,32 +877,43 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
Transform3D pose = b.pose_cache;
if (b.parent >= 0) {
b.pose_global = bonesptr[b.parent].pose_global * pose;
b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * pose;
b.global_pose = bonesptr[b.parent].global_pose * pose;
} else {
b.pose_global = pose;
b.pose_global_no_override = pose;
b.global_pose = pose;
}
} else {
if (b.parent >= 0) {
b.pose_global = bonesptr[b.parent].pose_global * b.rest;
b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * b.rest;
b.global_pose = bonesptr[b.parent].global_pose * b.rest;
} else {
b.pose_global = b.rest;
b.pose_global_no_override = b.rest;
b.global_pose = b.rest;
}
}
if (rest_dirty) {
b.global_rest = b.parent >= 0 ? bonesptr[b.parent].global_rest * b.rest : b.rest;
}
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<SkeletonModifier3D>(get_child(i));
if (c) {
modifiers.push_back(c->get_instance_id());
}
}
modifiers_dirty = false;
}
void Skeleton3D::_process_modifiers() {
for (const ObjectID &oid : modifiers) {
Object *t_obj = ObjectDB::get_instance(oid);
if (!t_obj) {
continue;
}
SkeletonModifier3D *mod = cast_to<SkeletonModifier3D>(t_obj);
if (!mod) {
continue;
}
real_t influence = mod->get_influence();
if (influence < 1.0) {
LocalVector<Transform3D> old_poses;
for (int i = 0; i < get_bone_count(); i++) {
old_poses.push_back(get_bone_pose(i));
}
mod->process_modification();
LocalVector<Transform3D> new_poses;
for (int i = 0; i < get_bone_count(); i++) {
new_poses.push_back(get_bone_pose(i));
}
for (int i = 0; i < get_bone_count(); i++) {
if (old_poses[i] == new_poses[i]) {
continue; // Avoid unneeded calculation.
}
set_bone_pose(i, old_poses[i].interpolate_with(new_poses[i], influence));
}
} else {
mod->process_modification();
}
force_update_all_dirty_bones();
}
}
void Skeleton3D::add_child_notify(Node *p_child) {
if (Object::cast_to<SkeletonModifier3D>(p_child)) {
_make_modifiers_dirty();
}
}
void Skeleton3D::move_child_notify(Node *p_child) {
if (Object::cast_to<SkeletonModifier3D>(p_child)) {
_make_modifiers_dirty();
}
}
void Skeleton3D::remove_child_notify(Node *p_child) {
if (Object::cast_to<SkeletonModifier3D>(p_child)) {
_make_modifiers_dirty();
}
}
void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone);
ClassDB::bind_method(D_METHOD("find_bone", "name"), &Skeleton3D::find_bone);
@ -1028,6 +1021,7 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_bones"), &Skeleton3D::clear_bones);
ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton3D::get_bone_pose);
ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_pose);
ClassDB::bind_method(D_METHOD("set_bone_pose_position", "bone_idx", "position"), &Skeleton3D::set_bone_pose_position);
ClassDB::bind_method(D_METHOD("set_bone_pose_rotation", "bone_idx", "rotation"), &Skeleton3D::set_bone_pose_rotation);
ClassDB::bind_method(D_METHOD("set_bone_pose_scale", "bone_idx", "scale"), &Skeleton3D::set_bone_pose_scale);
@ -1042,11 +1036,8 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_bone_enabled", "bone_idx"), &Skeleton3D::is_bone_enabled);
ClassDB::bind_method(D_METHOD("set_bone_enabled", "bone_idx", "enabled"), &Skeleton3D::set_bone_enabled, DEFVAL(true));
ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override);
ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_bone_global_pose_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_override);
ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton3D::get_bone_global_pose);
ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_no_override);
ClassDB::bind_method(D_METHOD("set_bone_global_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_global_pose);
ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms);
ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms);
@ -1057,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<Skeleton3D *>(this)->force_update_all_dirty_bones();
return bones[p_bone].pose_global_no_override;
}
Node *Skeleton3D::get_simulator() {
return simulator;
}
void Skeleton3D::set_animate_physical_bones(bool p_enabled) {
PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
if (!sim) {
return;
}
sim->set_active(p_enabled);
}
bool Skeleton3D::get_animate_physical_bones() const {
PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
if (!sim) {
return false;
}
return sim->is_active();
}
void Skeleton3D::physical_bones_stop_simulation() {
PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
if (!sim) {
return;
}
sim->physical_bones_stop_simulation();
sim->set_active(false);
}
void Skeleton3D::physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones) {
PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
if (!sim) {
return;
}
sim->set_active(true);
sim->physical_bones_start_simulation_on(p_bones);
}
void Skeleton3D::physical_bones_add_collision_exception(RID p_exception) {
PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
if (!sim) {
return;
}
sim->physical_bones_add_collision_exception(p_exception);
}
void Skeleton3D::physical_bones_remove_collision_exception(RID p_exception) {
PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
if (!sim) {
return;
}
sim->physical_bones_remove_collision_exception(p_exception);
}
#endif // _DISABLE_DEPRECATED
Skeleton3D::Skeleton3D() {
}

View File

@ -36,8 +36,8 @@
typedef int BoneId;
class PhysicalBone3D;
class Skeleton3D;
class SkeletonModifier3D;
class SkinReference : public RefCounted {
GDCLASS(SkinReference, RefCounted)
@ -66,61 +66,71 @@ public:
class Skeleton3D : public Node3D {
GDCLASS(Skeleton3D, Node3D);
#ifndef DISABLE_DEPRECATED
Node *simulator = nullptr;
void setup_simulator();
void remove_simulator();
#endif // _DISABLE_DEPRECATED
public:
enum ModifierCallbackModeProcess {
MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS,
MODIFIER_CALLBACK_MODE_PROCESS_IDLE,
};
private:
friend class SkinReference;
void _update_deferred();
bool is_update_needed = false; // Is updating reserved?
bool updating = false; // Is updating now?
struct Bone {
String name;
bool enabled;
int parent;
Vector<int> child_bones;
Transform3D rest;
Transform3D global_rest;
_FORCE_INLINE_ void update_pose_cache() {
bool enabled;
Transform3D pose_cache;
bool pose_cache_dirty = true;
Vector3 pose_position;
Quaternion pose_rotation;
Vector3 pose_scale = Vector3(1, 1, 1);
Transform3D global_pose;
void update_pose_cache() {
if (pose_cache_dirty) {
pose_cache.basis.set_quaternion_scale(pose_rotation, pose_scale);
pose_cache.origin = pose_position;
pose_cache_dirty = false;
}
}
bool pose_cache_dirty = true;
Transform3D pose_cache;
Vector3 pose_position;
Quaternion pose_rotation;
Vector3 pose_scale = Vector3(1, 1, 1);
Transform3D pose_global;
#ifndef DISABLE_DEPRECATED
Transform3D pose_global_no_override;
real_t global_pose_override_amount = 0.0;
bool global_pose_override_reset = false;
Transform3D global_pose_override;
PhysicalBone3D *physical_bone = nullptr;
PhysicalBone3D *cache_parent_physical_bone = nullptr;
Vector<int> child_bones;
#endif // _DISABLE_DEPRECATED
Bone() {
parent = -1;
child_bones = Vector<int>();
enabled = true;
#ifndef DISABLE_DEPRECATED
global_pose_override_amount = 0;
global_pose_override_reset = false;
#ifndef _3D_DISABLED
physical_bone = nullptr;
cache_parent_physical_bone = nullptr;
#endif // _3D_DISABLED
child_bones = Vector<int>();
#endif // _DISABLE_DEPRECATED
}
};
HashSet<SkinReference *> skin_bindings;
void _skin_changed();
bool animate_physical_bones = true;
Vector<Bone> bones;
bool process_order_dirty = false;
@ -138,6 +148,15 @@ private:
void _update_process_order();
// To process modifiers.
ModifierCallbackModeProcess modifier_callback_mode_process = MODIFIER_CALLBACK_MODE_PROCESS_IDLE;
LocalVector<ObjectID> modifiers;
bool modifiers_dirty = false;
void _find_modifiers();
void _process_modifiers();
void _process_changed();
void _make_modifiers_dirty();
#ifndef DISABLE_DEPRECATED
void _add_bone_bind_compat_88791(const String &p_name);
@ -152,12 +171,16 @@ protected:
void _notification(int p_what);
static void _bind_methods();
virtual void add_child_notify(Node *p_child) override;
virtual void move_child_notify(Node *p_child) override;
virtual void remove_child_notify(Node *p_child) override;
public:
enum {
NOTIFICATION_UPDATE_SKELETON = 50
};
// skeleton creation api
// Skeleton creation API
uint64_t get_version() const;
int add_bone(const String &p_name);
int find_bone(const String &p_name) const;
@ -179,8 +202,6 @@ public:
void set_bone_rest(int p_bone, const Transform3D &p_rest);
Transform3D get_bone_rest(int p_bone) const;
Transform3D get_bone_global_rest(int p_bone) const;
Transform3D get_bone_global_pose(int p_bone) const;
Transform3D get_bone_global_pose_no_override(int p_bone) const;
void set_bone_enabled(int p_bone, bool p_enabled);
bool is_bone_enabled(int p_bone) const;
@ -192,26 +213,23 @@ public:
void set_motion_scale(float p_motion_scale);
float get_motion_scale() const;
// posing api
// 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<Skin> create_skin_from_rest_transforms();
@ -221,31 +239,29 @@ public:
void force_update_all_bone_transforms();
void force_update_bone_children_transforms(int bone_idx);
// Physical bone API
void set_modifier_callback_mode_process(ModifierCallbackModeProcess p_mode);
ModifierCallbackModeProcess get_modifier_callback_mode_process() const;
#ifndef DISABLE_DEPRECATED
Transform3D get_bone_global_pose_no_override(int p_bone) const;
void clear_bones_global_pose_override();
Transform3D get_bone_global_pose_override(int p_bone) const;
void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false);
Node *get_simulator();
void set_animate_physical_bones(bool p_enabled);
bool get_animate_physical_bones() const;
void bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone);
void unbind_physical_bone_from_bone(int p_bone);
PhysicalBone3D *get_physical_bone(int p_bone);
PhysicalBone3D *get_physical_bone_parent(int p_bone);
private:
/// This is a slow API, so it's cached
PhysicalBone3D *_get_physical_bone_parent(int p_bone);
void _rebuild_physical_bones_cache();
public:
void physical_bones_stop_simulation();
void physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones);
void physical_bones_add_collision_exception(RID p_exception);
void physical_bones_remove_collision_exception(RID p_exception);
#endif // _DISABLE_DEPRECATED
public:
Skeleton3D();
~Skeleton3D();
};
VARIANT_ENUM_CAST(Skeleton3D::ModifierCallbackModeProcess);
#endif // SKELETON_3D_H

View File

@ -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));
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 = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta);
}
}
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
p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform;
}
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::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
skeleton_ref = Object::cast_to<Skeleton3D>(get_parent());
set_process_priority(1);
reload_chain();
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
void SkeletonIK3D::_process_modification() {
if (!internal_active) {
return;
}
if (target_node_override_ref) {
reload_goal();
}
_solve_chain();
} break;
}
void SkeletonIK3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
reload_chain();
} break;
case NOTIFICATION_EXIT_TREE: {
stop();
} break;
@ -445,14 +415,6 @@ StringName SkeletonIK3D::get_tip_bone() const {
return tip_bone;
}
void SkeletonIK3D::set_interpolation(real_t p_interpolation) {
interpolation = p_interpolation;
}
real_t SkeletonIK3D::get_interpolation() const {
return interpolation;
}
void SkeletonIK3D::set_target_transform(const Transform3D &p_target) {
target = p_target;
reload_goal();
@ -505,33 +467,25 @@ void SkeletonIK3D::set_max_iterations(int p_iterations) {
}
Skeleton3D *SkeletonIK3D::get_parent_skeleton() const {
return cast_to<Skeleton3D>(skeleton_ref.get_validated_object());
return get_skeleton();
}
bool SkeletonIK3D::is_running() {
return is_processing_internal();
return internal_active;
}
void SkeletonIK3D::start(bool p_one_time) {
if (p_one_time) {
set_process_internal(false);
if (target_node_override_ref) {
reload_goal();
}
_solve_chain();
internal_active = true;
SkeletonModifier3D::process_modification();
internal_active = false;
} else {
set_process_internal(true);
internal_active = true;
}
}
void SkeletonIK3D::stop() {
set_process_internal(false);
Skeleton3D *skeleton = get_parent_skeleton();
if (skeleton) {
skeleton->clear_bones_global_pose_override();
}
internal_active = false;
}
Transform3D SkeletonIK3D::_get_target_transform() {
@ -551,7 +505,7 @@ void SkeletonIK3D::reload_chain() {
FabrikInverseKinematic::free_task(task);
task = nullptr;
Skeleton3D *skeleton = get_parent_skeleton();
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
}
@ -575,7 +529,5 @@ void SkeletonIK3D::_solve_chain() {
if (!task) {
return;
}
FabrikInverseKinematic::solve(task, interpolation, override_tip_basis, use_magnet, magnet_position);
FabrikInverseKinematic::solve(task, override_tip_basis, use_magnet, magnet_position);
}
#endif // _3D_DISABLED

View File

@ -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

View File

@ -0,0 +1,139 @@
/**************************************************************************/
/* skeleton_modifier_3d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "skeleton_modifier_3d.h"
void SkeletonModifier3D::_validate_property(PropertyInfo &p_property) const {
//
}
PackedStringArray SkeletonModifier3D::get_configuration_warnings() const {
PackedStringArray warnings = Node3D::get_configuration_warnings();
if (skeleton_id.is_null()) {
warnings.push_back(RTR("Skeleton3D node not set! SkeletonModifier3D must be child of Skeleton3D or set a path to an external skeleton."));
}
return warnings;
}
/* Skeleton3D */
Skeleton3D *SkeletonModifier3D::get_skeleton() const {
return Object::cast_to<Skeleton3D>(ObjectDB::get_instance(skeleton_id));
}
void SkeletonModifier3D::_update_skeleton_path() {
skeleton_id = ObjectID();
// Make sure parent is a Skeleton3D.
Skeleton3D *sk = Object::cast_to<Skeleton3D>(get_parent());
if (sk) {
skeleton_id = sk->get_instance_id();
}
}
void SkeletonModifier3D::_update_skeleton() {
if (!is_inside_tree()) {
return;
}
Skeleton3D *old_sk = get_skeleton();
_update_skeleton_path();
Skeleton3D *new_sk = get_skeleton();
if (old_sk != new_sk) {
_skeleton_changed(old_sk, new_sk);
}
update_configuration_warnings();
}
void SkeletonModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) {
//
}
/* Process */
void SkeletonModifier3D::set_active(bool p_active) {
if (active == p_active) {
return;
}
active = p_active;
_set_active(active);
}
bool SkeletonModifier3D::is_active() const {
return active;
}
void SkeletonModifier3D::_set_active(bool p_active) {
//
}
void SkeletonModifier3D::set_influence(real_t p_influence) {
influence = p_influence;
}
real_t SkeletonModifier3D::get_influence() const {
return influence;
}
void SkeletonModifier3D::process_modification() {
if (!active) {
return;
}
_process_modification();
emit_signal(SNAME("modification_processed"));
}
void SkeletonModifier3D::_process_modification() {
//
}
void SkeletonModifier3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_PARENTED: {
_update_skeleton();
} break;
}
}
void SkeletonModifier3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_active", "active"), &SkeletonModifier3D::set_active);
ClassDB::bind_method(D_METHOD("is_active"), &SkeletonModifier3D::is_active);
ClassDB::bind_method(D_METHOD("set_influence", "influence"), &SkeletonModifier3D::set_influence);
ClassDB::bind_method(D_METHOD("get_influence"), &SkeletonModifier3D::get_influence);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "influence", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_influence", "get_influence");
ADD_SIGNAL(MethodInfo("modification_processed"));
}
SkeletonModifier3D::SkeletonModifier3D() {
}

View File

@ -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

View File

@ -38,9 +38,6 @@ void XRBodyModifier3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_body_tracker", "tracker_name"), &XRBodyModifier3D::set_body_tracker);
ClassDB::bind_method(D_METHOD("get_body_tracker"), &XRBodyModifier3D::get_body_tracker);
ClassDB::bind_method(D_METHOD("set_target", "target"), &XRBodyModifier3D::set_target);
ClassDB::bind_method(D_METHOD("get_target"), &XRBodyModifier3D::get_target);
ClassDB::bind_method(D_METHOD("set_body_update", "body_update"), &XRBodyModifier3D::set_body_update);
ClassDB::bind_method(D_METHOD("get_body_update"), &XRBodyModifier3D::get_body_update);
@ -51,7 +48,6 @@ void XRBodyModifier3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_show_when_tracked"), &XRBodyModifier3D::get_show_when_tracked);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "body_tracker", PROPERTY_HINT_ENUM_SUGGESTION, "/user/body"), "set_body_tracker", "get_body_tracker");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_target", "get_target");
ADD_PROPERTY(PropertyInfo(Variant::INT, "body_update", PROPERTY_HINT_FLAGS, "Upper Body,Lower Body,Hands"), "set_body_update", "get_body_update");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_when_tracked"), "set_show_when_tracked", "get_show_when_tracked");
@ -73,18 +69,6 @@ StringName XRBodyModifier3D::get_body_tracker() const {
return tracker_name;
}
void XRBodyModifier3D::set_target(const NodePath &p_target) {
target = p_target;
if (is_inside_tree()) {
_get_joint_data();
}
}
NodePath XRBodyModifier3D::get_target() const {
return target;
}
void XRBodyModifier3D::set_body_update(BitField<BodyUpdate> p_body_update) {
body_update = p_body_update;
}
@ -110,20 +94,6 @@ bool XRBodyModifier3D::get_show_when_tracked() const {
return show_when_tracked;
}
Skeleton3D *XRBodyModifier3D::get_skeleton() {
if (!has_node(target)) {
return nullptr;
}
Node *node = get_node(target);
if (!node) {
return nullptr;
}
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
return skeleton;
}
void XRBodyModifier3D::_get_joint_data() {
// Table of Godot Humanoid bone names.
static const String bone_names[XRBodyTracker::JOINT_MAX] = {
@ -281,7 +251,7 @@ void XRBodyModifier3D::_get_joint_data() {
}
}
void XRBodyModifier3D::_update_skeleton() {
void XRBodyModifier3D::_process_modification() {
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
@ -379,6 +349,10 @@ void XRBodyModifier3D::_tracker_changed(const StringName &p_tracker_name, const
}
}
void XRBodyModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) {
_get_joint_data();
}
void XRBodyModifier3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@ -388,10 +362,7 @@ void XRBodyModifier3D::_notification(int p_what) {
xr_server->connect("body_tracker_updated", callable_mp(this, &XRBodyModifier3D::_tracker_changed));
xr_server->connect("body_tracker_removed", callable_mp(this, &XRBodyModifier3D::_tracker_changed).bind(Ref<XRBodyTracker>()));
}
_get_joint_data();
set_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
XRServer *xr_server = XRServer::get_singleton();
@ -400,17 +371,11 @@ void XRBodyModifier3D::_notification(int p_what) {
xr_server->disconnect("body_tracker_updated", callable_mp(this, &XRBodyModifier3D::_tracker_changed));
xr_server->disconnect("body_tracker_removed", callable_mp(this, &XRBodyModifier3D::_tracker_changed).bind(Ref<XRBodyTracker>()));
}
set_process_internal(false);
for (int i = 0; i < XRBodyTracker::JOINT_MAX; i++) {
joints[i].bone = -1;
joints[i].parent_joint = -1;
}
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
_update_skeleton();
} break;
default: {
} break;
}

View File

@ -31,7 +31,7 @@
#ifndef XR_BODY_MODIFIER_3D_H
#define XR_BODY_MODIFIER_3D_H
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_modifier_3d.h"
#include "servers/xr/xr_body_tracker.h"
class Skeleton3D;
@ -41,8 +41,8 @@ class Skeleton3D;
data from an XRBodyTracker instance.
*/
class XRBodyModifier3D : public Node3D {
GDCLASS(XRBodyModifier3D, Node3D);
class XRBodyModifier3D : public SkeletonModifier3D {
GDCLASS(XRBodyModifier3D, SkeletonModifier3D);
public:
enum BodyUpdate {
@ -60,9 +60,6 @@ public:
void set_body_tracker(const StringName &p_tracker_name);
StringName get_body_tracker() const;
void set_target(const NodePath &p_target);
NodePath get_target() const;
void set_body_update(BitField<BodyUpdate> p_body_update);
BitField<BodyUpdate> get_body_update() const;
@ -77,6 +74,9 @@ public:
protected:
static void _bind_methods();
virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override;
virtual void _process_modification() override;
private:
struct JointData {
int bone = -1;
@ -84,15 +84,12 @@ private:
};
StringName tracker_name = "/user/body";
NodePath target;
BitField<BodyUpdate> body_update = BODY_UPDATE_UPPER_BODY | BODY_UPDATE_LOWER_BODY | BODY_UPDATE_HANDS;
BoneUpdate bone_update = BONE_UPDATE_FULL;
bool show_when_tracked = true;
JointData joints[XRBodyTracker::JOINT_MAX];
Skeleton3D *get_skeleton();
void _get_joint_data();
void _update_skeleton();
void _tracker_changed(const StringName &p_tracker_name, const Ref<XRBodyTracker> &p_tracker);
};

View File

@ -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<Skeleton3D>(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 Ref<XRH
}
}
void XRHandModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) {
_get_joint_data();
}
void XRHandModifier3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@ -287,8 +264,6 @@ void XRHandModifier3D::_notification(int p_what) {
}
_get_joint_data();
set_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
XRServer *xr_server = XRServer::get_singleton();
@ -298,16 +273,11 @@ void XRHandModifier3D::_notification(int p_what) {
xr_server->disconnect("hand_tracker_removed", callable_mp(this, &XRHandModifier3D::_tracker_changed).bind(Ref<XRHandTracker>()));
}
set_process_internal(false);
for (int i = 0; i < XRHandTracker::HAND_JOINT_MAX; i++) {
joints[i].bone = -1;
joints[i].parent_joint = -1;
}
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
_update_skeleton();
} break;
default: {
} break;
}

View File

@ -31,18 +31,16 @@
#ifndef XR_HAND_MODIFIER_3D_H
#define XR_HAND_MODIFIER_3D_H
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_modifier_3d.h"
#include "servers/xr/xr_hand_tracker.h"
class Skeleton3D;
/**
The XRHandModifier3D node drives a hand skeleton using hand tracking
data from an XRHandTracking instance.
*/
class XRHandModifier3D : public Node3D {
GDCLASS(XRHandModifier3D, Node3D);
class XRHandModifier3D : public SkeletonModifier3D {
GDCLASS(XRHandModifier3D, SkeletonModifier3D);
public:
enum BoneUpdate {
@ -54,9 +52,6 @@ public:
void set_hand_tracker(const StringName &p_tracker_name);
StringName get_hand_tracker() const;
void set_target(const NodePath &p_target);
NodePath get_target() const;
void set_bone_update(BoneUpdate p_bone_update);
BoneUpdate get_bone_update() const;
@ -65,6 +60,9 @@ public:
protected:
static void _bind_methods();
virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override;
virtual void _process_modification() override;
private:
struct JointData {
int bone = -1;
@ -72,13 +70,10 @@ private:
};
StringName tracker_name = "/user/left";
NodePath target;
BoneUpdate bone_update = BONE_UPDATE_FULL;
JointData joints[XRHandTracker::HAND_JOINT_MAX];
Skeleton3D *get_skeleton();
void _get_joint_data();
void _update_skeleton();
void _tracker_changed(StringName p_tracker_name, const Ref<XRHandTracker> &p_tracker);
};

View File

@ -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);

View File

@ -341,6 +341,8 @@ protected:
/* ---- Blending processor ---- */
virtual void _process_animation(double p_delta, bool p_update_only = false);
// For post process with retrieved key value during blending.
virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1);
GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, ObjectID, int);
@ -377,7 +379,6 @@ protected:
#ifndef DISABLE_DEPRECATED
virtual Variant _post_process_key_value_bind_compat_86687(const Ref<Animation> &p_anim, int p_track, Variant p_value, Object *p_object, int p_object_idx = -1);
static void _bind_compatibility_methods();
#endif // DISABLE_DEPRECATED
@ -436,7 +437,7 @@ public:
void make_animation_instance(const StringName &p_name, const PlaybackInfo p_playback_info);
void clear_animation_instances();
virtual void advance(double p_time);
virtual void clear_caches(); ///< must be called by hand if an animation was modified after added
virtual void clear_caches(); // Must be called by hand if an animation was modified after added.
/* ---- Capture feature ---- */
void capture(const StringName &p_name, double p_duration, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN);

View File

@ -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() {

View File

@ -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);