Skip to content

Commit 05e815b

Browse files
Merge pull request #27 from posecode-dev/fix/reach-before-floor-clamp
fix: run reach-IK before the floor safety clamp (levitating cobra)
2 parents d734877 + 2653896 commit 05e815b

1 file changed

Lines changed: 13 additions & 2 deletions

File tree

packages/posecode-render/src/index.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,12 @@ export function createViewer(
346346
effector: THREE.Object3D,
347347
): THREE.Vector3 | null {
348348
if (target === "floor") {
349+
// Rest the effector's MESH on the floor, not its bone origin: the hand
350+
// mesh extends below the wrist bone, so a bone target of y=0 would sink
351+
// the palm and force the floor safety clamp to lift the whole body.
349352
const p = effector.getWorldPosition(new THREE.Vector3());
350-
p.y = 0;
353+
const box = new THREE.Box3().setFromObject(effector);
354+
p.y = Number.isFinite(box.min.y) ? Math.max(0, p.y - box.min.y) : 0;
351355
return p;
352356
}
353357
const anchor = propAnchors.get(target);
@@ -458,6 +462,14 @@ export function createViewer(
458462
depenetrate(mannequin);
459463
applyGroundLockTo(mannequin, info.groundLock, frameAnchors(info.rootYaw, info.rootOffset));
460464
applyPins(info.pins);
465+
// Reach-IK BEFORE the floor safety clamp. When authored FK pushes a
466+
// reaching limb through the floor (cobra: prone + shoulders flex 50),
467+
// the limb must bend to meet the floor. Running reaches after the clamp
468+
// let the clamp "solve" the penetration first by hoisting the whole
469+
// rigid body into the air — legs floating, the classic levitating-cobra
470+
// bug. Ground-lock and pins have already fixed the root placement that
471+
// floor/landmark targets resolve against.
472+
applyReaches(info.reaches);
461473
// Safety net: nothing above ever intentionally pushes part of the body
462474
// below the floor, so clamp the root up whenever the lowest point dips
463475
// below y=0, a no-op whenever the pose is legitimately grounded or
@@ -473,7 +485,6 @@ export function createViewer(
473485
mannequin.root.position.y -= box.min.y;
474486
mannequin.root.updateMatrixWorld(true);
475487
}
476-
applyReaches(info.reaches);
477488
if (info.phaseName !== lastPhaseName) {
478489
lastPhaseName = info.phaseName;
479490
phaseCb({ phaseName: info.phaseName, ...(info.cue ? { cue: info.cue } : {}) });

0 commit comments

Comments
 (0)