Grip pose system: grip_points with per-hand drivers, states, and finger poses #46
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Grip pose system: grip_points with per-hand drivers, states, and finger poses
Status: DESIGN IN DISCUSSION (Zach + Lantern, 2026-11-12). Nothing built. This issue files the full discussion so it can be reviewed in one place.
Related: #42 (hands thread — glove curl calibration H5, two-handed pivot split H6, decoupled hands, per-hand grips slice 1 in PR #45), #4 (data-driven item definitions — this proposal revises #4's Grips section), #33 (weapon handedness), #40 (per-finger pose API appendix). Plan doc:
dead-air-grip-pose-plan.md(slices 1–5).The goal
Elements should be grabbable on objects. Ranged grab always grabs the primary grip point. Foregrips and grip attachments are secondary grip points — places you can physically grab, such as the slide on a gun, or the second hold of a two-grip hold. Hand grips also store finger poses, including state-based code for controllers: on Quest controllers, when the finger is not touching the trigger it stays straight out (as on a gun), and when it touches the trigger it curls onto the gun trigger, moving a % based on trigger pull. Those values are authored in the editor mod toolkit.
The data shape (current revision)
grip_pointskeyed by mesh/point name (any named part can be a grip point: slide, foregrip, mag body). Each point has arole(primary/secondary), and per hand (left/right) apose, adriver, and astatetable. Values below are illustrative, not the pistol's live numbers.Rules the shape bakes in
rolelives on the point:primaryis what ranged grab always takes,secondaryis physically grabbable (slide rack, second hand).model.grip_offset/grip_rotate, folded in — slice 1'sgrip_posesis subsumed bygrip_points[<point>].<hand>.pose. Slice 1's mirror rule and scale rule carry over unchanged: left omitted means mirror of right (for centered objects; non-centered left defaults to right, flagged not-mirrored); poses are authored in model space and scale with the item's content scale at load.default. A state only lists what changes (the trigger states only touch the index finger).defaultis the fallback; everything else is code-selected or driven.stateis singular, matching the proposal's notation; hand keys are lowercaseleft/right, matching slice 1'shands: ["left"]convention.spread, and an additive per-jointjointsescape hatch on individual knuckles. GDScript owns the per-joint curves, clamps, and left-hand mirroring.schema: 1versions it.driversits at hand level (per hand, next toposeandstate) and names the registry id of the driver controlling the hand pose for those grip states. The states and pose data are provided to the driver, and the driver interprets the values.live_inputscomes from the XR action state; in the editor, the dock feeds the same dict from sliders (trigger pull slider, touch toggle). The driver cannot tell the difference — that is what makes it runnable in the viewport, so the author can see how it actually works.The GDScript sketch
Sketch, not the real API — method names like
apply_finger_poseare illustrative.Call site, the same in both worlds:
How the design evolved (revisions)
grip_points[mesh/point name].{Left/Right}.state["default", ...]with abasefield holding a named pose (e.g.pistol_grip) and adriverentry insidestateblending between two states on an analog input. Finger data from the per-finger pose API design.basedoubles as a code control field.pistol_griprenamed togun_grip_pose; GDScript attaches to that name to control how the fingers are arranged and controlled from the configured states.driveroverride was dropped at this step; JSON stays pure data, code stays in code. The editor never has to understand a controller's logic, just the state names each registered controller declares.basefield is renamed todriver, and it controls which driver is controlling the hand pose for those grip states. The states and pose data are provided to the driver, and the driver interprets the values. The driver must be able to run in the viewport so the author can see how it works. This supersedes revision 2's "driver override dropped" — the override is gone, but the data stays in JSON and the driver field selects the interpreter.Relationship to existing work
model.grip_offset/grip_rotateas the base/right pose,grip_posesentries overriding per side, two primary GrabPoints (GrabPrimaryR/GrabPrimaryL), mirror rule (lateral axis = item-local X, 5 mm centered epsilon), scale rule, editor side/mirror/render toggles,gun_grip→griprename. This proposal subsumes slice 1's data:grip_offset/grip_rotateandgrip_posesfold intogrip_points[<point>].<hand>.pose(+driver+state).base/driverfield is the new part.rootpos/rot plus abonestable, side-less, mirrored by the engine. This proposal replaces it with per-point, per-handpose+driver+state.weaponproperty table instead ofweapon_one_hand. Verified: the pistol declareskind_tags: ['weapon', 'weapon.pistol', 'holster.hip']and its stats live inproperties.weapon— noweapon_one_handanywhere, and notype_tableskey at all. So it gets the genericweapontreatment, consistent with the hypothesis. Caveat: the table governs the handedness multipliers, but the two-hand pivot alignment is the separate H6 gap —_follow's two-hand branch does full basis alignment for any two-handed grab regardless of table. Switching the table fixes the multipliers, not the pivot; the pivot split is still its own work (plan in the rebuild doc §2d.5, waiting on the #43 spike results per Zach's sequencing).Open questions / decisions for Zach
grip_pointstop-level, or undermodel? The proposal wroteobject.grip_points—objectread as the item's object/model table.apply_finger_posemethod on HandView, and the real da_* input name for trigger pull (da_trigger_pullin the sketch is illustrative).gun_grip_pose,slide_grip_pose, … — confirm the_posesuffix convention.weapon_one_hand(data change), or stay genericweapon?What is NOT in this issue
Nothing is built — design only, no code changes, no item JSON changes. The editor side (dock offering each registered driver's declared states, sliders feeding simulated inputs in the viewport) is described, not built. The per-finger curl calibration rework (H5) and the pivot split (H6) stay in #42.