A chain of any length
Open the live demo · Read the source · View on GitHub
Two bones are one shape; a tail, a rope or a tentacle is a chain of however
many the rig has. FabrikIk solves that general case: pin the tip to the
target, walk backward re-placing each joint at its fixed distance from the
next, then pin the root back where it started and walk forward the same way,
repeated until the tip is close enough or the iteration budget runs out.
Step 1: A chain, not a fixed shape #
Building the pose is the same idea as two-bone IK, just for however many
joints _jointCount says. Nothing about Pose or FabrikIk knows there
used to be a fixed shoulder-elbow-wrist story here.
final List<int> parents = <int>[
-1,
for (var i = 1; i < _jointCount; i++) i - 1,
];
final Float32List translations = Float32List(_jointCount * 3);
final Float32List rotations = Float32List(_jointCount * 4);
final Float32List scales = Float32List(_jointCount * 3);
for (var i = 0; i < _jointCount; i++) {
translations[i * 3 + 1] = i == 0 ? 0.0 : -_boneLength;
rotations[i * 4 + 3] = 1.0;
scales[i * 3] = 1.0;
scales[i * 3 + 1] = 1.0;
scales[i * 3 + 2] = 1.0;
}
_pose = Pose(
parents: parents,
restTranslations: translations,
restRotations: rotations,
restScales: scales,
);
Step 2: Solve it every frame #
FabrikIk.solve takes the joint list root to tip and a target, and moves
every one of them. The result still only exists on the pose until it is
written onto the beads this page draws.
final double maxReach = _boneLength * (_jointCount - 1);
final Vector3 direction = Vector3(0.5, -0.8, 0.4)..normalize();
final Vector3 target = direction * (reach * maxReach * 0.85);
_pose.resetToRest();
FabrikIk.solve(
_pose,
joints: <int>[for (var i = 0; i < _jointCount; i++) i],
target: target,
);
_pose.writeTo(_joints);
_targetMarker.setPositionFrom(target);
final Vector3 tip = _pose.worldMatrices()[_jointCount - 1].getTranslation();
_lastError = (tip - target).length;
Step 3: Watch the last bead answer #
The same lines from step 2 measure the tip's own distance from the target after solving, which is what this page checks against on every frame.
final double maxReach = _boneLength * (_jointCount - 1);
final Vector3 direction = Vector3(0.5, -0.8, 0.4)..normalize();
final Vector3 target = direction * (reach * maxReach * 0.85);
_pose.resetToRest();
FabrikIk.solve(
_pose,
joints: <int>[for (var i = 0; i < _jointCount; i++) i],
target: target,
);
_pose.writeTo(_joints);
_targetMarker.setPositionFrom(target);
final Vector3 tip = _pose.worldMatrices()[_jointCount - 1].getTranslation();
_lastError = (tip - target).length;
Drag Reach and follow the orange bead, the tip of the chain. The chain bends smoothly along its whole length rather than concentrating the bend at one joint, which is what a rope or a tail is expected to look like.
What to look at #
Push Reach toward one and the chain straightens into nearly a single line reaching for the target; pull it back and the chain curls, since a shorter reach leaves the solve more freedom in how it distributes the bend along four joints instead of two.