X Tutup
Skip to content

Add SkeletonModifier3D IKs as IKModifier3D#110120

Merged
Repiteo merged 1 commit intogodotengine:masterfrom
TokageItLab:ik-modifier-3d
Nov 4, 2025
Merged

Add SkeletonModifier3D IKs as IKModifier3D#110120
Repiteo merged 1 commit intogodotengine:masterfrom
TokageItLab:ik-modifier-3d

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
    • Jacobian transpose
    • 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 a review from a team August 30, 2025 17:39
@TokageItLab TokageItLab requested a review from a team as a code owner August 30, 2025 17:39
@TokageItLab TokageItLab requested a review from a team August 30, 2025 17:39
@TokageItLab TokageItLab requested a review from a team as a code owner 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 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.

@5tuv
Copy link

5tuv commented Nov 4, 2025

Thank god for this. LookUpModifier3D ain't it.

@JekSun97
Copy link
Contributor

JekSun97 commented Nov 4, 2025

Let's combine this with master!

@Repiteo Repiteo merged commit 3fb8961 into godotengine:master Nov 4, 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 Nov 4, 2025
@Repiteo
Copy link
Contributor

Repiteo commented Nov 4, 2025

Thanks!

@ettiSurreal
Copy link
Contributor

ettiSurreal commented Nov 6, 2025

@TokageItLab Also, if SkeletonIK3D does get removed in the future, I think we should just keep "IKModifier3D" as this modifier's name. I think it works better than "SkeletonIK3D", is more consistent with other modifiers, and by that point users will be used to the name of the new modifier.

@jitspoe
Copy link
Contributor

jitspoe commented Nov 7, 2025

So I gave this a test, but the results seem far less stable than the existing SkeletonIK3D. I think part of the problem might be that there's no way to to make the IK target follow the rotation of the target, like the "Override Tip Basis" setting on SkeletonIK3D. Though I just turned that off and the rotation of the target still impacts the bones on SkeletonoIK3D, while with your IK modifiers the rotation seems to be completely ignored. With a long tentacle type object it just sort of freaks out and goes loopy. This is with the Fabrik one, which I would expect to behave most similar to SkeletonIK3D:

image

And here's with SkeletonIK3D:

image

@TokageItLab
Copy link
Member Author

TokageItLab commented Nov 7, 2025

So I gave this a test, but the results seem far less stable than the existing SkeletonIK3D. I think part of the problem might be that there's no way to to make the IK target follow the rotation of the target, like the "Override Tip Basis" setting on SkeletonIK3D.

@jitspoe As I mentioned in the description, we consider this to be the role of BoneConstraint rather than IK, so it is supplemented by #110336.

Another possible cause is when the change of the bone pose position or scale from the bone rest is included in the chain, but that needs to be supplemented by #111815.

FYI, in FABRIK, setting the Angular Delta Limit to 180 degrees (means no limitation) should make the rotation more similar to old SkeletonIK3D.

@jitspoe
Copy link
Contributor

jitspoe commented Nov 7, 2025

Try this:

test-different-ik.zip

I made a simplified example. SkeletonIK3D goes straight to the target, but the FABRIK3D meanders and twists around.

fabrik_ik

@TokageItLab
Copy link
Member Author

TokageItLab commented Nov 7, 2025

@jitspoe As mentioned above, meanders twisting is related to the angular delta limit.

image

The old SkeletonIK has no rotation limit per iteration, making it equivalent to setting an 180-degree angular delta limit.

And another difference between old SkeletonIK vs IKModifier3D's FABRIK is whether it is deterministic or not (IKModifier3D's FABRIK is non-deterministic).

As mentioned in #110120 (comment), resetting rotation every frame to the rest makes deterministic results. But this increases the number of calculations required to reach the result, depending on the angular delta limit, impacting performance. In other words, after resetting the rotation in the beggining of the each frame, if the angular delta limit is set too small with less iteration, the end bone will never reach the target, no matter how many frames are progressed.

However, in cases without the limitations mentioned above, such as setting the angular delta limit to 180 degrees, performance should not be significantly affected (since a large number of iterations is not required). Therefore, I believe deterministic behavior can be added as an option (I already discussed this with @lyuma during the review meeting).

@TokageItLab
Copy link
Member Author

@jitspoe @ettiSurreal @lyuma I sent a PR #112524 to add deterministic option, so you can test it.

@jitspoe
Copy link
Contributor

jitspoe commented Nov 9, 2025

Ok, setting the angular limit to 180 seems to make FABRIK work, but the Jakobian on just spazzes out like crazy, especially when it's near the max range or out of range.

jacobian_ik

@TokageItLab
Copy link
Member Author

TokageItLab commented Nov 9, 2025

@jitspoe The JacobianIK requires a small angular delta limit for stable results, so this is expected behavior (although the behavior outside the range might be stabilized a bit more). I assume we should document the recommended settings somewhere.

Edited:
Sorry, the gradient of the distance was not applied to angle correctly. I've sent the fix in #112573. Please test it if you can. It should be more stable as far as the end can reach the target. But in any case, large angular delta will cause the oscilation in JacobianIK.

Godot.Engine.2025.11.09.-.16.07.52.04.mp4

@YahelBaram
Copy link

YahelBaram commented Nov 15, 2025

Hi

I'm currently using Godot 4.6 dev 4, and I'm doing some tests for the new IKs system. While I'm pretty happy with the results when it comes to bones positioning, I don't see anything related to rotation (For example, rotating the hand of a player model in a way the entire arm will react to the changes of the hand) . I wanted to know if I'm missing something or if it's not in the scope of this PR in the first place.

Thanks ahead!

@TokageItLab
Copy link
Member Author

TokageItLab commented Nov 15, 2025

@YahelBaram That is considered more the role of BoneConstraint than IK, and we agreed it upon during the animation meeting.

For example, when the twist axis is +Y, the propagation of hand rotation can be fulfilled by transferring the +Y rotation of the hand to the +Y axis rotation of the LowerArm/UpperArm using BoneConstraint(Copy/ConvertTransformModifier3D). See also #100984, #110336 and #111367.

image

When propagating a child's rotation to its parent using constraints, a final constraint for canceling by self-references is required (as above image, processing the sequence that UpperLeg 0.25 of Foot -> LowerLeg 0.5 of Foot -> Foot -0.75 of Foot), but it should work as intended as long as the rotation does not exceed 180 degrees from rest.

@x0r-b0t
Copy link

x0r-b0t commented Nov 22, 2025

Hi

I've been playing around with TwoBoneIK3D in 4.6 dev 4, and it's been working great so far. However, today I built master from commit 235a32a to test out #110336 and some other changes, and now TwoBoneIK3D logs repeatedly:

ERROR: The vectors must not be zero.
   at: Quaternion (./core/math/quaternion.h:140)

@TokageItLab have you tested TwoBoneIK3D against a more recent master? If it's not happening in your tests, let me know and I can put together an MRP.

I built Godot with:
scons platform=linuxbsd use_llvm=yes linker=lld debug_symbols=yes

System info:

System:
  Kernel: 6.8.0-87-generic arch: x86_64 bits: 64 compiler: gcc v: 13.3.0 clocksource: tsc
  Desktop: Cinnamon v: 6.4.8 tk: GTK v: 3.24.41 wm: Muffin v: 6.4.1 vt: 7 dm: LightDM v: 1.30.0
    Distro: Linux Mint 22.1 Xia base: Ubuntu 24.04 noble
CPU:
  Info: dual core model: Intel Core i3-7100 bits: 64 type: MT MCP smt: enabled arch: Kaby Lake
    rev: 9 cache: L1: 128 KiB L2: 512 KiB L3: 3 MiB
  Speed (MHz): avg: 800 min/max: 800/3900 cores: 1: 800 2: 800 3: 800 4: 800 bogomips: 31199
  Flags: avx avx2 ht lm nx pae sse sse2 sse3 sse4_1 sse4_2 ssse3 vmx
Graphics:
  Device-1: NVIDIA TU116 [GeForce GTX 1660] driver: nvidia v: 580.95.05 arch: Turing pcie:
    speed: 5 GT/s lanes: 16 ports: active: none off: DP-1,HDMI-A-1 empty: DVI-D-1 bus-ID: 01:00.0
    chip-ID: 10de:2184 class-ID: 0300
  Display: x11 server: X.Org v: 21.1.11 with: Xwayland v: 23.2.6 driver: X: loaded: nvidia
Info:
  Memory: total: 32 GiB available: 31.3 GiB used: 5.54 GiB (17.7%)
  Compilers: clang: 18.1.3 gcc: 13.3.0 Client: Cinnamon v: 6.4.8 inxi: 3.3.34

@TokageItLab
Copy link
Member Author

TokageItLab commented Nov 22, 2025

@x0r-b0t It seems to be an issue with the dynamic bone axis direction calculation by mutable_bone_axis. I'll take a look.

Edited:
I sent a fix as #113055.

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.

X Tutup