Skip to content

[Feature] Physics pose: a seam for external pose sources - #55

Merged
miogds merged 2 commits into
developfrom
feature/physics_pose
Sep 22, 2026
Merged

miogds merged 2 commits into
developfrom
feature/physics_pose

Conversation

@miogds

@miogds miogds commented Sep 22, 2026

Copy link
Copy Markdown
Member

Summary

A public seam so an external pose source (a physics plugin, a game) can read an entity's skeleton and hand a pose back that the animation pipeline blends in per joint. The first consumer is the CoolZombie demo's ragdoll on the Jolt plugin: knockdowns and hit reactions, with the mesh following the simulated parts.

  • SkeletonJointInfo + getSkeletonJointInfo(entityId:): joint paths (parents before children), parent indices, the model-space bind pose. What a rig builder needs to put physics parts on the joints.
  • getJointModelTransforms(entityId:): this frame's displayed model-space joint transforms (the bind pose before the first update). World = the entity's world transform × model.
  • setPhysicsPose(entityId:jointModelTransforms:jointWeights:) / clearPhysicsPose(entityId:): one model-space transform and one weight per joint, applied every animation update after foot IK and before the world pose is composed, until cleared. A weighted joint takes the physics rotation, slerped by its weight, in its parent's blended frame; it also takes the physics translation when it heads a physics-driven subtree (no parent, or a parent at weight 0), while joints below keep their animated bone offsets; joints at weight 0 keep the animated pose under whatever moves their parent. Wrong array counts are ignored with a logged error.

The stage writes into the local pose, so the transition history stays valid and a clip change out of a ragdoll pose inertializes for free. A paused entity skips the stage like every other. Motion matching, reach IK and foot IK are untouched.

Tests

AnimationPhysicsPoseTests (11) on a three-joint chain: full weight on a leaf, half-weight rotation, a weight-0 child under a driven parent, the translation rule at the top of a subtree and below it, clear restores the animated pose, pause defers the pose, wrong counts ignored, weight clamping, joint info and model transforms consistent with each other and with the bind pose, nil readers without a skeleton. The animation suite (114) stays green.

Docs

docs/API/UsingPhysicsPose.md (new, in the mkdocs nav after Root Motion) and the pipeline description in docs/Architecture/animationPoseLayer.md.

Javier Segura added 2 commits September 22, 2026 10:38
A game or a physics plugin can now read an entity's skeleton (joint paths,
parents, the model-space bind pose) and this frame's displayed joint
transforms, and hand a pose back: one model-space transform and one weight
per joint, applied every animation update after foot IK and before the
world pose is composed, until cleared. A weighted joint takes the physics
rotation, slerped by its weight, in its parent's blended frame; it also
takes the physics translation when it heads a physics-driven subtree (no
parent, or a parent at weight 0), while joints below keep their animated
bone offsets, and joints at weight 0 keep the animated pose under whatever
moves their parent. Writing into the local pose keeps the transition
history valid, so a clip change out of a ragdoll pose inertializes for
free. A paused entity skips the stage like every other. Eleven tests on a
three-joint chain; a UsingPhysicsPose guide and the pipeline description
updated.
… blend

A plugin driving its bodies toward the animation read the displayed pose,
which already carried the bodies' result: at full weight the motors aimed
at where the bodies were and stopped pulling. getAnimatedJointModelTransforms
returns the pose the update composed before the physics blend (equal to
the displayed pose while none is active), captured only when a physics
pose is set.

The blend composed translations without rest scale while the pose a
plugin reads includes it, so a rig with a scaled root did not round-trip.
The walk now accumulates the rest scale of the ancestors and applies it
to translations both ways: exact for uniform scales, per-axis otherwise.

Docs note that a clip switch while a physics pose is active inertializes
from the blended pose (what a get-up wants) and that a cut is halflife 0.
@miogds
miogds merged commit 740b5fd into develop Sep 22, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant