Skeleton Files - Kamzik123/AnvilToolkit-Resources GitHub Wiki

This tutorial explains how Skeleton files work in Assassin's Creed games — how bones are structured, how parent-child hierarchies define a rig, how BoneModifiers add physics, constraints, and procedural behavior, and how to add or remove bones.

The Skeleton system is part of the AnvilNext engine and the core bone structure is consistent across all AC titles. BoneModifiers have evolved over the series — see BoneModifier History Across Games for details on which modifiers are available in each title.

All examples are taken from real exported AC4: Black Flag game files using AnvilToolkit (with the Rogue schema used for the complete modifier reference, as AC4 has some modifiers removed). Examples use the Standard Exporter format throughout, as it is available for Skeletons in all games and is easier to edit (the toolkit automatically recomputes bone indices and child counts on import). A comparison section covers the Schema-Based format differences.

A note on Path vs ID: As with all Anvil files, Path attributes are filled in by AnvilToolkit for human readability only. The engine locates everything by numeric ID.


Table of Contents


Core Concept: What is a Skeleton?

A Skeleton defines the bone hierarchy (rig) that drives a character, weapon, vehicle, or any animated object in the game. It contains:

  • A flat array of bones, each with position/rotation data and a parent reference that creates the hierarchy
  • A root bone pointer
  • BoneModifiers attached to individual bones that add runtime behavior (physics simulation, look-at constraints, hinge joints, etc.)
  • Optional metadata like IK data, body part mappings, and pose groups

Skeletons come in two main categories:

  • Full character skeletons (e.g., Generic_Unique_Male.Skeleton) — complete biped rigs with 70+ bones
  • Addon skeletons (e.g., Addon_Human_Weapon_HiddenBlade.Skeleton) — small rigs that attach to a character skeleton to add weapons, accessories, or additional physics bones

How Addon Skeletons Work

Addon skeletons are the engine's way of attaching additional bones to a character without modifying the base skeleton. Weapons, hair physics, outfit-specific bones, and head setups are all addon skeletons.

The Root Bone as Attachment Point

The key to understanding addon skeletons is the root bone. The addon skeleton's root bone must share the same name (BoneID hash) as a bone in the main skeleton. At runtime, the engine matches the root bone by name and attaches the addon's child bones under that bone in the main skeleton's hierarchy.

The root bone itself is not added — it's already on the main skeleton. Only its children (and their descendants) get merged in.

Example: Hidden Blade Addon

The Addon_Human_Weapon_HiddenBlade.Skeleton has this structure:

Spine2 (root bone)              ← Matches "Spine2" on the main skeleton
 ├── LeftClavicle
 │   └── LeftArm
 │       └── LeftForeArm
 │           └── A_Hidden_Blade      ← New bone (blade attachment point)
 └── RightClavicle
     └── RightArm
         └── RightForeArm
             └── A_Hidden_Blade2     ← New bone (blade attachment point)

When the engine loads this addon, it finds Spine2 on the main character skeleton and merges everything underneath it. The LeftClavicle, LeftArm, LeftForeArm bones already exist on the main skeleton — they act as a path to the new A_Hidden_Blade bones. The engine matches existing bones by name and only truly adds the bones that don't already exist (the blade attachment points).

Example: Sword Addon

The Addon_Human_Weapon_Sword_MainHand.Skeleton is simpler:

Spine (root bone)               ← Matches "Spine" on the main skeleton
 └── P_Sword_Tag                ← New bone (with HingeBoneModifiers for swing physics)
     └── P_LeftWaist_Tag        ← New bone (secondary position)

The root bone is Spine, so the sword's physics bones get attached under the character's spine. The HingeBoneModifiers on P_Sword_Tag then simulate the sword swaying at the character's hip.

Attachment Point Patterns

Different addon types attach at different points on the main skeleton:

Addon Type Root Bone Why
Swords, melee weapons Spine Weapons hang from the torso
Small weapons, pouches Hips Items attached to the belt/waist
Hidden blades, arm addons Spine2 Need to follow the arm chain from upper spine
Head, hair, facial bones Spine2 Head hierarchy branches from Spine2 via Neck
Full character addons Reference Complete override starting from the absolute root
Quad pistols, complex rigs Reference Need access to multiple body parts

How Existing Bones are Handled

When an addon skeleton contains bones with the same name as bones already on the main skeleton, the engine doesn't duplicate them — it matches them. This is how the addon "navigates" the existing hierarchy to reach the point where new bones need to be added.

For example, the hidden blade addon includes LeftClavicle → LeftArm → LeftForeArm not to create new arm bones, but to define the path from the root (Spine2) down to where A_Hidden_Blade should be attached. The existing arm bones are matched by their BoneID hash; only the truly new bones are added.

This also means an addon skeleton can add BoneModifiers to existing bones. If the addon's copy of LeftForeArm has a BallJointBoneModifier, that modifier gets applied to the main skeleton's LeftForeArm bone at runtime.

Critical: The hierarchy of matched bones in the addon skeleton must exactly match the hierarchy on the base skeleton. If the base skeleton has LeftClavicle → LeftArm → LeftForeArm, the addon must have the same parent-child chain. If you insert a new bone between LeftClavicle and LeftArm (e.g., parent LeftArm to a new intermediate bone instead of LeftClavicle), the hierarchies no longer match and the game will crash. New bones must be added as additional children, not inserted between existing bones in the chain.

Bone Count Limits

The engine has a hard limit on the total number of bones per character (counted after deduplicating matched bones from all addon skeletons):

Games Maximum Bones Why
Pre-Unity (AC1 through Rogue) 255 Bone index is stored as a single byte. Index 255 is reserved as an invalid/null bone marker.
Unity and later 65535 Bone index is stored as a 16-bit value.

Exceeding this limit will crash the game. Keep this in mind when stacking multiple addon skeletons on a single character — the head addon, body addon, weapon addons, and accessory addons all contribute to the total count after deduplication.

Creating Your Own Addon Skeleton

  1. Choose the attachment point — decide which bone on the main skeleton should be the root of your addon
  2. Include the path bones — if your new bones need to be deep in the hierarchy, include the intermediate bones (they'll be matched, not duplicated)
  3. Set the root bone — make the addon's RootBone pointer reference the attachment point bone
  4. Set SkeletonType to SKELETONTYPE_CUSTOM (8) for addon skeletons
  5. Add your new bones as children (directly or through the path bones)
  6. Reference the addon in a BuildTable — addon skeletons are loaded through the BuildTable system (typically in a BuildColumn with type Skeleton)

Anatomy of a Skeleton File

Here's a simplified annotated skeleton — a sword weapon addon with 3 bones and hinge physics:

<Skeleton ID="882909070">
    <!-- What kind of skeleton this is -->
    <Value Name="SkeletonType" Type="UInt32">8</Value>  <!-- 8 = SKELETONTYPE_CUSTOM -->

    <!-- ALL bones in a flat array (hierarchy defined by Parent links) -->
    <List Name="Bones">
        <!-- Bone 0: Root -->
        <Bone ID="882909071">
            <Value Name="Name" Type="UInt32" HashName="Spine">1393476043</Value>
            <ObjectPtr Name="Parent" />  <!-- No parent = root -->
            ...
        </Bone>

        <!-- Bone 1: Sword tag (with HingeBoneModifiers) -->
        <Bone ID="882909072">
            <Value Name="Name" Type="UInt32" HashName="P_Sword_Tag">3055624953</Value>
            <ObjectPtr Name="Parent">
                <Link>
                    <Value Name="Value" Type="UInt64">882909071</Value>  <!-- Parent = Spine -->
                </Link>
            </ObjectPtr>
            <List Name="BoneModifiers">
                <!-- Physics modifiers go here -->
            </List>
            ...
        </Bone>

        <!-- Bone 2: Secondary position bone -->
        <Bone ID="882909077">
            <Value Name="Name" Type="UInt32" HashName="P_LeftWaist_Tag">981686566</Value>
            ...
        </Bone>
    </List>

    <!-- Pointer to the root bone -->
    <ObjectPtr Name="RootBone">
        <Link>
            <Value Name="Value" Type="UInt64">882909071</Value>
        </Link>
    </ObjectPtr>

    <!-- Root bone's default transform -->
    <Vector4 Name="RootBoneDefaultLocalPosition">
        <X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
    </Vector4>
    <Quat Name="RootBoneDefaultLocalRotation">
        <X>0</X> <Y>0</Y> <Z>0</Z> <W>1</W>
    </Quat>

    <!-- Optional references (often null for addon skeletons) -->
    <Reference Name="BodyPartMapping">
        <FileReference Name="Value" IsGlobal="0" Path="0">0</FileReference>
    </Reference>
    <List Name="PoseGroups" />
    <BaseObjectPtr Name="IKData" />
    <ObjectPtr Name="COMData" />
    <Reference Name="LookAtDataSkeletonDefinitionDefault">
        <FileReference Name="Value" IsGlobal="0" Path="0">0</FileReference>
    </Reference>
    <Value Name="SkeletonKey" Type="UInt32">3480848966</Value>
</Skeleton>

Skeleton-Level Fields

Field Purpose
SkeletonType Enum identifying what kind of rig this is (see table below)
Bones Flat array of all bones. Order matters — the Index field on each bone corresponds to its position here.
RootBone Link pointer to the root bone in the Bones array
RootBoneDefaultLocalPosition/Rotation The root bone's default rest pose transform
BodyPartMapping Reference to a BodyPartMapping file (maps bones to body regions for hit detection, etc.)
PoseGroups Array of pose group definitions (for animation blending)
IKData Inverse Kinematics configuration
COMData Center of Mass data (used for physics/balance)
LookAtDataSkeletonDefinitionDefault Default look-at behavior configuration
SkeletonKey A unique hash identifying this skeleton configuration

Skeleton Types

Type Value Usage
SKELETONTYPE_BIPED 0 Standard male humanoid
SKELETONTYPE_BIPED_FEMALE 1 Female humanoid (different proportions)
SKELETONTYPE_QUADRUPED 2 Four-legged animals
SKELETONTYPE_CARRIAGEHITCH 3 Carriage attachment points
SKELETONTYPE_HITCH 4 Generic hitch/attachment points
SKELETONTYPE_WAGON 5 Wagon rigs
SKELETONTYPE_REINS 6 Rein/rope rigs
SKELETONTYPE_CANNON 7 Cannon rigs
SKELETONTYPE_CUSTOM 8 Custom/addon skeletons (weapons, accessories, etc.)

Bone Structure

Bone Hierarchy — Parent-Child Relationships

Although bones are stored in a flat array, each bone has a FatherBone link that points to its parent, creating a tree hierarchy. The root bone has FatherBone set to Null.

Here's how a typical character skeleton's first few bones form a hierarchy:

Reference (root)                    ← FatherBone = Null
 ├── Hips                           ← FatherBone = Reference
 │   ├── LeftUpLeg                  ← FatherBone = Hips
 │   │   └── LeftLeg               ← FatherBone = LeftUpLeg
 │   │       └── LeftFoot           ← FatherBone = LeftLeg
 │   ├── RightUpLeg                 ← FatherBone = Hips
 │   └── Spine                      ← FatherBone = Hips
 │       └── Spine1                 ← FatherBone = Spine
 │           └── Spine2             ← FatherBone = Spine1
 │               ├── Neck           ← FatherBone = Spine2
 │               │   └── Head       ← FatherBone = Neck
 │               ├── LeftClavicle   ← FatherBone = Spine2
 │               └── RightClavicle  ← FatherBone = Spine2

The hierarchy is defined purely through FatherBone links — the order in the flat array doesn't define parentage, though children are typically listed after their parents.

Bone Fields Explained

Here's a complete bone with all fields annotated:

<Bone ID="1091164576">
    <!-- Bone name as a hash — HashName shows the resolved name -->
    <Value Name="Name" Type="UInt32" HashName="Hips">3738240529</Value>

    <!-- Parent bone (Link to another bone's ID in this skeleton) -->
    <ObjectPtr Name="Parent">
        <Link>
            <Value Name="Value" Type="UInt64">1091164575</Value>
        </Link>
    </ObjectPtr>

    <!-- Mirror bone for symmetric animations (points to the opposite-side bone) -->
    <ObjectPtr Name="MirrorBone">
        <Link>
            <Value Name="Value" Type="UInt64">1091164576</Value>  <!-- Self = no mirror -->
        </Link>
    </ObjectPtr>

    <!-- World-space position and rotation (rest pose) -->
    <Vector4 Name="GlobalPosition">
        <X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
    </Vector4>
    <Quat Name="GlobalRotation">
        <X>-0.5</X> <Y>-0.5</Y> <Z>-0.5</Z> <W>0.5</W>
    </Quat>

    <!-- Position and rotation relative to parent bone (rest pose) -->
    <Vector4 Name="LocalPosition">
        <X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
    </Vector4>
    <Quat Name="LocalRotation">
        <X>-0.5</X> <Y>-0.5</Y> <Z>-0.5</Z> <W>0.5</W>
    </Quat>

    <!-- Priority for constraint solving (higher = solved later) -->
    <Value Name="SolvingPriority" Type="Byte">0</Value>

    <!-- How this bone mirrors for symmetric animations -->
    <Value Name="MirroringType" Type="Enum" EnumName="BoneMirroringType"
        ValueName="BONEMIRRORING_PELVIS_TO_BIPED">4</Value>

    <!-- Array of BoneModifiers (physics, constraints, etc.) -->
    <List Name="BoneModifiers" />

    <!-- Wrinkle map category (for facial/skin deformation) -->
    <Value Name="WrinkleCategory" Type="Enum" EnumName="WrinkleCategory"
        ValueName="None">7</Value>
    <Value Name="WrinkleFactor" Type="Single">0</Value>
</Bone>

Note: The Index and ChildBoneCount fields shown in the Schema-Based format are automatically computed by the toolkit in the Standard format, so they don't appear in the XML.

Field Purpose
BoneID Hash of the bone's name. The HashValue attribute shows the human-readable name (e.g., Hips, Spine, Head). Bones with unknown names show as hex (e.g., x2BA0BB9F).
FatherBone Link to the parent bone's ID. Null = this is the root bone.
MirrorBone Link to the bone on the opposite side of the body for mirrored animations. A bone pointing to itself means it has no mirror (center bones like Spine, Head).
GlobalPosition World-space position at rest pose (X, Y, Z, W). W is typically 0.
GlobalRotation World-space rotation at rest pose as a quaternion (X, Y, Z, W).
LocalPosition Position relative to parent bone at rest pose.
LocalRotation Rotation relative to parent bone at rest pose.
SolvingPriority Order in which constraint modifiers on this bone are evaluated. 0 = default.
MirroringType Defines how animation mirroring is applied at this bone (see below).
Modifiers Array of BoneModifier objects attached to this bone.
WrinkleCategory Which wrinkle map channel this bone drives (for facial animation).
WrinkleFactor Intensity multiplier for the wrinkle effect.
Index This bone's position in the flat Bones array (0-based).
ChildBoneCount Total number of descendant bones (children, grandchildren, etc.).

Mirroring Types

The MirroringType controls how the engine handles symmetric animations (e.g., walking, where left and right legs mirror each other):

Type Value Usage
BONEMIRRORING_NONE 0 No mirroring
BONEMIRRORING_WORLD_TO_WORLD 1 Mirror in world space
BONEMIRRORING_WORLD_TO_PELVIS 2 Mirror from world space to pelvis space (used on the Reference root bone)
BONEMIRRORING_PELVIS_TO_WORLD 3 Mirror from pelvis space to world space
BONEMIRRORING_PELVIS_TO_BIPED 4 Mirror from pelvis to biped space (used on Hips, Spine)
BONEMIRRORING_BIPED_TO_BIPED 5 Mirror between matching biped bones (used on limbs, head, etc.)

Wrinkle Categories

Used for facial animation to drive wrinkle/skin deformation maps:

Category Value Usage
Translation 0 Driven by bone translation
AxisMagnitude 1 Driven by rotation magnitude around an axis
RotationDifference_Expensive 2 Driven by rotation difference (costly)
RotationYaw 3 Driven by yaw rotation
RotationPitch 4 Driven by pitch rotation
RotationRoll 5 Driven by roll rotation
Debug 6 Debug mode
None 7 No wrinkle effect (default for most bones)

BoneModifiers — Adding Behavior to Bones

BoneModifiers are objects attached to individual bones that add runtime procedural behavior — physics simulation, constraints, look-at targeting, and more. They are evaluated every frame after animation playback, modifying the bone's final transform.

Common BoneModifier Fields

All BoneModifier types inherit these base fields:

<!-- In the Standard format, BlendWeight and UseModifiedBoneMatrix are attributes on the modifier element -->
<HingeBoneModifier ID="882909073" BlendWeight="1" UseModifiedBoneMatrix="false">
    <!-- The Owner link is implicit — the modifier is inside the bone's BoneModifiers list -->
    ...
</HingeBoneModifier>

In the Schema-Based format, these are separate child elements including an explicit Owner link. In the Standard format, BlendWeight and UseModifiedBoneMatrix are attributes on the modifier element, and the Owner is implicit from the parent bone.

Field Purpose
Owner Link back to the bone this modifier is attached to
BlendWeight How strongly the modifier affects the bone (0 = no effect, 1 = full effect). Useful for blending physics in/out.
UseModifiedBoneMatrix If True, uses the matrix output by previous modifiers as input. If False, uses the original animated matrix.

Physics Modifiers

These modifiers add physics-based procedural motion to bones — things that sway, dangle, or react to gravity and wind.

BallJointBoneModifier

Simulates a ball-and-socket joint — the bone can swing freely within a cone constraint. Used for dangling objects (small weapons, pouches, hair strands, cloth flaps).

<BallJointBoneModifier ID="22223737855" BlendWeight="1" UseModifiedBoneMatrix="false">
    <Value Name="Mass" Type="Single">2.5</Value>                    <!-- Mass of the simulated object -->
    <Value Name="CollisionRadius" Type="Single">0.04</Value>         <!-- Collision sphere radius -->
    <Value Name="ConstraintAngle" Type="Single">15</Value>           <!-- Maximum swing angle (degrees) -->
    <Value Name="ConstraintScaleY" Type="Single">1</Value>           <!-- Scale the constraint ellipse on Y -->
    <Value Name="ConstraintScaleZ" Type="Single">1</Value>           <!-- Scale the constraint ellipse on Z -->
    <Value Name="MechanicalFriction" Type="Single">1.5</Value>       <!-- Joint friction (resists motion) -->
    <Value Name="AirFriction" Type="Single">0.25</Value>             <!-- Air drag -->
    <Value Name="WindScale" Type="Single">0.35</Value>               <!-- How much wind affects this bone -->
    <Value Name="WindRandomFactor" Type="Single">1</Value>            <!-- Randomness added to wind effect -->
    <Value Name="GravityMode" Type="Enum" EnumName="GravityMode"
        ValueName="GlobalGravity">0</Value>                          <!-- Gravity source -->
    <Vector4 Name="OwnGravity">
        <X>0</X> <Y>0</Y> <Z>-9.81</Z> <W>0</W>                    <!-- Custom gravity vector -->
    </Vector4>
    <Value Name="LocalYawOft" Type="Single">7.5</Value>              <!-- Local yaw offset (degrees) -->
    <Value Name="LocalPitchOft" Type="Single">15</Value>             <!-- Local pitch offset (degrees) -->
    <Value Name="LocalRollOft" Type="Single">0</Value>               <!-- Local roll offset (degrees) -->
    <Reference Name="CommonData">
        <FileReference Name="Value" IsGlobal="0" Path="0">0</FileReference>  <!-- Shared config (null) -->
    </Reference>
</BallJointBoneModifier>
Field Purpose
Mass Heavier objects swing slower and are less affected by wind
CollisionRadius Size of the collision sphere for self-collision
ConstraintAngle Maximum angle (in degrees) the bone can swing from its rest pose
ConstraintScaleY/Z Make the constraint cone elliptical instead of circular (values < 1 restrict that axis)
MechanicalFriction How quickly motion decays due to joint resistance
AirFriction How quickly motion decays due to air drag
WindScale Multiplier for wind force on this bone
WindRandomFactor Adds random variation to wind (0 = steady, higher = gusty)
GravityMode GlobalGravity (0) = uses world gravity. LocalGravity (1) = uses OwnGravity vector.
OwnGravity Custom gravity vector (only used when GravityMode = LocalGravity)
LocalYawOft/PitchOft/RollOft Offset angles applied to the bone's rest orientation before simulation
CommonData Optional reference to a shared BallJointCommonData file for common parameters

HingeBoneModifier

Simulates a hinge joint — the bone swings on a single axis, like a door or a hanging sword. Can have multiple Hinge constraint sub-objects for different axes.

<HingeBoneModifier ID="882909073" BlendWeight="1" UseModifiedBoneMatrix="false">
    <Value Name="LagFactor" Type="Single">0.4</Value>             <!-- How much the bone lags behind motion -->
    <Value Name="WeightFactor" Type="Single">0.4</Value>           <!-- Weight influence on swing -->
    <Vector4 Name="Gravity">
        <X>0</X> <Y>0</Y> <Z>-65</Z> <W>0</W>                    <!-- Gravity force vector -->
    </Vector4>
    <ObjectPtr Name="GravityAssociatedBone" />                     <!-- Optional: inherit gravity from another bone -->
    <Value Name="WindScale" Type="Single">0.1</Value>              <!-- Wind influence -->
    <Value Name="AxisConstraintType" Type="Int32">0</Value>        <!-- 0=AxisX, 1=AxisY, 2=AxisZ -->
    <Value Name="AxisLookAtType" Type="Int32">2</Value>            <!-- Which axis to align toward -->

    <!-- Array of Hinge constraints (angle limits) -->
    <List Name="Constraints">
        <BaseObject>
            <Hinge ID="882909074">
                <ObjectPtr Name="ConstraintAssociatedBone" />
                <Value Name="MinAngle" Type="Single">-0.7853982</Value>      <!-- Min angle (radians) -->
                <Value Name="MaxAngle" Type="Single">0.7853982</Value>        <!-- Max angle (radians) -->
                <Value Name="DeltaMinDamping" Type="Single">0.7853982</Value> <!-- Damping near min -->
                <Value Name="DeltaMaxDamping" Type="Single">0</Value>         <!-- Damping near max -->
                <Vector4 Name="InitialGlobalPosition">
                    <X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
                </Vector4>
                <Quat Name="InitialGlobalRotation">
                    <X>0</X> <Y>0</Y> <Z>0</Z> <W>1</W>
                </Quat>
            </Hinge>
        </BaseObject>
    </List>

    <Vector4 Name="LocalAxisConstraint">
        <X>1</X> <Y>0</Y> <Z>0</Z> <W>0</W>                     <!-- Constraint axis in local space -->
    </Vector4>
</HingeBoneModifier>

Real-world usage: The sword addon skeleton (Addon_Human_Weapon_Sword_MainHand) uses two HingeBoneModifiers on the P_Sword_Tag bone — one for the X-axis swing (side-to-side sway, ±45°) and one for the Y-axis (forward tilt, 0° to ~10°). This creates realistic sword-on-hip motion.

Field Purpose
LagFactor How much the bone trails behind character movement (0 = no lag, 1 = maximum lag)
WeightFactor How much the simulated weight affects swing amplitude
Gravity Gravity force vector (note: much stronger than real gravity for visual effect)
GravityAssociatedBone Optional bone to inherit gravity direction from
AxisConstraintType Which axis the hinge rotates around (AxisX=0, AxisY=1, AxisZ=2)
AxisLookAtType Which axis points along the bone's length
Constraints Array of Hinge objects defining angle limits
LocalAxisConstraint The constraint axis expressed in local bone space

Hinge sub-object fields:

Field Purpose
MinAngle / MaxAngle Angle limits in radians (e.g., 0.7854 ≈ 45°, 0.1745 ≈ 10°)
DeltaMinDamping Damping force applied when approaching the minimum angle
DeltaMaxDamping Damping force applied when approaching the maximum angle
ConstraintAssociatedBone Optional bone that this constraint is relative to

HingeVectorBoneModifier

A more advanced hinge variant that uses target bones to define the hinge direction dynamically, rather than a fixed axis. Includes distance-based ramp-down for the effect.

Field Purpose
HingeLocalOffsetPosition/Rotation Local offset from the bone's origin for the hinge pivot
EffectRampDownMinAngle/MaxAngle Angle range where the effect ramps down
EffectRampDownMinDistance/MaxDistance Distance range where the effect ramps down
HingeTargets Array of target bones that define the hinge direction

PendulumBoneModifier

Simulates a pendulum — the bone swings freely under gravity like a weight on a string. Simpler than BallJoint but with different physics characteristics.

Field Purpose
GravityAxis Which space to apply gravity in (GlobalGravity=0, LocalGravity=1)
GravityScale Gravity strength multiplier
Mass Mass of the pendulum weight
Friction Mechanical friction at the pivot
AirFriction Air drag on the pendulum
Length Length of the pendulum arm
FakeCentrifugalForce Simulated centrifugal force for fast rotations

SpringBoxModifier

Adds spring-based physics to a bone — the bone oscillates around its rest position with spring dynamics. Used for bouncy or springy elements.


Constraint Modifiers

These modifiers constrain a bone's transform based on other bones, keeping things aligned or synchronized.

PositionConstraintBoneModifier

Constrains a bone's position to be a weighted blend of target bone positions. Inherits from ConstraintBoneModifier.

<!-- The base ConstraintBoneModifier provides: -->
<DynamicSmallArray Name="Targets" Type="ObjectPtr">
    <!-- Link to target bone 1 -->
    <!-- Link to target bone 2 -->
</DynamicSmallArray>
<DynamicSmallArray Name="KeepInitialOffset" Type="Bool">
    <Bool>False</Bool>
    <Bool>False</Bool>
</DynamicSmallArray>

The position constraint adds no extra fields beyond the base — it simply constrains position using the target bones and their weights.

Real-world usage: In the quad pistols skeleton, position constraints keep the secondary pistol holster bones aligned between two reference points.

OrientationConstraintBoneModifier

Constrains a bone's rotation to be a weighted blend of target bone rotations. Inherits from ConstraintBoneModifier.

<!-- Adds to ConstraintBoneModifier: -->
<Quaternion Name="Offset">( 0, 0, 0, 1 )</Quaternion>  <!-- Rotation offset applied after blending -->
Field Purpose
Offset A quaternion offset applied after the constraint blending

Often used together with PositionConstraintBoneModifier on the same bone to constrain both position and orientation.

RotationConstraintModifier

A more advanced rotation constraint with inertia and damping — the constrained rotation has physical properties rather than being an instant snap.

Field Purpose
Inertia How much the constraint resists changes in rotation
Damping How quickly oscillations die out
Gravity Gravity vector affecting the constraint
ConstraintAssociatedBone The reference bone for the constraint
Constraints Array of constraint definitions

SplineConstraintModifier

Constrains a bone to follow a spline curve defined by other bones. Used for chain-like structures (ropes, belts, hair strands).


Look-At Modifiers

These modifiers make a bone automatically orient toward a target — essential for head tracking, eye movement, and IK-like setups.

MaxLookAtBoneModifier

The most commonly used look-at modifier. Makes a bone aim one of its axes toward a target bone, with a configurable up-vector.

<MaxLookAtBoneModifier ID="4789178997" BlendWeight="1" UseModifiedBoneMatrix="false">
    <!-- The bone that defines the "up" direction -->
    <ObjectPtr Name="UpNode">
        <Link>
            <Value Name="Value" Type="UInt64">1091164576</Value>  <!-- e.g., Hips -->
        </Link>
    </ObjectPtr>

    <!-- The bone to look at -->
    <ObjectPtr Name="LookAtBone">
        <Link>
            <Value Name="Value" Type="UInt64">1091164580</Value>  <!-- e.g., LeftLeg -->
        </Link>
    </ObjectPtr>

    <!-- Position offset from the look-at target -->
    <Vector4 Name="LookAtPosOffset">
        <X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
    </Vector4>

    <!-- Which axis of THIS bone points toward the target -->
    <Value Name="LookAtUsedAxis" Type="Enum" EnumName="MaxLookAtBoneAxis"
        ValueName="AXIS_X">0</Value>

    <!-- Which axis of THIS bone aligns with the up direction -->
    <Value Name="UpUsedAxis" Type="Enum" EnumName="MaxLookAtBoneAxis"
        ValueName="AXIS_Z">2</Value>

    <!-- How the up-vector is controlled -->
    <Value Name="UpNodeControl" Type="Enum" EnumName="MaxLookAtUpNodeControl"
        ValueName="AXIS_ALIGNMENT">1</Value>

    <!-- Source axis for alignment calculation -->
    <Value Name="SourceAxis" Type="Enum" EnumName="MaxLookAtBoneAxis"
        ValueName="AXIS_MINUS_Z">5</Value>
</MaxLookAtBoneModifier>
Field Purpose
UpNode Link to the bone that defines "up" for this look-at
LookAtBone Link to the target bone to aim toward
LookAtPosOffset Offset from the target bone's position
LookAtUsedAxis Which axis of the modified bone points at the target (AXIS_X=0, AXIS_Y=1, AXIS_Z=2, AXIS_MINUS_X=3, AXIS_MINUS_Y=4, AXIS_MINUS_Z=5)
UpUsedAxis Which axis of the modified bone aligns with the up direction
UpNodeControl How the up node is used: WORLD_ALIGNMENT=0, AXIS_ALIGNMENT=1, LOOK_AT_ALIGNMENT=2
SourceAxis The axis used for the alignment calculation source

Real-world usage: The Generic_Unique_Male skeleton uses MaxLookAtBoneModifiers extensively on the leg IK helper bones — each one aims toward the next bone in the leg chain, creating a smooth joint alignment system. The head/neck setup (Addon_CHR_P_Edward_Naked_NeckSetup) uses 5 MaxLookAtBoneModifiers to create a multi-joint head-turning system.

AnvilLookAtBoneModifier

A variant of the look-at modifier with angle clamping — the bone looks at a target but with min/max angle limits and clamped rotation axis support.

Field Purpose
LookAtBone Target bone to aim toward
LookAtPosOffset Position offset from target
MinAngle / MaxAngle Angle limits for the look-at rotation
LookAtUsedAxis Which axis aims at the target
ParentUpAxis Up axis derived from the parent bone
ClampedRotationAxis Which rotation axis to clamp
ClampedRotationReferenceVector Reference vector for the clamped rotation

Utility Modifiers

FollowBoneModifier

Makes a bone copy the transform of another bone. Simple parent-follow behavior.

Field Purpose
AssociatedBone The bone to follow/copy

RollBoneModifier

Adds roll rotation to a bone based on another bone's rotation. Used for forearm twist distribution (ulna/radius roll).

Field Purpose
RollBoneType Type of roll behavior
RollFactor How much of the source rotation to apply (0 to 1)
SourceBone The bone to read rotation from

CompressBoneModifier

Squashes/stretches a bone based on the positions of two reference bones. Used for volume preservation in deformations.

Field Purpose
PositionInfluence1 How much position affects the compression
RotationInfluence1 How much rotation affects the compression
OffsetBone1 / OffsetBone2 The two reference bones
RotOffset1 / RotOffset2 Rotation offsets for each reference

StretchBoneModifier

Scales a bone along its length to stretch toward a target. Used for stretchy IK or rubber-band effects.

RotationExpressionModifier

Drives bone rotation via a mathematical expression based on another bone's rotation. Used for mechanical linkages or complex driven-key setups.

Field Purpose
BoneModifierUpdateMode When to evaluate the expression
LinkedBone Source bone to read rotation from
InitialOffset Starting offset value
AxisConstraint Which axis to constrain the expression to
GravityBone Optional gravity reference bone

RotationPasteModifier

Directly copies rotation from one bone to another. Simpler than RotationExpressionModifier — pure copy with no expression evaluation.

Field Purpose
LinkedBone The bone whose rotation to copy

FXBoneModifier

A marker modifier for effects bones (particle emitters, blood effects, etc.). Has no configurable properties — its presence tells the engine this bone drives visual effects.


BoneModifier History Across Games

BoneModifiers have evolved across the series. The core bone structure (hierarchy, transforms, mirroring) is the same in all games, but the available modifiers and the physics systems that power them have changed.

Pre-AC3 (AC1, AC2, Brotherhood, Revelations)

The original Anvil engine had the following set of modifiers:

  • CompressBoneModifier — volume preservation
  • FXBoneModifier — effects bone marker
  • FollowBoneModifier — copy another bone's transform
  • HingeBoneModifier — single-axis swing physics
  • LookAtBoneModifier — look-at targeting
  • PendulumBoneModifier — pendulum physics
  • RollBoneModifier — twist distribution
  • StretchBoneModifier — stretch toward target

BallJointBoneModifier and MaxLookAtBoneModifier did not exist yet.

AC3

AC3 significantly expanded the modifier set:

  • BallJointBoneModifier — new ball-and-socket physics (replaced HingeBoneModifier as the go-to physics modifier)
  • MaxLookAtBoneModifier — new look-at system with more control
  • The original LookAtBoneModifier was renamed to AnvilLookAtBoneModifier (it still works the same way)
  • New modifiers added: PositionConstraintBoneModifier, OrientationConstraintBoneModifier, RotationConstraintModifier, RotationExpressionModifier, RotationPasteModifier, HingeVectorBoneModifier, SplineConstraintModifier, SpringBoxModifier

AC4: Black Flag

AC4 has the same modifier system as AC3 but with some modifiers removed from the schema. The examples in this tutorial are from AC4, but the Rogue schema is used for the complete modifier reference.

AC Rogue through AC Syndicate

These titles have the complete set of all BoneModifier types listed in this tutorial. This is the golden era for BoneModifiers — the system is at its most feature-complete.

AC Unity

Unity introduced the early stages of Reflex — a new physics/simulation system that would eventually replace BoneModifiers. In Unity, Reflex-related data started appearing in skeleton files alongside BoneModifiers. Both systems are functional.

AC Origins

Origins is the transitional game. It is the first title that stopped actively using BoneModifiers in new content, preferring Reflex3 instead. However, BoneModifiers are still supported by the engine — existing modifier data still works.

Origins also added Reflex3Constraints — a new array at the end of the skeleton file that defines Reflex3 physics constraints. This is separate from and replaces the per-bone BoneModifier system for physics simulation.

AC Odyssey and AC Valhalla

Reflex3 is the primary system. BoneModifiers exist in the schema but are increasingly vestigial. Odyssey also added face builder related data to skeleton files.

AC Mirage

Mirage still has BoneModifier definitions in its files, but they no longer function — the engine has fully transitioned to Reflex3. BoneModifier data may be present in skeleton files but has no runtime effect.

Summary Table

Game LookAtBoneModifier AnvilLookAtBoneModifier MaxLookAtBoneModifier BallJointBoneModifier Pre-AC3 Modifiers Full Modifier Set Reflex3 Constraints
AC1 / AC2 / Brotherhood / Revelations Yes -- -- -- Yes -- --
AC3 Renamed Yes Yes Yes Yes Full --
AC4: Black Flag -- Yes Yes Yes Yes Partial --
AC Rogue -- Yes Yes Yes Yes Full --
AC Unity -- Yes Yes Yes Yes Full Early Reflex
AC Syndicate -- Yes Yes Yes Yes Full Early Reflex
AC Origins -- Yes Yes Yes Yes Supported Reflex3
AC Odyssey / Valhalla -- Yes Yes Yes Yes Supported Reflex3
AC Mirage -- Present Present Present Present Non-functional Reflex3

For modders: If you're working on AC3 through Syndicate, BoneModifiers are the way to add procedural physics and constraints. For Origins and later, BoneModifiers still technically work but new content should use Reflex3. For Mirage, BoneModifiers have no effect.


Practical: How to Add a New Bone

To add a bone to an existing skeleton:

Step 1: Create the Bone Entry

Add a new <Bone> element inside the <List Name="Bones"> block:

<Bone ID="YOUR_UNIQUE_ID">
    <Value Name="Name" Type="UInt32" HashName="YourBoneName">YOUR_BONE_HASH</Value>

    <!-- Link to the parent bone -->
    <ObjectPtr Name="Parent">
        <Link>
            <Value Name="Value" Type="UInt64">PARENT_BONE_ID</Value>
        </Link>
    </ObjectPtr>

    <!-- Mirror bone (point to self if no mirror, or to the opposite-side bone) -->
    <ObjectPtr Name="MirrorBone">
        <Link>
            <Value Name="Value" Type="UInt64">YOUR_UNIQUE_ID</Value>
        </Link>
    </ObjectPtr>

    <!-- Transforms — set these to position the bone correctly -->
    <Vector4 Name="GlobalPosition">
        <X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
    </Vector4>
    <Quat Name="GlobalRotation">
        <X>0</X> <Y>0</Y> <Z>0</Z> <W>1</W>
    </Quat>
    <Vector4 Name="LocalPosition">
        <X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
    </Vector4>
    <Quat Name="LocalRotation">
        <X>0</X> <Y>0</Y> <Z>0</Z> <W>1</W>
    </Quat>

    <Value Name="SolvingPriority" Type="Byte">0</Value>
    <Value Name="MirroringType" Type="Enum" EnumName="BoneMirroringType"
        ValueName="BONEMIRRORING_BIPED_TO_BIPED">5</Value>

    <!-- Add modifiers here if needed, or leave empty -->
    <List Name="BoneModifiers" />

    <Value Name="WrinkleCategory" Type="Enum" EnumName="WrinkleCategory"
        ValueName="None">7</Value>
    <Value Name="WrinkleFactor" Type="Single">0</Value>
</Bone>

Step 2: (Automatic) Index and ChildBoneCount

When using the Standard Exporter format, the toolkit automatically recomputes the Index and ChildBoneCount values on import. You don't need to worry about these — just add the bone in the right position and the toolkit handles the bookkeeping.

Note: If using the Schema-Based format, you must manually update Index on all subsequent bones and increment ChildBoneCount on the parent bone.

Step 3: (Optional) Add BoneModifiers

Add modifiers inside the bone's <List Name="BoneModifiers"> as needed.


Practical: How to Remove a Bone

  1. Remove the bone from the <List Name="Bones"> block
  2. Re-parent any children of the removed bone to the removed bone's parent (update their <ObjectPtr Name="Parent"> links)
  3. Update any MirrorBone links that pointed to the removed bone
  4. Update the RootBone pointer if you removed the root bone (point it to the new root)
  5. Remove any BoneModifier references in other bones that pointed to the removed bone (e.g., LookAtBone, UpNode, AssociatedBone links)

The toolkit automatically recomputes Index and ChildBoneCount on import, so you don't need to update those manually.

Warning: Removing bones that are referenced by animations, meshes, or other skeletons will cause issues. Only remove bones that you're certain are not needed.


Standard Exporter vs Schema-Based Exporter

Both formats are available for Skeletons in all AC games. This tutorial uses the Standard format in all examples. Here's how the same data looks in the Schema-Based format, for reference.

The Standard format is recommended for skeleton editing because the toolkit automatically recomputes Index and ChildBoneCount on import, and uses more readable element names.

Side-by-Side: Bone Definition

Standard (used in this tutorial):

<Bone ID="1091164576">
    <Value Name="Name" Type="UInt32" HashName="Hips">3738240529</Value>
    <ObjectPtr Name="Parent">
        <Link>
            <Value Name="Value" Type="UInt64">1091164575</Value>
        </Link>
    </ObjectPtr>
    <Vector4 Name="GlobalPosition">
        <X>0</X> <Y>0</Y> <Z>0</Z> <W>0</W>
    </Vector4>
    <Quat Name="GlobalRotation">
        <X>-0.5</X> <Y>-0.5</Y> <Z>-0.5</Z> <W>0.5</W>
    </Quat>
    <Value Name="MirroringType" Type="Enum" EnumName="BoneMirroringType"
        ValueName="BONEMIRRORING_PELVIS_TO_BIPED">4</Value>
    <List Name="BoneModifiers" />
</Bone>

Schema-Based equivalent:

<ObjectPtr ID="1091164576" Type="Bone" PointerType="Inlined">
    <Hash32 Name="BoneID" HashValue="Hips">3738240529</Hash32>
    <ObjectPtr Name="FatherBone" PointerType="Link">
        <Link Name="FatherBone" Path="1091164575">1091164575</Link>
    </ObjectPtr>
    <Vector4 Name="GlobalPosition">( 0, 0, 0, 0 )</Vector4>
    <Quaternion Name="GlobalRotation">( -0.5, -0.5, -0.5, 0.5 )</Quaternion>
    <Enum Name="MirroringType" Type="BoneMirroringType"
        Value="BONEMIRRORING_PELVIS_TO_BIPED">4</Enum>
    <DynamicSmallArray Name="Modifiers" Type="ObjectPtr" />
    <Unsigned8 Name="Index">1</Unsigned8>
    <Unsigned8 Name="ChildBoneCount">12</Unsigned8>
</ObjectPtr>

Side-by-Side: BoneModifier

Standard (used in this tutorial):

<HingeBoneModifier ID="882909073" BlendWeight="1" UseModifiedBoneMatrix="false">
    <Value Name="LagFactor" Type="Single">0.4</Value>
    <Value Name="AxisConstraintType" Type="Int32">0</Value>
    ...
</HingeBoneModifier>

Schema-Based equivalent:

<ObjectPtr ID="882909073" Type="HingeBoneModifier" PointerType="Inlined">
    <ObjectPtr Name="Owner" PointerType="Link">
        <Link Name="Owner" Path="882909072">882909072</Link>
    </ObjectPtr>
    <Float Name="BlendWeight">1</Float>
    <Bool Name="UseModifiedBoneMatrix">False</Bool>
    <Float Name="LagFactor">0.4</Float>
    <Enum Name="AxisConstraintType" Type="BoneModifier::ModifierAxis" Value="AxisX">0</Enum>
    ...
</ObjectPtr>

Note: In Schema-Based format, BlendWeight, UseModifiedBoneMatrix, and Owner are child elements. In Standard format, the first two are attributes on the modifier element and Owner is implicit.

Field Name Mapping

Concept Schema-Based Standard
Root element <Object Type="Skeleton"> <Skeleton>
Bone element <ObjectPtr Type="Bone"> <Bone>
Bone name <Hash32 Name="BoneID" HashValue="..."> <Value Name="Name" HashName="...">
Parent bone <ObjectPtr Name="FatherBone"> <ObjectPtr Name="Parent">
Modifiers array <DynamicSmallArray Name="Modifiers"> <List Name="BoneModifiers">
Modifier element <ObjectPtr Type="HingeBoneModifier"> <HingeBoneModifier>
BlendWeight/UseModified Child elements Attributes on modifier element
Rotation <Quaternion Name="...">( X, Y, Z, W )</Quaternion> <Quat Name="..."><X>...</X>...</Quat>
Vector <Vector4 Name="...">( X, Y, Z, W )</Vector4> <Vector4 Name="..."><X>...</X>...</Vector4>
Skeleton type <Enum Name="SkeletonType" Value="..."> <Value Name="SkeletonType" Type="UInt32">
Owner (modifier) <ObjectPtr Name="Owner"> (explicit link) Implicit from parent bone
IK Data <ObjectPtr Name="IKData"> <BaseObjectPtr Name="IKData">
Index / ChildBoneCount Manual (<Unsigned8> elements) Automatic (computed by toolkit on import)
Encoding UTF-8, type="1" UTF-16, type="0"

Quick Reference Summary

Concept What It Does
Skeleton Defines a bone hierarchy (rig) for an animated object. Contains a flat array of bones and metadata.
Bone A single joint in the hierarchy. Has position/rotation, a parent link, and optional BoneModifiers.
Parent / FatherBone Link to the parent bone. Creates the tree hierarchy from the flat array. Null = root. (Parent in Standard, FatherBone in Schema-Based.)
MirrorBone Link to the opposite-side bone for symmetric animations. Self-link = no mirror.
BoneID Hash of the bone's name. The engine identifies bones by this hash, not by array position.
SkeletonType What kind of rig: Biped, Biped Female, Quadruped, Custom (addon), etc.
BoneModifier Procedural behavior attached to a bone. Evaluated every frame after animation.
BallJointBoneModifier Ball-and-socket physics (dangling objects, hair, pouches).
HingeBoneModifier Single-axis swing physics (swords, hanging objects). Uses Hinge sub-objects for angle limits.
PendulumBoneModifier Pendulum physics (simple gravity-driven swing).
MaxLookAtBoneModifier Aims a bone axis toward a target bone (head tracking, IK helpers).
AnvilLookAtBoneModifier Look-at with angle clamping limits.
PositionConstraintBoneModifier Constrains position to a blend of target bone positions.
OrientationConstraintBoneModifier Constrains rotation to a blend of target bone rotations.
FollowBoneModifier Copies another bone's transform directly.
RollBoneModifier Distributes twist rotation from a source bone (forearm roll).
CompressBoneModifier Squash/stretch based on reference bone positions.
RotationExpressionModifier Drives rotation via mathematical expression from a source bone.
RotationPasteModifier Directly copies rotation from another bone.
FXBoneModifier Marker for effects bones (particles, blood). No properties.
BlendWeight 0–1 multiplier on any modifier's effect.
SolvingPriority Order for constraint evaluation (higher = later).
ChildBoneCount Total number of descendants below this bone in the hierarchy.
⚠️ **GitHub.com Fallback** ⚠️