Problem
A PMX skeleton rests at identity rotation with its arms hanging 37–42° below horizontal. A VRM or a T-pose BVH rests at identity with level arms. RestPoseCorrection compares rest rotations only, so it treats both rests as the same pose. The upper arms therefore land about 40° off, in both directions:
- Onto a PMX target. A level-arm clip (VRMA,
t-pose BVH) leaves a PMX target's arms at its A-pose: the median error is 40°, and it reaches 42°. This happens with motionRetarget v0.5.0 today, whatever usd-mmd-plugins does.
- From a PMX source. An MMD clip onto a level-arm rig raises the arms by the same angle.
usd-mmd-plugins measured this on 17 local characters and two dance motions, retargeting with motionRetarget v0.5.0 against MMD's own evaluated segment directions (animu-sphere/usd-mmd-plugins#24, report docs/reports/2026-09-25-phase9-rest-pose-comparison.md):
| Upper arm, median / max |
Identity rests (today) |
Arm-chain rest stated on the source |
Stated on source and target |
| MMD → level-arm rig |
40.16° / 42.42° |
0.00° / 0.04° |
— |
| PMX → PMX |
2.45° / 5.26° |
40.16° / 42.42° |
0.00° / 0.04° |
| level-arm clip → PMX |
40.16° / 42.42° |
— |
0.00° / 0.04° |
The source half already exists: SourceRestPose can state an A-pose rest aimed onto the T-pose directions, and it makes MMD → level-arm exact. The same rest stated on the source alone, though, makes PMX → PMX as wrong as the other two cases. A PMX target has to state it too. Today a target's rest is SkeletonJoint::restRotation, which must equal /Asset/skel/Skeleton's rest transforms (identity). There is nowhere to put a humanoid reference rest that differs from the UsdSkel rest.
Putting the aim into restRotation in the caller does work in a scratch run, but it is not an answer:
- the descriptor no longer matches what
BuildSkeletonDescriptor reads off the stage, so every consumer reading the stage (motionUsd, exec nodes, the avatar runtime) gets a different descriptor;
PoseRetargeter writes a mapped joint that the clip does not drive at its restRotation, so a clip that leaves the arms out would raise the PMX's arms to level.
Proposal
Two small additions to motionRetarget. usd-mmd-plugins would consume both through mmdSkeletonAdapter.
- A target reference rest, separate from the bind/UsdSkel rest. For example, an optional per-bone world reference rotation on the retarget side (on
RetargetMap, in RetargetOptions, or in a TargetRestPose mirroring SourceRestPose). ComputeRestPoseCorrection would use it for T/Tp in place of restRotation. Joints the clip does not drive would still take restRotation, unchanged. When no reference rest is given, behaviour stays exactly as today.
- The T-pose directions as public vocabulary.
TPoseDirection and the shortest-rotation aim currently live privately in motionSource's canonical conversion. usd-mmd-plugins may not copy them (its WORKSPACE invariant 9). A public helper would let a format repository build a stated rest for the bones it names without owning the directions. It could be the direction table, or "aim these bones of this skeleton onto the T-pose".
The bone selection stays in the format repository. For MMD it is the arm chain only (shoulder, upper arm, lower arm, hand; fingers inherit). The same aim on the whole body is wrong for MMD: the chest flips where 上半身3 lies below 上半身2, and it levels feet that a humanoid rest leaves sloped.
Alternatives and context
- RT-O4 (whether
SkeletonDescriptor carries bind transforms separately from rest) is the nearest open question. This is its first consumer, though what the consumer needs is a humanoid reference rest rather than a bind transform.
- A retarget option that simply offsets the arms by a fixed angle was rejected. The angle is per model (37–42° measured), and it has to be symmetric across source and target.
- Until this exists, usd-mmd-plugins keeps identity rests (MOT-O10). PMX → PMX stays at a median 2.5°, and the 40° from a level-arm source to a PMX target remains.
Checklist
Problem
A PMX skeleton rests at identity rotation with its arms hanging 37–42° below horizontal. A VRM or a T-pose BVH rests at identity with level arms.
RestPoseCorrectioncompares rest rotations only, so it treats both rests as the same pose. The upper arms therefore land about 40° off, in both directions:t-poseBVH) leaves a PMX target's arms at its A-pose: the median error is 40°, and it reaches 42°. This happens withmotionRetargetv0.5.0 today, whatever usd-mmd-plugins does.usd-mmd-plugins measured this on 17 local characters and two dance motions, retargeting with
motionRetargetv0.5.0 against MMD's own evaluated segment directions (animu-sphere/usd-mmd-plugins#24, reportdocs/reports/2026-09-25-phase9-rest-pose-comparison.md):The source half already exists:
SourceRestPosecan state an A-pose rest aimed onto the T-pose directions, and it makes MMD → level-arm exact. The same rest stated on the source alone, though, makes PMX → PMX as wrong as the other two cases. A PMX target has to state it too. Today a target's rest isSkeletonJoint::restRotation, which must equal/Asset/skel/Skeleton's rest transforms (identity). There is nowhere to put a humanoid reference rest that differs from the UsdSkel rest.Putting the aim into
restRotationin the caller does work in a scratch run, but it is not an answer:BuildSkeletonDescriptorreads off the stage, so every consumer reading the stage (motionUsd, exec nodes, the avatar runtime) gets a different descriptor;PoseRetargeterwrites a mapped joint that the clip does not drive at itsrestRotation, so a clip that leaves the arms out would raise the PMX's arms to level.Proposal
Two small additions to
motionRetarget. usd-mmd-plugins would consume both throughmmdSkeletonAdapter.RetargetMap, inRetargetOptions, or in aTargetRestPosemirroringSourceRestPose).ComputeRestPoseCorrectionwould use it forT/Tpin place ofrestRotation. Joints the clip does not drive would still takerestRotation, unchanged. When no reference rest is given, behaviour stays exactly as today.TPoseDirectionand the shortest-rotation aim currently live privately inmotionSource's canonical conversion. usd-mmd-plugins may not copy them (its WORKSPACE invariant 9). A public helper would let a format repository build a stated rest for the bones it names without owning the directions. It could be the direction table, or "aim these bones of this skeleton onto the T-pose".The bone selection stays in the format repository. For MMD it is the arm chain only (shoulder, upper arm, lower arm, hand; fingers inherit). The same aim on the whole body is wrong for MMD: the chest flips where
上半身3lies below上半身2, and it levels feet that a humanoid rest leaves sloped.Alternatives and context
SkeletonDescriptorcarries bind transforms separately from rest) is the nearest open question. This is its first consumer, though what the consumer needs is a humanoid reference rest rather than a bind transform.Checklist