Files
animation/.agents/plans/flat-handpose-runtime-2026-08-17.md
T

89 lines
5.1 KiB
Markdown
Raw Normal View History

# Handoff: runtime FLAT hand pose in ariki-game (2026-08-17)
> **STATUS 2026-08-17 (session "flatpose"): DONE — all 3 acceptance criteria verified.**
> Implementation (uncommitted, in ariki-game): `src/Animation/HandPoseLayer.cs`
> (SkeletonModifier3D, per-hand 0..1 slerp blend, Mako/MixamoSkin excluded, missing bones
> skipped), PlayerController wiring (`SetFlatHands`, attached to `AnimatedSkeleton`),
> DanceTeam/DancerRig plumbing, bed hotkeys **H** (toggle, active team) + **J** (hand cam),
> HUD `hands=FLAT` flag, pose copied to `assets/quaternius/hand-poses/pose_flat.json`.
> Verified in the dance bed (agent API screenshots): exp01 body fingers visibly flatten
> mid-dance with no spikes; shipped mitt body = silent no-op, zero console errors; nothing
> committed. Test body copy `derived-bodies/lena_leafbikini_quatskin_fingers_glb_exp01.glb`
> is untracked/test-only — do not ship it from here. One Vulkan device-lost crash occurred
> during testing (RX 5700 XT TDR) — unrelated to this code, relaunch cured it.
**Goal:** the female player character can hold a FLAT hand (fingers straight, together)
at runtime, applied as a layer on top of any playing animation. Flat only — fist and
grip are blocked on a weight repair that is running in a parallel lane (see "Scope
fence" below).
## Why this works at all
- ~70100% of finger tracks in the shipped dance clips are frozen at rest, so a
per-frame finger override loses nothing from the animations.
- Hand poses were harvested from the Kevin packs into `animation/hand-poses/`:
`pose_flat.json`, `pose_relaxed.json`, `pose_fist.json`, `pose_grip.json`.
- Format: `{"bones": {"<bone_name>": [x, y, z, w], ...}}` — glTF node-local
quaternions on the **canonical Quaternius skeleton**, which is exactly Godot
bone-pose space for these bodies. Apply directly:
`skeleton.SetBonePoseRotation(skeleton.FindBone(name), new Quaternion(x, y, z, w))`.
No rest-relative correction on canonical rigs (that hack is only for Mako, out of
scope here).
- 40 bones per pose file, including `*_04_leaf_*` tip bones. Some bodies lack the
leaf bones — **skip bones that FindBone returns -1 for**, never error.
## Body situation (the trap that makes testing confusing)
- The SHIPPED female body `assets/quaternius/derived-bodies/Ariki_Female_QuatSkin.glb`
has **zero finger weights** (the converter deliberately folds fingers into the hand
bone — "rigid mitt"). Applying the pose to her is correct code but shows NOTHING.
- A finger-weighted candidate exists and the flat pose is validated on it
(max displacement 1.8 cm, clean QA renders in
`animation/characters/work/lena_leafbikini/v02/review/`):
`animation/characters/work/lena_leafbikini/v02/lena_leafbikini_quatskin_fingers_glb_exp01.glb`
Use it as the TEST body.
- Mako (`Ariki_Male_Mako.glb`) has corrupt cross-hand finger weights — do NOT enable
the pose layer on him; a finger curl throws verts metres. Female/canonical only.
## Implementation pointers
- Pattern to copy: `ariki-game/src/Animation/PlayerIKRig.cs` — post-animation bone
modification. The hand-pose layer is the same idea: after the AnimationTree/Player
updates, write the pose quats onto the finger bones each frame while the layer is
active. Keep an on/off (and ideally a blend weight 0..1 slerping from the animated
pose) per hand.
- Test bed: `ariki-game/src/Testing/Dance/DanceTestBed.cs`. `DANCE_BODY_GLB` env var
swaps the bed's body — point it at the exp01 GLB above (see comment near line 82).
Run with `MOCK_ONLY=1`.
- ENGINE TRAP: the S3 engine is v1.0.0 (Godot 4.6.2) and the repo is on SDK 4.7.
If you locally downgrade `csproj`/`project.godot` to launch, **NEVER commit those
lines**.
## Verification tools (animation repo)
- `tools/handpose_bake_preview.py body.glb pose.json out.glb` — bakes a pose into a
GLB's rest rotations (what the runtime layer should reproduce).
- `tools/skin_displacement_check.py posed.glb original.glb` — Godot-exact LBS math;
flat on exp01 reads max 1.8 cm / median 0.27 cm. Meter-scale numbers = broken.
- `tools/handpose_skin_to_obj.py posed.glb l|r out.obj` +
`tools/handpose_render_objs.py` (blender --background) — the only honest VISUAL
check. **Do not judge by importing a baked-pose GLB into Blender and rendering:
the importer ignores the rest-vs-bind rewrite and draws false shards.**
## Scope fence
- Do not swap or re-export any shipped body (`Ariki_Female_QuatSkin.glb`,
`characters/female/lena_leafbikini_base_v01/` is frozen). The finger-weighted body
ships from the parallel weight-repair lane, not from this task.
- Do not touch `ariki-game/tools/make_lena_fullres_quatskin.py`; its
`LENA_RIGID_FINGERS` default must stay `"1"`.
- Flat pose only. Fist/grip activation waits for the repaired weights.
## Acceptance
1. In the dance test bed with `DANCE_BODY_GLB` = exp01, toggling the layer while a
dance plays visibly straightens/flattens the fingers, no vertex spikes, and the
rest of the animation is unaffected.
2. On the shipped mitt-handed body the layer is a silent no-op (no errors).
3. Nothing committed in either repo changes any shipped asset or engine version.