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
object
HumanoidBoneSet

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
Animator
animator
BindPose
bindPose

Reference pose to restore to. Pass the mesh bind pose. When null it is read from the rig's current Transforms, which is only safe if the rig is not being driven — see the remarks on BindPose for why that matters.

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
HumanBodyBones
bone
float
weight

BindLocalPosition(HumanBodyBones)

public Vector3 BindLocalPosition(HumanBodyBones bone)

Parameters

Type Name Description
HumanBodyBones
bone

Returns

Vector3

BindLocalRotation(HumanBodyBones)

public Quaternion BindLocalRotation(HumanBodyBones bone)

Parameters

Type Name Description
HumanBodyBones
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
HumanBodyBones
upperBone
HumanBodyBones
hingeBone
HumanBodyBones
endBone
Vector3
fallbackRootAxis
Vector3
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:

  1. 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 fallbackRootAxis in character space.
  2. 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. intendedRootMotion is 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
HumanBodyBones
bone
HumanBodyBones
childBone
Vector3
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
HumanBodyBones
bone

Returns

Transform

Has(HumanBodyBones)

public bool Has(HumanBodyBones bone)

Parameters

Type Name Description
HumanBodyBones
bone

Returns

bool

HasHingeAxis(HumanBodyBones)

public bool HasHingeAxis(HumanBodyBones bone)

Parameters

Type Name Description
HumanBodyBones
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
Vector3
bindDirectionRootSpace
Vector3
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
HumanBodyBones
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
HumanBodyBones
bone
float
pitch
float
twist
float
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
Quaternion
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

true when this Avatar maps Chest or UpperChest, because those bones have taken the rotation and everything above them inherits it.

false when 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, as Quaternion.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
HumanBodyBones
bone
float
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
HumanBodyBones
bone
Vector3
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
HumanBodyBones
bone
Quaternion
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
HumanBodyBones
bone
Vector3
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.