Skip to content

Allow Spring / IK to set mutable bone axes#111815

Merged
Repiteo merged 1 commit into
godotengine:masterfrom
TokageItLab:bone-expander
Nov 17, 2025
Merged

Allow Spring / IK to set mutable bone axes#111815
Repiteo merged 1 commit into
godotengine:masterfrom
TokageItLab:bone-expander

Conversation

@TokageItLab

@TokageItLab TokageItLab commented Oct 19, 2025

Copy link
Copy Markdown
Member

We omit the BoneExpander from this PR to prioritize adding mutable_bone_axes to 4.6 based on the meeting. I will send it as separate PR.

BoneExpander (outdated)

Add a BoneExpander. This applies scaling to the bone pose position and skin bind matrix, enabling visual scaling without
modifying the bone pose basis.

Compared to #83903, this approach minimizes impact on projects that do not utilize scaling. Furthermore, by handling scaling settings externally, it should keep the Skeleton system itself clean.

There are several methods for scaling while avoiding global shearing. Other game engines like Unity typically solve this by using BoneConstraints for parent-child relationships without hierarchical parent-child relationships. Another approach involves using the inverse of the CopyTransformModifier to add a cancel bone without length as a child to the bone you want to scale.

mov_016.mp4

However, those three methods, including #83903, all use scaling, and adopting this result in IK or SpringBone is not straightforward.

One way to handle this is to convert the scale into bone translation (pose position) and change the SkeletonModifier's ForwardAxis retrieval from bone rest to bone pose. This is similar to what is done internally in several places within the glTF importer. Then, the restriction that scaling may cause breakage remains unchanged from previous versions.

While calculations might be possible in cases involving cancel bones, but we cannot guarantee the presence of cancel bones. And since Godot will never remove cases where global shears occur, I think it's safer to maintain consistency regarding the potential for breakage when scaling is used.

How to work

Modifications to the skin are not managed by the SkeletonModifier, so they are managed using signals.

To enable this, the list of skin references can be retrieved from the skeleton using get_skin_bindings(). However, since this API returns a HashSet and carries some risk, it is published only to the C++ core (not exposed to GDScript). Additionally, the skeleton_rendered and skin_changed signals are added to restore the original, unscaled skin after rendering. These signals are considered safe and are published to GDScript.

During SkeletonModifier iteration, only the transformed translate is applied to the bone pose, and only the undeformed skin is retrieved. Then, scaling is applied to the skin when the skeleton_updated signal fires immediately before rendering. This method prevents skin corruption because even if multiple BoneExpanders process the same bone, the scaled skin is never retrieved. Finally, the unscaled skin is restored after rendering.

UpdateProcess
↓
skeleton dirty PoseUpdate
↓
ModifierProcess
↓
BoneExpander (change only pose position)
↓
IK
↓
skeleton updated signal (change skin bind pose scale)
↓
ApplySkin
↓
skeleton rendered singal (restore skin bind pose)
↓
NextFrame

This implementation is quite experimental, but I believe it represents the best compromise currently available to keep Skeleton's internal processing clean and resolve issues externally.

However, be aware that performance issues may arise due to the extensive use of arrays for skin changes. Additionally, since skin changes are not inherited, combining this with RetargetModifier3D or baking IK animation to FK animation may result in unintended movement. These restrictions have been added to the documentation and as an experimental flag.

image

mutable_bone_axes

Add the mutable_bone_axes option to handle bones have deformed position to the following SkeletonModifier.

  • SpringBoneSimulator3D
  • ManyBoneIK3D

Disabled:
image

Enabled:
image

bone_expander_demo.zip

This means using the bone pose origin instead of the bone rest origin. Enabling this option indicates an increase in computational cost and may slightly impact performance. Since SpringBone and IK cached bone lengths that were not dynamically changed.

Performance comparisons with the option enabled are as follows:

[CCDIK 5 joint x 1000 instance]
Mutable disabled: 42.0 FPS
Mutable enabled: 41.25 FPS

[SpringBone 5 joint x 1000 instance]
Mutable disabled 84.0 FPS
Mutable enabled: 82.25 FPS

spring_and_ik_bench.zip

Note for reviewer: The mutable_bone_axes option is implemented for IK that hasn't been merged yet, so this PR exists in a commit that bases on all related PRs. Merged.

@TokageItLab TokageItLab added this to the 4.6 milestone Oct 19, 2025
@TokageItLab TokageItLab requested a review from a team as a code owner October 19, 2025 09:19
@TokageItLab TokageItLab requested review from a team as code owners October 19, 2025 09:19
@TokageItLab TokageItLab requested a review from a team as a code owner October 19, 2025 09:19
@TokageItLab TokageItLab requested a review from a team October 19, 2025 09:19
@TokageItLab TokageItLab requested review from a team as code owners October 19, 2025 09:19
@TokageItLab TokageItLab requested a review from a team October 19, 2025 09:19
@TokageItLab TokageItLab requested a review from a team as a code owner October 19, 2025 09:19
@TokageItLab TokageItLab moved this to Ready for review in Animation Team Issue Triage Oct 19, 2025
@fire

fire commented Oct 19, 2025

Copy link
Copy Markdown
Member

However, be aware that performance issues may arise due to the extensive use of arrays for skin changes.

Are you aware of the set optimization done in skeleton? #97538

@TokageItLab

Copy link
Copy Markdown
Member Author

@fire #97538 optimization reduces unnecessary recursive processing for bone_pose, so it is irrelevant.

Performance issues with BoneExpander depend on how skin access is handled. In the future, this could potentially be resolved by making skin deformation a role of the Skeleton, implementing a base class like SkinModifier separate from SkeletonModifier, or implementing a method to process skin directly through shaders, as mentioned several times in the BlendShape discussion.

@TokageItLab TokageItLab force-pushed the bone-expander branch 2 times, most recently from 001ea78 to 067f6a2 Compare October 31, 2025 20:12
@TokageItLab TokageItLab force-pushed the bone-expander branch 3 times, most recently from 77d0b8f to 318f562 Compare November 3, 2025 17:53
@TokageItLab TokageItLab requested a review from a team as a code owner November 3, 2025 17:53
@TokageItLab TokageItLab force-pushed the bone-expander branch 2 times, most recently from a54de73 to a6b5289 Compare November 4, 2025 23:02
@TokageItLab TokageItLab force-pushed the bone-expander branch 4 times, most recently from d17a6e3 to 42432f3 Compare November 5, 2025 13:17
@TokageItLab

TokageItLab commented Nov 5, 2025

Copy link
Copy Markdown
Member Author

Since the performance loss was less than expected, I set the default for mutable_bone_axes to true as it resolves the Gizmo issue with the root bone mentioned in #110120 's description.

Known issue
Moving the position breaks the rendering of the Limitation gizmo because binding to position alone is impossible. To fix it, an approach such as canceling rotation within the shader like godotengine/godot-proposals#9735 is required. Currently, it is drawn by a hack to bind the limitation to the parent joint. See also #111815.

Although it could potentially be non-optional (debatable).

Instead, I added some more detail to the documentation.

BTW, BoneExpander3D still has performance and stability concerns, so it remains marked as experimental.

Comment thread scene/3d/bone_expander_3d.cpp Outdated
Comment thread doc/classes/Skeleton3D.xml Outdated
Comment thread doc/classes/BoneExpander3D.xml Outdated
Comment thread scene/3d/spring_bone_simulator_3d.cpp Outdated
Comment thread scene/3d/bone_expander_3d.h Outdated
Comment thread scene/3d/bone_expander_3d.cpp Outdated
Comment thread scene/3d/spline_ik_3d.cpp Outdated
Comment thread doc/classes/BoneExpander3D.xml Outdated
Comment thread doc/classes/IKModifier3D.xml Outdated
Comment thread doc/classes/SpringBoneSimulator3D.xml Outdated
Comment thread scene/3d/skeleton_3d.cpp Outdated
@TokageItLab TokageItLab requested a review from lyuma November 14, 2025 19:25
@TokageItLab TokageItLab changed the title Add BoneExpander3D and allow Spring / IK to set mutable bone axes Allow Spring / IK to set mutable bone axes Nov 14, 2025
@TokageItLab

TokageItLab commented Nov 14, 2025

Copy link
Copy Markdown
Member Author

Now we omit the BoneExpander from this PR to prioritize adding mutable_bone_axes to 4.6 based on the meeting.

@lyuma lyuma left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved in animation meeting

@Repiteo Repiteo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has same doc issues as #112524

@Repiteo Repiteo merged commit c7bed1e into godotengine:master Nov 17, 2025
20 checks passed
@github-project-automation github-project-automation Bot moved this from Ready for review to Done in Animation Team Issue Triage Nov 17, 2025
@Repiteo

Repiteo commented Nov 17, 2025

Copy link
Copy Markdown
Contributor

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants