Merge branch 'worktree-agent-a07f1b74ffe22c320'

# Conflicts:
#	HANDOFF.md
This commit is contained in:
Foxyz committed 2026-10-02 05:52:00 +03:00
commit b5e7241ac1
11 files changed
+1281 -315

No files matched your search

+40 -26
View File
@@ -147,32 +147,45 @@ device only; the editor has an XR simulator for dev. Trackers/action map: see `x
(bool actions unreliable across runtimes). Engine logs land in
`~/.local/share/godot/app_userdata/VR Messs/logs/godot.log` — agents: read this after user
playtests instead of guessing.
- **UI direction — physical prop controls first**: buttons, knobs, sliders as real 3D objects
(touch interaction, later Box3D for feel). Godot has NO native world-space widget system —
`Control`s are screen-space; world UI is always Control-rendered-into-3D via `SubViewport`
(optionally `SubViewportCompositionLayer` for XR-native sharpness/perf). Use at most one
SubViewport/composition-layer screen for dense content (file browser, settings) if needed;
everything else is props. World-space text is native and cheap: `Label3D` (SDF, crisp at
distance) and `TextMesh` (extruded) — no SubViewport required.
- **`WorldPanel.cs` is the first prop-UI prototype**: summoned/dismissed via `ax_button` (A/X —
- **UI direction — physical prop controls first, ONE dense screen**: buttons, knobs, sliders
as real 3D objects where they live in the world (touch interaction, later Box3D for feel).
Godot has NO native world-space widget system — `Control`s are screen-space; world UI is
always Control-rendered-into-3D via `SubViewport` (optionally `SubViewportCompositionLayer`
for XR-native sharpness/perf). At most ONE SubViewport/composition-layer screen for dense
content — **that screen is now the in-VR menu (`WorldPanel`, below)**; everything else is
props. World-space text is native and cheap: `Label3D` (SDF, crisp at distance) and
`TextMesh` (extruded) — no SubViewport required.
- **`WorldPanel.cs` = the in-VR menu (2026-10-02, replaced the chunky prop-knob panel)**:
real Godot Control UI (`MainMenu.cs`: tabs Player / World / System, normal-sized buttons,
slider, scrollable log, FPS) rendered by `MenuScreen.cs` into a 960×640 SubViewport shown on
a thin 0.36×0.24 m panel (~2.7 px/mm ≈ 1:1 with headset pixels at 45 cm — viewport textures
have no mipmaps, so don't oversample). Summoned/dismissed via `ax_button` (A/X —
menu_button belongs to the runtime). It is PARENTED UNDER `XROrigin3D` — Koikatsu-VR-style
"fixed in the player's room": IRL movement doesn't move it relative to you, game locomotion
carries it. If that feel is wrong, reverting = reparenting to `Main` and fixing the `_Ready`
node paths. Floats in front of the summoning hand facing the player (full 3D facing — works
from lying poses), draggable directly or via laser at fixed ray distance while gripped
(continuously re-orients toward the head). The Spawn
knob also recalls the panel in front of the head. The pointer
tip is a protruding stylus cone (apex ~12 cm ahead of the controller) and is THE cursor:
knobs hover-highlight and PRESS ON CONTACT (tip within 4.5 cm, with cooldown) — physical
buttons, no trigger needed; trigger still presses laser-hovered knobs (knobs are
laser-targetable). Presses play a synthesized click (code-generated `AudioStreamWav` PCM
— no asset). Spawn/Ping/Close knobs. Locomotion defers
node paths. Appears 0.3 m in front of the summoning hand facing the head (full 3D facing —
works from lying poses), draggable by grip directly or via laser at fixed ray distance
(continuously re-orients toward the head); "Return to spawn" recalls it in front of the head.
**Input = the touch-first hybrid cursor, injected as mouse events**: each frame both
controller rays are projected onto the panel plane (analytic, no physics query), converted to
SubViewport pixels and pushed with `Viewport.PushInput(event, true)`
(`InputEventMouseMotion`/`InputEventMouseButton`, `NotifyMouseEntered/Exited`). The stylus
tip (12 cm ahead of the controller) within 6 cm of the surface = TOUCH cursor: crossing
0.6 cm presses, backing out past 1.8 cm releases (hysteresis; entering from the side/behind
never presses) — buttons fire on release like a real tap, sliders drag while touching.
Farther away = LASER cursor, trigger clicks/drags (edge-triggered: a trigger already held
never clicks). Thumbstick Y while hovering = mouse wheel (scroll containers). One Godot mouse
per viewport → one owning hand (press holder ≻ touching hand ≻ laser). Cursor dot per hand
(shrinks as the tip nears = proximity cue), haptic tick on press, synthesized click
(code-generated `AudioStreamWav`, no asset). Menu actions run DEFERRED (never from inside
the injected event). "Recalibrate body" calls `Recalibrate()` on the first node in group
`"calibration"` (disabled when absent — the menu never creates it; `Calibration.cs` provides it). Locomotion defers
grab/teleport to the panel when a hand is near it (`WorldPanel.IsBlocking`). Laser lines are
owned by `Locomotion.UpdatePointer`: shown only when aiming at something interactive (marker
or panel) AND beyond `LaserMinDistance` (0.35 m) — near targets are touched via the pointer
tip instead, which is the touch-first hybrid cursor in miniature. Grab deltas and turn
pivots are computed in WORLD space (tracking-space math made two-hand turns orbit the map
center — do not regress this).
owned by `Locomotion.UpdatePointer`: for the menu it asks `WorldPanel.TryGetLaserDistance`
(beam only while that hand is in laser mode, beyond `LaserMinDistance` 0.35 m); markers keep
the raycast path. Regression: `scenes/menu_spike.tscn` "MENU: overall PASS". Grab deltas
and turn pivots are computed in WORLD space (tracking-space math made two-hand turns orbit
the map center — do not regress this).
- **Control scheme is deliberate**: decide what lives on controllers vs. what lives in menus
before stacking bindings — controller button budget is scarce, menus carry the rest.
@@ -189,8 +202,9 @@ device only; the editor has an XR simulator for dev. Trackers/action map: see `x
movement weird, teleport + lasers broken (owner report 2026-10-01; experiments, not
current issues). Fix-or-replace deferred — owner's candidate replacement is physics
thumbstick movement if the body-physics track lands.
4. ~~UI: first prop panel~~ — **done** (`WorldPanel.cs`); SubViewport/composition-layer screen
deferred until dense content (file browser) is actually needed.
4. ~~UI: first prop panel~~ — **done** (`WorldPanel.cs`), then REPLACED (2026-10-02, owner:
knobs "incredibly chonky") by the SubViewport menu screen (`MenuScreen` + `MainMenu`) —
the one allowed dense screen. Awaiting owner playtest.
5. ~~Box3D validation spike~~ — **done, PASS** (drop+contact, velocity-chase vs wall, headless
via `scenes/physics_spike.tscn`).
6. **Physics hands & props sandbox — grab v5.3 = SOLVER-OWNED RIGID WELD (patched addon).**
@@ -223,8 +237,8 @@ device only; the editor has an XR simulator for dev. Trackers/action map: see `x
(Nimso/VRChat precedent); props/floor/table mask 1|3|4|5, hands 4/1|4, chest 3/1|3,
arm segments 5/1; panel stays 2/0 (props pass
through even when shown); layers live on
CollisionObject3D, NOT CollisionShape3D. Panel = prop-UI prototype (ax_button summon,
contact-press knobs, Reset). Laser/distant grab REMOVED (user never knew it existed);
CollisionObject3D, NOT CollisionShape3D. Panel = the in-VR menu (ax_button summon,
touch/laser Control UI, Reset props). Laser/distant grab REMOVED (user never knew it existed);
grab = palm-proximity only, per-hand palm-face offset (left +X, right −X — taste).
PinJoint quirks (anchors captured at creation; joint node position READ every tick;
break measures the prop-local anchor vs the palm point) apply to the v5.0–5.2 PIN path