Skip to content

Retarget onto a target whose humanoid reference rest differs from its UsdSkel rest (A-pose PMX) #32

Description

@snkmcb

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.

  1. 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.
  2. 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

  • I searched existing issues and pull requests.
  • This request is not a security report.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions