Skip to content

Conversation

@TokageItLab
Copy link
Member

@TokageItLab TokageItLab commented Aug 30, 2025

Add several IK modifiers. As the name SkeletonIK3D conflicts with an older node, I will adopt the provisional name ManyBoneIK3D. Considering that the LookAtModifier targets only a single bone, I think this is a reasonable name. Tentatively it now named as IKModifier3D.

image

ManyBoneIK3D has the following extended classes:

  • IKModifier3D
    • TwoBoneIK3D
    • ChainIK3D
      • SplineIK3D
      • IterateIK3D
        • FABRIK3D
        • CCDIK3D
        • JacobianIK3D

There may be demand for FullbodyIK3D, but for now, this PR aims to fulfill the need for SkeletonModificationStack3D which was removed from 3.x to 4.0. (So don't close - godotengine/godot-proposals#6039)

I expect that FullbodyIK3D could be implemented in the future by extending one of the base classes. Until then, I assume PhysicalBoneSimulator can serve as a substitute in some extent.

Also this PR includes refactoring to unify enums for bone direction among some SkeletonModifiers.

IKModifier3D

It has a virtual method for having multiple configurations and a virtual bone structure for simulating IK to be able to hold poses and rests separately from the Skeleton.

TwoBoneIK3D

It provides deterministic results by constructing a plane from each joint and pole target and finding the intersection of two circles (disks in 3D). This IK can handle twist by setting the knuckle direction.

image
tbik.mp4

If there are more than one bones between each set bone, their rotations are ignored, and the straight line connecting the root-middle and middle-end joints are treated as virtual bones.

Godot.Engine.2025.08.30.-.22.29.34.06.mp4

ChainIK3D

It has a virtual method that generates a chain by setting root bone and end bone.

SplineIK3D

This IK aligning bones on Path3D. Bone twist is determined relatively based on the curve's tilt.

Godot.Engine.2025.08.30.-.22.29.34.05.mp4

If the root bone joint and the start point of Curve3D are apart, it assumes that there is a linear line segment between them. If the end bone joint exceeds the path length, bend the first joint exceeding the path length as close as possible to the end point of the Curve3D, and from there it is extended straight ahead - it should look almost the same as Blender's SplineIK.

IterateIK3D

It has a virtual method to approach the goal by repeating small rotations.

  • FABRIK3D
    • forward and backward reaching
    • good result when there are no limitations
  • CCDIK3D
    • cyclic coordinate descent
    • good result for mechanical movements
  • JacobianIK3D
    • pseudo inverse Jacobian matrix
    • good result for biological movements
Godot.Engine.2025.08.30.-.22.29.34.04.mp4

Each bone chain (setting) has one effector, which is processed in order of setting list. You can set some limitations for each joint.

image

Limitations currently only support rotation axis and cone shapes. You can set arbitrary rotation offsets on each joint. BTW, there is potential for future reuse in SpringBone (ref. vrm-c/vrm-specification#496). Also I expect that kusudama shapes like godotengine/godot-proposals#11208 will be added in the future.

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.

Incompatibility with old SkeletonIK3D

Regarding the pole target (magnet), I tried implementing it but finally decided against it because I couldn't stabilize it. This was due to strong conflicts with limitations and the difficulty in acquiring good results with solvers other than FABRIK. Even with FABRIK (both the version implemented this time and the current SkeletonIK3D), it is difficult to consistently obtain ideal results.

For these purposes, I determined it is more appropriate to use IK that can deterministically control direction, such as the TwoBoneIK and SplineIK described above, depending on the use case.

If someone needs a magnet no matter what, it possibly could be added later as a follow up PR. (If it can be stabilized)

If you want rotation on a plane, you can set the RotationAxis for absolute axis specification. For dynamic planes, you can use a LookAtModifier to orient the chain root specific (knuckle) axis to secondary (or same) target before IterateIK, which should achieve that. In other words, probably the general (other software's) chain IK's pole is processed in this way - only determining the twist of the first joint.

Also, omitted the functionality to apply the target's rotation to the tip. If we needed, the correct approach should be to allow users to specify a node in BoneConstraint3D (CopyTransformModifier3D) for arbitrary rotation transformations, rather than hard-coding it into the IK. See also #110336.


Demo project

ik_test.zip

@TokageItLab TokageItLab added this to the 4.6 milestone Aug 30, 2025
@TokageItLab TokageItLab requested a review from a team August 30, 2025 17:38
@TokageItLab TokageItLab requested a review from a team as a code owner August 30, 2025 17:38
@TokageItLab TokageItLab requested review from a team as code owners August 30, 2025 17:39
@TokageItLab TokageItLab requested review from a team as code owners August 30, 2025 17:39
@TokageItLab TokageItLab moved this to Ready for review in Animation Team Issue Triage Aug 30, 2025
@fire fire self-requested a review August 30, 2025 18:03
@realcoloride
Copy link

Cool PR

@TokageItLab TokageItLab force-pushed the ik-modifier-3d branch 7 times, most recently from ca9d2b8 to ecc0595 Compare September 1, 2025 00:54
@fire fire changed the title Add some SkeletonModifier3Ds for IK as ManyBoneIK3D Add SkeletonModifier3D IKs as ManyBoneIK3D Sep 1, 2025
@TokageItLab TokageItLab force-pushed the ik-modifier-3d branch 3 times, most recently from 52af90b to a3dda7d Compare September 1, 2025 17:31
@TokageItLab TokageItLab force-pushed the ik-modifier-3d branch 2 times, most recently from 4d811b0 to 6e06853 Compare October 15, 2025 21:45
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.

I'm leaving some comments about property validation of ChainIK3D (the base class), since the logic and flow for bone-name <-> bone-index assignment, and updating of skeleton joints is overly complex and I fear there may be corner cases where the update is not done, or done multiple times.

Some examples of corner cases: if the bone name is assigned but not the bone index, or if the bone name and bone index are both assigned but set to different bones. And the same thing but before / after skeleton is assigned. And same thing but for joints but not root bone.

I wish that the flow could be made simpler. For example joint dirty flag + skeleton changed. Or go through a single update function.

I also would like if we can choose bone-name or bone-index, rather than supporting both, since allowing the user to set name or index (or both) adds even more complexity

@JekSun97
Copy link
Contributor

What are the chances of this appearing in 4.6?

@TokageItLab
Copy link
Member Author

We are making progress with focusing on 4.6, doing regular biweekly animation meetings on the contributor chat.

@TokageItLab
Copy link
Member Author

Rebased to the latest.

@lyuma I have responded to the code review comments, but fundamentally, the design of the ManyBoneIK class is based on other nodes (BoneConstraint3D and SpringBoneSimulator3D) already merged, as mentioned in #110120 (comment). Therefore, I assume there is not much we can specifically address here as it should be resolved at the same time as those other nodes.

I suppose the design of the internal struct mentioned in #110120 (comment) could be improved, but since the problem is how to deal the internal array, I believe it can definitely be sent as a future follow up without breaking compatibility.

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.

Discussed in animation meeting with @TokageItLab @SaracenOne @fire and @aaronfranke . I think we are good with merging this, and we can add blog post, do more testing and fix issues afterwards.

@TokageItLab TokageItLab moved this from Ready for review to Approved, Waiting for Production in Animation Team Issue Triage Oct 31, 2025
@TokageItLab TokageItLab force-pushed the ik-modifier-3d branch 2 times, most recently from 3ed7fde to 15bbd6a Compare November 2, 2025 13:24
@TokageItLab TokageItLab changed the title Add SkeletonModifier3D IKs as ManyBoneIK3D Add SkeletonModifier3D IKs as ModifierIK3D Nov 2, 2025
@TokageItLab
Copy link
Member Author

TokageItLab commented Nov 2, 2025

ManyBoneIK3D has been renamed to ModifierIK3D now. Since the opinion is raised by other maintainers that the name ManyBoneIK3D is somewhat unclear.

Since the name SkeletonIK3D is unavailable until Godot 5, the following are the candidates proposed among the maintainers:

  • IKGroup3D
  • NewSkeletonIK3D
  • SkeletonInverseKinematics3D
  • InverseKinematics3D
  • ModifierIK3D (current)

IKGroup3D sounds to have a role beyond that of a superclass (it sounds like a parent class that has an IK as a children in the scene tree) for me.

InverseKinematics3D might be acceptable as a superclass name, but considering Godot as a whole, there was an argument that it was too generic.

SkeletonInverseKinematics3D feels quite verbose, and considering the name FABRIK3D should not be SkeletonFABRIK3D, SkeletonFABRInverseKinematics3D or SkeletonForwardAndBackwardReachingInverseKinematics3D, I believe it should be avoided.

Considering that the words “Skeleton” and “Bone” are used as an identifier to avoid generality, the third identifier could be a "Modifier". Also, considering naming consistency with subclasses like "ChainIK3D", I tentatively prefer "ModifierIK3D".

Please suggest other name candidates if you have any.

@ettiSurreal
Copy link
Contributor

What about IKModifier3D since that seems to be a more consistent naming convention for skeleton modifiers that have the word "modifier" in their name. The only exception to that is ModifierBoneTarget3D which is the only one that has it at the start.

@YahelBaram
Copy link

IKModifier3D
BonesIK3D

@TokageItLab
Copy link
Member Author

TokageItLab commented Nov 2, 2025

@ettiSurreal IKModifier was also a candidate, but considering that its subclasses are ChainIK and IterateIK, I thought consistency with them might suggest using “IK” as the suffix.

However, considering consistency with modifiers like LookAtModifier, “IKModifier” is fine. So it’s just a matter of whether to prioritize consistency with child or sibling classes.

Let's do quick poll on whether to use IKModifier3D or ModifierIK3D by reactions:

🚀 IKModifier3D
👀 ModifierIK3D

@ettiSurreal
Copy link
Contributor

The justification can be that the names of the subclasses could be shortened versions of TwoBoneIKModifier3D, ChainIKModifier3D, and so on.
(not suggesting to rename these to that, clarifying just in case)

@TokageItLab TokageItLab requested a review from a team as a code owner November 3, 2025 17:24
@TokageItLab TokageItLab changed the title Add SkeletonModifier3D IKs as ModifierIK3D Add SkeletonModifier3D IKs as IKModifier3D Nov 3, 2025
@TokageItLab
Copy link
Member Author

I have adopted the name IKModifier for the base class.

The justification can be that the names of the subclasses could be shortened versions of TwoBoneIKModifier3D, ChainIKModifier3D, and so on.
(not suggesting to rename these to that, clarifying just in case)

@ettiSurreal Considering the possibility that SkeletonIK may become available in Godot 5 and rename this to that, I assume the child classes can remain as-is.

Now:
image

@AThousandShips Thank you very much for the large amount of proofreading. I appreciate it, as reviewing this much must have taken a lot of time. Regarding line break spacing, it wasn't marked as outdated since diffs don't detect it, but I should have fixed it.

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

Projects

Status: Approved, Waiting for Production

Development

Successfully merging this pull request may close these issues.

9 participants