Class HumanoidBoneSet
- Namespace
- CodeSmile.AnyMotion.AnimateYourself.Spaces /
- Assembly
- ApiSource.dll
Resolved bone Transforms of one Humanoid rig, plus a snapshot of their bind pose.
public sealed class HumanoidBoneSet
Expand Details ...
- Inheritance
-
objectHumanoidBoneSet
Remarks
Every driver space that writes Transforms must restore the bind pose before it writes, otherwise each frame's rotation is applied on top of the previous frame's and the pose drifts without bound. Restoring first makes each frame's write absolute.
The snapshot is taken once at bind time. That is correct while the Animator has no Controller, which is the case for the P2 spike. Once an Animator clip drives the rig, the restore point must become the Animator's output for that frame instead, and these drivers have to run as a layer on top of it rather than from a fixed snapshot.
Constructors
HumanoidBoneSet(Animator, BindPose)
public HumanoidBoneSet(Animator animator, BindPose bindPose = null)
Parameters
| Type | Name | Description |
|---|
Properties
BindPoseSource
Where the reference pose came from, for display in the inspector.
public string BindPoseSource { get; }
Property Value
- string
Root
Transform the gait's local-space positions are expressed relative to.
public Transform Root { get; }
Property Value
- Transform
Methods
ApplyRestRotation(HumanBodyBones, float)
L11: puts a bone at the rest posture CaptureRestDirection(HumanBodyBones, HumanBodyBones, Vector3) measured for it,
applying no authored angle. This is the posture the arm IK solves from, so the arm's roll comes
from a hanging arm rather than from whatever pose the model was authored in.
weight 0 leaves the bind pose untouched and 1 is the full correction, so
fading a driver in from zero weight does not snap a T-posed arm to hanging in one frame. Does
nothing when no rest direction was captured for this bone.
public void ApplyRestRotation(HumanBodyBones bone, float weight)
Parameters
| Type | Name | Description |
|---|---|---|
bone |
||
weight |
BindLocalPosition(HumanBodyBones)
public Vector3 BindLocalPosition(HumanBodyBones bone)
Parameters
| Type | Name | Description |
|---|---|---|
bone |
Returns
- Vector3
BindLocalRotation(HumanBodyBones)
public Quaternion BindLocalRotation(HumanBodyBones bone)
Parameters
| Type | Name | Description |
|---|---|---|
bone |
Returns
- Quaternion
CaptureHinge(HumanBodyBones, HumanBodyBones, HumanBodyBones, Vector3, Vector3)
Works out which axis a hinge joint bends about, and in which direction, then stores it for RotateHinge(HumanBodyBones, float).
public void CaptureHinge(HumanBodyBones upperBone, HumanBodyBones hingeBone, HumanBodyBones endBone, Vector3 fallbackRootAxis, Vector3 intendedRootMotion)
Parameters
| Type | Name | Description |
|---|---|---|
upperBone |
||
hingeBone |
||
endBone |
||
fallbackRootAxis |
||
intendedRootMotion |
Remarks
Parent-space axes are still model-dependent, so a fixed choice such as "the knee bends about local X" is wrong on some rigs. Two steps remove the dependency:
-
Take the axis from the geometry — the normal of the plane through the two limb
segments. A rig posed with any bend at all gives this directly. When the limb is
perfectly straight the cross product is degenerate, so fall back to
fallbackRootAxisin character space. -
Fix the sign by measurement rather than by assumption: rotate a test amount, see which
way the end of the chain actually moved, and flip the axis if it went the wrong way.
intendedRootMotionis where a positive angle should send it, in character space — a flexing knee sends the foot backwards, a flexing elbow sends the hand forwards.
Runs once per rig at bind time, so the cost does not matter.
CaptureRestDirection(HumanBodyBones, HumanBodyBones, Vector3)
L11 (arm layering), arm-swing-spec.md §2: measures how far bone's
bind-pose direction is from restDirectionRootSpace and stores the
correction, so a later RotateAboutRootAxes(HumanBodyBones, float, float, float) starts from a known rest posture
instead of from whatever pose the model was authored in. Same principle as
CaptureHinge(HumanBodyBones, HumanBodyBones, HumanBodyBones, Vector3, Vector3): measured per rig, not configured per rig.
public void CaptureRestDirection(HumanBodyBones bone, HumanBodyBones childBone, Vector3 restDirectionRootSpace)
Parameters
| Type | Name | Description |
|---|---|---|
bone |
||
childBone |
||
restDirectionRootSpace |
Get(HumanBodyBones)
Returns the bone's Transform, or null when the Avatar does not map it.
public Transform Get(HumanBodyBones bone)
Parameters
| Type | Name | Description |
|---|---|---|
bone |
Returns
- Transform
Has(HumanBodyBones)
public bool Has(HumanBodyBones bone)
Parameters
| Type | Name | Description |
|---|---|---|
bone |
Returns
- bool
HasHingeAxis(HumanBodyBones)
public bool HasHingeAxis(HumanBodyBones bone)
Parameters
| Type | Name | Description |
|---|---|---|
bone |
Returns
- bool
ResolveRestRotation(Vector3, Vector3)
Minimal rotation taking a measured bind direction onto the wanted rest direction, both in
root space. Minimal because a larger rotation would introduce an axial twist nothing asked
for — the same reason L05 (foot planting on uneven ground) uses FromToRotation for the
ground normal.
public static Quaternion ResolveRestRotation(Vector3 bindDirectionRootSpace, Vector3 restDirectionRootSpace)
Parameters
| Type | Name | Description |
|---|---|---|
bindDirectionRootSpace |
||
restDirectionRootSpace |
Returns
- Quaternion
RestoreBindPose()
Puts every tracked bone back to the pose captured at bind time.
public void RestoreBindPose()
RestoreBindPose(HumanBodyBones)
Puts one tracked bone back to the pose captured at bind time.
public void RestoreBindPose(HumanBodyBones bone)
Parameters
| Type | Name | Description |
|---|---|---|
bone |
Remarks
Needed by anything that has to solve the same bone more than once in a frame. Every write in this system composes onto whatever the bone already holds, so a second solve without a restore first lands on top of the first one instead of replacing it.
RotateAboutRootAxes(HumanBodyBones, float, float, float)
Rotates a bone about the character's own axes, applying the three rotations in an explicit anatomical order rather than as one Euler triple.
public void RotateAboutRootAxes(HumanBodyBones bone, float pitch, float twist, float spread)
Parameters
| Type | Name | Description |
|---|---|---|
bone |
||
pitch |
||
twist |
||
spread |
Remarks
RotateInRootSpace(HumanBodyBones, Vector3) takes a Vector3 and passes it to
Quaternion.Euler, which applies the components in a fixed Z-X-Y order. For small
angles that is close enough to independent per-axis control, but the three rotations couple as
the angles grow, so "20° of pitch and 25° of spread" stops meaning what it reads as — a leg can
end up displaced sideways rather than swung forward and abducted. The rigs with the widest hips
produce the largest spread values and so show this first.
Order applied to the bone: the rest correction from CaptureRestDirection(HumanBodyBones, HumanBodyBones, Vector3) (if one was captured) first, then twist about the character's up axis, then pitch about its right axis, then spread about its forward axis outermost. For a limb hanging downwards the up axis is the limb's own length axis, so twist is a true axial twist. For a limb held out sideways it is not, which is why arm twist is left at zero by the gait.
RotateChestLevelInRootSpace(Quaternion)
Rotates the chest about the character's axes and returns the part of that rotation the bones above and beside the chest have to apply to themselves, because this Avatar maps no chest bone for them to inherit it from.
public bool RotateChestLevelInRootSpace(Quaternion chestRotation)
Parameters
| Type | Name | Description |
|---|---|---|
chestRotation |
Everything the chest level has to turn by this frame, in the character's frame, already combined by the caller. It takes a UnityEngine.Quaternion and not an euler triple so that the caller decides which rotation sits outside the other, at the one place that holds both. |
Returns
- bool
truewhen this Avatar maps Chest or UpperChest, because those bones have taken the rotation and everything above them inherits it.falsewhen it maps neither. The caller must then write the chest's rotation itself on the neck — or on the head, when the Avatar maps no neck either — and on both shoulders, so that everything above the chest ends up where a mapped chest bone would have carried it. The caller multiplies that carry onto each bone's own rotation, asQuaternion.Euler(theBonesOwnRotation) * carry: a mapped chest bone turns first and the bone above it turns on top of the result, so the carry has to compose the same way round.What the caller carries is the gait's own chest rotation alone, not
chestRotation. Look-at's share of the chest reaches no bone on such a rig, so the head stops short of the target by it: Robot Kyle reaches 84.5 degrees for a 90-degree target where a chest-mapping rig reaches 94.5. That shortfall is known, it is unchanged from before the carry was written, and it is left to its own task.
Remarks
Robot Kyle is a rig of this kind: its Avatar maps Spine, Neck and Head and no chest at all. Without the carry, the chest rotation is simply dropped on such a rig, which has two visible consequences. Chest Twist moves nothing whatsoever — measured 2026-09-11 on Kyle-1000, sweeping it from 0 to 60 degrees left every bone in the character at the same angle to four decimal places. And Shoulder Twist turns the shoulders further the way the spine already turned instead of against it, because the chest rotation it is measured against never happened: the shoulder line's side-to-side swing grew from 47.9 to 88.3 degrees as that setting went from 0 to 60. The chest's own mesh deformation is the one thing the carry cannot recover, and no rig without a chest bone can produce it.
GaitMath.ChestYawReachingTheHead states, as one number per bone mapping, how far
the head ends up turned by what this method writes. Change which bone takes what here and
that function is wrong until it is changed to match, and the head stops cancelling the
chest's turn at Head.CounterTwistWeight 1.
RotateHinge(HumanBodyBones, float)
Bends a hinge joint by degrees about the axis found by
CaptureHinge(HumanBodyBones, HumanBodyBones, HumanBodyBones, Vector3, Vector3), starting from the bind pose. A positive value always flexes,
on every rig.
public void RotateHinge(HumanBodyBones bone, float degrees)
Parameters
| Type | Name | Description |
|---|---|---|
bone |
||
degrees |
RotateInParentSpace(HumanBodyBones, Vector3)
Rotates a bone by eulerInParentSpace degrees about its parent's axes,
starting from the bind pose. Correct for hinge joints — knee, elbow, ankle — whose bend is
defined relative to the limb above them and not relative to the character.
public void RotateInParentSpace(HumanBodyBones bone, Vector3 eulerInParentSpace)
Parameters
| Type | Name | Description |
|---|---|---|
bone |
||
eulerInParentSpace |
RotateInRootSpace(HumanBodyBones, Quaternion)
Rotates a bone by an already-built rotation about the character's axes rather than the bone's own axes, starting from the bind pose. Same operation as the euler form above; this one exists for a caller that has to combine two rotations before writing them.
public void RotateInRootSpace(HumanBodyBones bone, Quaternion rotationInRootSpace)
Parameters
| Type | Name | Description |
|---|---|---|
bone |
||
rotationInRootSpace |
Remarks
Combine two rotations by multiplying quaternions, never by adding two euler triples. Adding
the triples gives the same answer only while one of the two turns about a single axis:
Quaternion.Euler builds Ry · Rx · Rz, so a yaw and a pitch that are added end up
applied in that fixed order rather than one after the other, and rotations about different
axes do not commute. Multiplying in the order the bones sit in gives the orientation the
hierarchy itself would have produced, whatever axes the two rotations use.
RotateInRootSpace(HumanBodyBones, Vector3)
Rotates a bone by eulerInRootSpace degrees about the character's axes
rather than the bone's own axes, starting from the bind pose.
public void RotateInRootSpace(HumanBodyBones bone, Vector3 eulerInRootSpace)
Parameters
| Type | Name | Description |
|---|---|---|
bone |
||
eulerInRootSpace |
Remarks
This is the operation that makes a preset portable. "Pitch the thigh forward 20°" is a different local-axis rotation on every rig, because bone local axes are set by whoever authored the model. Expressing the rotation in the character's own frame (X right, Y up, Z forward) and conjugating it into the bone's parent space gives the same visible result on any rig, with no per-rig axis configuration.