Skip to content

Conversation

@TokageItLab
Copy link
Member

@TokageItLab TokageItLab commented Oct 19, 2025

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.

@fire
Copy link
Member

fire commented Oct 19, 2025

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
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 2 times, most recently from 5076644 to 77d0b8f Compare November 2, 2025 14:01
@TokageItLab TokageItLab requested a review from a team as a code owner November 3, 2025 17:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Ready for review

Development

Successfully merging this pull request may close these issues.

Skeleton add the option to Lock Bone Scaling

2 participants