Skip to content

Conversation

@TokageItLab
Copy link
Member

Add BoneTwistDisperser3D.

As commented in #110120 (comment), using BoneConstraints can provide a solution for propagating twist to the parent to some extent. However, in cases where the twist axis does not align with the XYZ basis, the solution is not as straightforward. Furthermore, since the canceled twist must be applied to the target itself, it is not easy to use for not advanced users.

This allows them to automate these processes by creating a chain. Of course, it can be used even without IK.

dp.mp4

ik_test_match_target.zip

@TokageItLab TokageItLab force-pushed the twist-disperser branch 2 times, most recently from 2f6cee2 to 71accb8 Compare November 28, 2025 16:26
@TokageItLab TokageItLab moved this to Ready for review in Animation Team Issue Triage Nov 28, 2025
@TokageItLab TokageItLab force-pushed the twist-disperser branch 2 times, most recently from a1f7ce0 to 860ddab Compare November 28, 2025 22:16
@TokageItLab TokageItLab marked this pull request as draft November 29, 2025 06:47
@TokageItLab TokageItLab force-pushed the twist-disperser branch 2 times, most recently from cda9745 to 51ca55c Compare November 29, 2025 08:32
@TokageItLab
Copy link
Member Author

TokageItLab commented Nov 29, 2025

There is request for a curve editor from @lyuma, so now it is implemented.

Thereby, twist weight has been changed to twist amount without normalization. Also, it has been added to the documentation that the auto assigning amount except for DISPERSE_MODE_CUSTOM are monotonically increasing.

image

Whether twist_amount is read-only depends on the existence of the Curve resource.


Can be considered the future follow up

The horizontal axis of the curve currently represents the index rather than the bone length, but since this implementation is the same as SpringBoneSimulator3D, it could be improved as a follow-up in the future .

The same applies to improvements for extracting twists greater than 180 degrees; this should be fixed at the same time as ConvertTransformModifier3D.

@TokageItLab TokageItLab marked this pull request as ready for review November 29, 2025 08:40
@TokageItLab TokageItLab force-pushed the twist-disperser branch 5 times, most recently from 9b846ad to 8fafdec Compare November 29, 2025 17:56
@TokageItLab
Copy link
Member Author

TokageItLab commented Nov 29, 2025

As I've mentioned several times before, the IKModifier itself only changes the joint placement/rotation using only Swing , without Twist. So these modules for the twist may become that IK enhancement as option.

For example, it should be interesting to see how the behavior of IK can be quite varied due to configuration differences like the following:


  • Simple non-deterministic FABRIK:
image
simple_fabrik_and_target.mp4

  • Pre-twist the root using aim
  • Further pre-twist the target and then sparse
  • Finally, performing deterministic FABRIK can stabilize the bend direction to some extent
image
combination.mp4

ik_combination.zip

In other words, a rich IK component has these nodes as member options (like bool to call internal function) for IK.

However, since these calculations are also useful outside of IK, having them as separate nodes makes sense IMO. Especially when using custom IK, reusability is a big benefit. Although we will need to prepare several tutorials and demos later on - or we can provide a wrapper component as an addon to automate these configurations.

@TokageItLab TokageItLab requested review from fire and lyuma November 30, 2025 14:56
@fire
Copy link
Member

fire commented Dec 1, 2025

In a discord conversation we discussed the issue of order problem -- like pre-ik mnodifiers vs post-ik modifiers solving causing incorrect results.

A resolution is to use documentation as a workaround by documenting placing the CopyConstraint and TwistDisperse before IK. If we ultimately need the tip to face the target, we can apply CopyConstraint again after IK. See #113284 (comment) demo.

@TokageItLab
Copy link
Member Author

In practice, whether to apply IK before or after depends on the specific use case.

For example, if you want to apply a Twist constraint to IK,
first perform unrestricted Twist using CopyTransform+Target and TwistDisperser, then either use RotationAxis in IK afterward, or restrict the Twist range before IK using ConvertConstraint or similar. Finally, depending on how you want the tip to orient toward the target, use CopyTransform or AimModifier after IK. In some cases, users may need to add additional twist restrictions here.

For simpler cases, such as twisting tentacles or vines, using TwistDisperser after IK alone is sufficient.

However, these are examples for combining with IK and are off-topic for discussions about the TwistDisperser API itself. TwistDisperser is purely a node that mathematically dissipates and distributes twist along a chain.

Copy link
Member

@fire fire left a comment

Choose a reason for hiding this comment

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

Reviewed the math on the twist.

Regarding the before and after of IK vs BoneTwistDisperser3D, a documentation solution was devised - see relevant comments above.

@lyuma is also reviewing.

Looks good to me.

@TokageItLab TokageItLab moved this from Ready for review to Approved, Waiting for Production in Animation Team Issue Triage Dec 2, 2025
@TokageItLab
Copy link
Member Author

Tweaked icon to reduced usage by removing unnecessary points. Appearance remains unchanged.

Copy link
Contributor

@lyuma lyuma left a comment

Choose a reason for hiding this comment

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

It looks correct.

There are some parts of the design that are unintuitive: notably, BoneTwistDisperser3D is difficult to use on many skeletons unless extend_end_bone is true, since otherwise you have to target a child of the target bone.

Also I found that it does not function as I would expect when targeting root_bone = Hips (due to Hips having a large origin relative to 0,0,0), but I'm not sure it's wrong, but rather this is mostly a documentation issue.

Example showing how it can be used for twisting the spine of a character when solving IK for a character's spine.
godot_twist_disperser_renik2

@GeorgeS2019

This comment was marked as off-topic.

@TokageItLab TokageItLab force-pushed the twist-disperser branch 4 times, most recently from 45adfcc to 7b9e621 Compare December 2, 2025 10:15
@akien-mga akien-mga merged commit 2c8a0c3 into godotengine:master Dec 2, 2025
20 checks passed
@github-project-automation github-project-automation bot moved this from Approved, Waiting for Production to Done in Animation Team Issue Triage Dec 2, 2025
@akien-mga
Copy link
Member

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.

6 participants