@@ -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