6.2 KiB
6.2 KiB
Research: grabbing/picking up physics objects in VR (for physics-hands rigs)
Third research pass for VR Messs, after script-level grab welds (PD force weld, then
rigid-follow-with-yield) proved unstable: launched objects, hand drift, limit cycles.
Label: [S] = sourced, [I] = inference. Companion to vr-arm-ik.md / vr-arm-ik-deep.md.
Sources verified (primary where possible)
- [S] godot-xr-tools — actual source read (pickable.gd, grab_driver.gd, grab_point_hand.gd, master: https://github.com/GodotVR/godot-xr-tools/blob/master/addons/godot-xr-tools/objects/pickable.gd): pickables are RigidBody3Ds that get
freeze = trueon grab; a GrabDriver node kinematically drives the frozen body to the hand's grab point per physics tick; on release, freeze is restored andlinear_velocityis set for the throw. Second-hand enum IGNORE/SWAP/SECOND — in SECOND mode both grab points drive, blended by each point'sdrive_position. NO JOINTS anywhere. - [S] Unity XR Interaction Toolkit (https://docs.unity3d.com/Packages/com.unity.xr.interaction.toolkit@3.1/manual/xr-grab-interactable.html): three movement types — VelocityTracking (sets velocity/angular velocity per FixedUpdate; best collision fidelity + weight feel, "can appear to lag"), Kinematic (move-toward), Instantaneous (teleport; clips). Two-handed = multiple grab points + grab transformers. Zero joints.
- [S] BNG/VRIF (https://wiki.beardedninjagames.com/en/Core/Grabbable): GrabPhysics = Physics Joint (ConfigurableJoint grabber→grabbable, rigidity dynamically retuned by collisions), Kinematic, Velocity, or None; Break Distance for force-release; secondary grab "DualGrab currently unsupported" — single primary hand only.
- [S] Blade & Sorcery (BasSDK wiki: https://kospy.github.io/BasSDK/Components/ThunderRoad/Items/Handle.html): items stay DYNAMIC rigidbodies; Handle defines grab axis/radius;
twoHandedRequireSamePhysicbody— dual grip = both hands gripping handles on the SAME body; hand colliders disabled while gripping. [I] Joint type per grip (decompiled ThunderRoad). - [S] Boneworks/Bonelab (MarrowSDK: https://github.com/StressLevelZero/MarrowSDK; GDC: https://www.gamedeveloper.com/design/how-classical-game-mechanics-and-physics-converge-in-vr-hit-i-boneworks-i/): hands are rigidbodies force-driven to controllers; grab = JOINT from hand body to object at the grab point → mass "tug of war"; philosophy: "no infinite force", the 82 kg body grounds all force budgets.
- [S] HL:Alyx (dev commentary: https://combineoverwiki.net/wiki/Developer_commentary/Half-Life:_Alyx): two-handed weapons were CUT because "the object itself provides a physical connection between the two hands" and VR couldn't fake it. [I] Attachment = Source 2 constraint (undocumented).
- [I] H3VR: no public implementation writeup (interview: https://voicesofvr.com/1717-h3vr-10-year-retrospective-with-anton-hand-origins-design-evolution-of-hot-dogs-horseshoes-hand-grenades/); community consensus says spring/joint attach.
- [I] Auto Hand: physics-based, two-handed grabs, break events (https://assetstore.unity.com/packages/tools/game-toolkits/auto-hand-vr-interaction-165323); joint details not public.
- [S] Instability prior art: ODE manual on joint motors with force limits — letting the SOLVER integrate beats per-step velocity override, which overshoots/oscillates (https://ode.org/wiki/index.php); wirewhiz Alyx-hands writeup: joint-driven hands at 90 Hz, "greatly increased stability" from a hand-root collider, overly aggressive joint values destabilize the sim (https://wirewhiz.com/making-half-life-alyx-physics-hands-in-unity/).
Taxonomy
| Approach (who uses it) | Stability | Weight feel | Two hands | Cost in Box3D |
|---|---|---|---|---|
| Freeze + kinematic drive (xr-tools, XRIT Kinematic) | High (boring-safe) | None real; faked via clamps | Blend two drivers; source-verified | Low |
| Velocity-tracking hold (XRIT VelocityTracking, VRIF Velocity) | Medium — lag/jitter documented | Good (real mass resists) | Weak; lag reported | Low — but this was our unstable design |
| Joint hand→object, force-limited hand (Boneworks, B&S, VRIF Physics Joint, H3VR [I]) | High IF the solver owns the coupling | Best: hand lags under load, the hand's force budget does the work | Second joint to the same body (B&S model), or single-hand-only (VRIF) | Medium |
| Rigid weld/parenting (Alyx-style) | High but fake | Tuned by illusions | Cut by Valve for good reason | High (needs 6DOF) |
Recommended architecture for VR Messs
- [S-backed] Position coupling: PinJoint3D hand→object at the grab point. Stop setting object velocity — velocity override + reaction force = two controllers fighting one integrator (the limit cycle we hit). Let the engine's joint solver own both bodies (ODE/force-limit literature; matches Boneworks/B&S/VRIF). One authority, no fight.
- [I] Rotation: clamped angular spring on the held object toward the hand's grip orientation (angular-velocity chase toward the basis delta, capped ~20 rad/s), owned by the PRIMARY hand only. With only Pin/Hinge/Slider, the pin gives position for free; the clamped spring supplies rigid-grip feel without 6DOF.
- [S] Weight feel = the existing hand force budget. Don't clamp object mass; heavy objects drag the hand through the joint (Boneworks tug-of-war). Optionally scale the rotational spring by mass (nose-heavy guns).
- [S] Grip break: Godot joints have no break force — monitor the joint anchor separation; sustained error over N ticks = grip ripped away (same effect as VRIF Break Distance, manually).
- [S] Two hands: BOTH hands PinJoint to the same object (B&S
twoHandedRequireSamePhysicbodymodel). Primary hand owns the rotation spring; secondary adds position only. Do NOT connect the hands through the object — Valve cut that. - [S] Fallback if Box3D's pin joint proves too soft/unstable: the xr-tools pattern — freeze the held body, drive it kinematically to the hand each tick, restore velocity on release, blend second-hand drivers. Zero instability, weight feel becomes theater.
Note: our physics hands already chase with forces, so the joint approach slots straight in — the hand body IS the Boneworks setup; only the grab coupling was wrong.