Merge branch 'worktree-agent-a07f1b74ffe22c320'
# Conflicts: # HANDOFF.md
This commit is contained in:
commit
b5e7241ac1
11 files changed
+1281
-315
No files matched your search
+84
-24
@@ -39,7 +39,11 @@ TERMINOLOGY (owner-corrected — use precisely):
|
||||
`physics_spike` "SPIKE: overall PASS", `weld_spike` "WELD: …", `grab_spike` "GRAB: …",
|
||||
`body_spike` "BODY: …", `collapse_spike` "COLLAPSE: …", `arm_spike` "ARM: overall PASS",
|
||||
`sixdof_spike` "6DOF: overall PASS" (run FIRST with `weld_spike` whenever the addon binary changes),
|
||||
`truebody_spike` "TRUEBODY: overall PASS" (no physics; plain `--headless`). All eight PASS 3× as of this handoff. Headless asset import:
|
||||
`truebody_spike` "TRUEBODY: overall PASS" (no physics; plain `--headless`), `menu_spike` "MENU: overall PASS"
|
||||
(in-VR menu input injection, no XR). All nine PASS as of this handoff.
|
||||
**Fresh checkout / new worktree: run `godot-mono --headless --import` FIRST** (it may segfault at
|
||||
exit — harmless) — without `.godot/extension_list.cfg` the Box3D extension silently doesn't load,
|
||||
Godot falls back to its default physics and weld/grab/arm/6dof spikes report CHECK. Headless asset import:
|
||||
`godot-mono --headless --import`.
|
||||
- Engine logs: `~/.local/share/godot/app_userdata/VR Messs/logs/godot.log` — **read after user
|
||||
playtests instead of guessing**.
|
||||
@@ -64,8 +68,11 @@ TERMINOLOGY (owner-corrected — use precisely):
|
||||
Teleport: aim + trigger edge, fade to black 0.12 s in / 0.25 s out, near markers within
|
||||
`MarkerNearDistance` (1.2 m) hide fully (disc/beam/label) and are not targetable. Lasers via
|
||||
`UpdatePointer`: shown only when aiming at something interactive (marker or panel) AND beyond
|
||||
`LaserMinDistance` (0.35 m). Also builds the stylus pointer tip per controller. `ReturnToSpawn()`
|
||||
is public (panel Spawn knob calls it).
|
||||
`LaserMinDistance` (0.35 m); for the menu the beam length comes from
|
||||
`WorldPanel.TryGetLaserDistance` (beam only while that hand is in LASER mode — a hand touching the
|
||||
menu shows no beam); the beam starts at the stylus tip (`LaserStart` 0.12 m) and ends at the hit.
|
||||
Also builds the stylus pointer tip per controller. `ReturnToSpawn()` is public (menu "Return to
|
||||
spawn" calls it).
|
||||
- **AnchorMoveMode.cs** — grab-world movement, ANCHOR semantics. One-hand grip moves the player
|
||||
OPPOSITE to hand motion (full 3D); both grips = anchor translation + in-place yaw turn around the
|
||||
head. Grip is suppressed while that hand prop-grabs (`PropGrabber.IsGrabbing`) or the panel is
|
||||
@@ -115,17 +122,55 @@ TERMINOLOGY (owner-corrected — use precisely):
|
||||
its controller beyond `PhysicsHands.SnapDistance` (2 m) → "grip dragged off" instant break
|
||||
(heavy props can drag the hand since PhysicsHands' snap is suspended while gripping).
|
||||
- **PropShop.cs** — builds table (StaticBody3D) + spawns prop table from `Specs[]` (ping pong 2.7 g
|
||||
→ anvil 50 kg), Label3D captions, CCD for < 0.1 kg, group "prop". `Respawn()` = Reset knob.
|
||||
- **WorldPanel.cs** — prop-UI panel, PARENTED UNDER `XROrigin3D` (fixed in player's room; revert =
|
||||
reparent to Main + fix node paths). Summoned/dismissed via `ax_button` (A/X; menu_button belongs
|
||||
to the runtime). Knobs: Spawn (return to spawn + recall panel in front of head), Ping, Reset
|
||||
(PropShop.Respawn), Close. Panel/knob bodies: **CollisionLayer 2, CollisionMask 0** (props pass
|
||||
through); all CollisionShapes disabled while hidden (`SetShapesEnabled`, deferred). Pointer tip is
|
||||
a 12 cm stylus cone; knobs hover-highlight and PRESS ON CONTACT (tip within 4.5 cm, 400 ms
|
||||
cooldown) — physical buttons; trigger still presses laser-hovered knobs. Click = code-generated
|
||||
`AudioStreamWav` PCM (no asset). Draggable direct or via laser at fixed ray distance, re-orients
|
||||
toward head while gripped. `IsBlocking(controller)` (within 0.45 m) makes Locomotion defer
|
||||
grab/teleport; `IsPanelCollider(node)` identifies interactive colliders for lasers.
|
||||
→ anvil 50 kg), Label3D captions, CCD for < 0.1 kg, group "prop". `Respawn()` = menu "Reset props".
|
||||
- **WorldPanel.cs** — the in-VR MENU (2026-10-02; replaced the chunky 3D knob panel). XR glue only:
|
||||
PARENTED UNDER `XROrigin3D` (fixed in player's room; revert = reparent to Main + fix node paths).
|
||||
Summoned/dismissed via `ax_button` (A/X; menu_button belongs to the runtime), appears
|
||||
`SummonDistance` 0.3 m in front of the summoning hand facing the head; grip-drag direct (hand
|
||||
within `DirectGrabRadius` 0.45 m) or along the laser at fixed ray distance, re-orients toward the
|
||||
head while gripped; "Return to spawn" recalls it `HeadRecallDistance` 0.45 m ahead / 0.12 m below
|
||||
the head. Feeds both controllers (ray = `-Basis.Z`, `trigger`, `primary` stick; a hand dragging
|
||||
the panel doesn't point) into MenuScreen every frame, wires MainMenu events (deferred) to
|
||||
`Locomotion.ReturnToSpawn`, `PropShop.Respawn`, ping tone, close; synthesized click + ping
|
||||
(`AudioStreamWav` PCM, no asset), haptic tick (`"haptic"` action) on press. `IsBlocking(controller)`
|
||||
(hand within 0.45 m of the shown panel) makes Locomotion/AnchorMoveMode defer grab/teleport;
|
||||
`IsPanelCollider(node)` = the screen's StaticBody (**layer 2, mask 0**, props pass through; shapes
|
||||
disabled while hidden); `TryGetLaserDistance(controller)` drives the laser beam.
|
||||
- **MenuScreen.cs** — generic "Control UI on a 3D panel" + the touch-first hybrid cursor,
|
||||
input-agnostic (spike drives it without XR). SubViewport 960×640 (`Disable3D`, MSAA 2D 4×,
|
||||
update Always while shown / Disabled while hidden) on an explicit-UV quad 0.36×0.24 m (unshaded,
|
||||
linear filter — viewport textures have no mipmaps, resolution chosen ≈ 1:1 with headset pixels at
|
||||
~45 cm) + backing frame + StaticBody. Per hand, in panel-local space (analytic plane math, no
|
||||
raycast): TOUCH if the stylus tip (`TipLength` 0.12 m ahead) is over the panel (+1.5 cm edge
|
||||
margin) between `HoverDepth` 6 cm in front and `PushThroughDepth` 8 cm behind, controller in
|
||||
front — press when the tip crosses `ContactDepth` 0.6 cm FROM ABOVE (armed only by hovering in
|
||||
front: sideways/behind entry never presses), release past `ReleaseDepth` 1.8 cm; else LASER if
|
||||
the ray hits the panel from the front — trigger rising edge presses, release on trigger up
|
||||
(trigger state tracked even while hidden/reset, so a held trigger never clicks). One mouse per
|
||||
viewport → one OWNER hand: the hand holding a press keeps it; else the touching hand (smallest
|
||||
depth); else the previous laser owner; else any laser hand. Owner change / leaving the panel
|
||||
releases the button at the last position (Godot then cancels the click, like dragging off a
|
||||
button). Injection: `Viewport.PushInput(InputEventMouseMotion{Position, Relative, ButtonMask},
|
||||
true)` on cursor move, `InputEventMouseButton{Left}` on press/release, `NotifyMouseEntered/Exited`
|
||||
on owner gain/loss; owner's stick Y beyond 0.3 → wheel notches at 14/s (`WheelUp/Down`, press +
|
||||
release). Cursor dot per hand (touch: blue, shrinks as the tip nears; laser: orange; non-owner
|
||||
half-transparent). Buttons fire on release (Godot default) = tap semantics.
|
||||
- **MainMenu.cs** — the menu content (`PanelContainer`, built in code, own `Theme`: 26 px font,
|
||||
64 px buttons ≈ 24 mm, round 36 px slider grabber, wide scrollbar, hover/pressed styles, no focus
|
||||
boxes). Header: title, FPS (4 Hz), Close. Tabs: **Player** (Return to spawn, Recalibrate body),
|
||||
**World** (Reset props, Ping sound), **System** (click volume slider, Close menu, scrollable log).
|
||||
**Calibration contract**: "Recalibrate body" calls `Call("Recalibrate")` on
|
||||
`GetTree().GetFirstNodeInGroup("calibration")` if it `HasMethod("Recalibrate")`; otherwise the
|
||||
button is disabled with "Not available" (re-checked on summon and at 4 Hz). The menu never creates
|
||||
that node — `Calibration.cs` (main.tscn) provides it. Button actions are C# events invoked via `CallDeferred` (never from inside the
|
||||
injected input event). `Log(line)` appends to the System log (last 40).
|
||||
- **MenuSpike.cs** + `scenes/menu_spike.tscn` — headless menu regression without XR: MenuScreen +
|
||||
MainMenu on a tilted panel; asserts pixel mapping round trip, touch path (hover, contact press,
|
||||
Return-to-spawn fires once while the other hand idles as a laser = arbitration), side entry
|
||||
behind the surface does NOT press, laser + trigger (tab switch, Reset props, laser distance),
|
||||
held trigger arriving does NOT click, touch tab switch, laser slider drag, thumbstick scroll of
|
||||
the log, Recalibrate disabled without / enabled + called with a stub "calibration" node, header
|
||||
Close by touch. Must print "MENU: overall PASS".
|
||||
- **TeleportMarker.cs** — `scenes/teleport_marker.tscn`: disc + beam + Label3D, group
|
||||
"teleport_marker", StaticBody pick box. `SetNear` hides visuals, `SetHighlighted` swaps colors.
|
||||
- **PhysicsTorso.cs** — the player's PHYSICAL CHEST (body-physics Stage 1). Implemented
|
||||
@@ -253,7 +298,7 @@ plan-B if the patched-wrapper path is ever abandoned (owner pointed at it; adopt
|
||||
leaving the standard RigidBody3D stack).
|
||||
|
||||
Scene wiring (`scenes/main.tscn`): Main → WorldEnvironment, Sun, Floor, XROrigin3D (XRCamera3D,
|
||||
LeftHand, RightHand, WorldPanel), SpawnPoint, 3 TeleportMarkers, Locomotion, AnchorMoveMode,
|
||||
LeftHand, RightHand, WorldPanel — builds its MenuScreen/SubViewport/MainMenu children in code), SpawnPoint, 3 TeleportMarkers, Locomotion, AnchorMoveMode,
|
||||
PropShop, PropGrabber, PhysicsHands, PhysicsTorso, PhysicsArms, BodyRig, FadeLayer/Fade
|
||||
(physics order matters: hands → chest → arms, so the arm IK reads this tick's bodies). Action map: `openxr_action_map.tres`.
|
||||
Floor StaticBody mask 1|3|4|5 (collision_mask 29).
|
||||
@@ -280,7 +325,7 @@ Floor StaticBody mask 1|3|4|5 (collision_mask 29).
|
||||
patch, and rebuild recipe). Pin/hinge/slider paths untouched — grab v5.2.1 remains runnable
|
||||
as fallback (git history). Stink-O's box3d-godot (custom components, more joint types) is
|
||||
the plan-B.
|
||||
- **Collision layers**: 1 = world, 2 = UI. Panel/knob bodies are layer 2 mask 0 (props pass through
|
||||
- **Collision layers**: 1 = world, 2 = UI. The menu panel body is layer 2 mask 0 (props pass through
|
||||
the menu); **1 = world+props, 2 = UI, 3 = player body (chest), 4 = player hands, 5 = player arms** —
|
||||
no self-collision between any own body parts (props/table/floor mask 1|3|4|5, hands 4/1|4,
|
||||
chest 3/1|3, arm segments 5/1). Layers/masks live on `CollisionObject3D`, never `CollisionShape3D`. The
|
||||
@@ -344,8 +389,15 @@ Floor StaticBody mask 1|3|4|5 (collision_mask 29).
|
||||
about the locomotion decision; don't over-invest in anchor-loco until that's decided.
|
||||
- **Teleport + lasers are BROKEN** — owner report; both were experiments anyway and are
|
||||
NOT current issues. Locomotion.cs's marker/laser machinery may be resurrected or deleted
|
||||
when the locomotion direction is decided; WorldPanel's laser-hover knobs still reference
|
||||
the laser system, so a deletion needs that decoupling first.
|
||||
when the locomotion direction is decided. The menu's laser cursor itself lives in MenuScreen
|
||||
(analytic, no raycast); only the drawn BEAM is in `Locomotion.UpdatePointer` — move the beam
|
||||
drawing along if that file is deleted.
|
||||
- **In-VR menu (MenuScreen/MainMenu, 2026-10-02) — awaiting owner playtest.** Things to check
|
||||
in-headset: text sharpness/shimmer at normal distance (no mipmaps — if it shimmers when far,
|
||||
lower the resolution or try `SubViewportCompositionLayer`; if blurry up close, raise
|
||||
`SizePixels`), touch press depth/feel (`ContactDepth`/`ReleaseDepth`), accidental presses when
|
||||
reaching through the panel, laser/touch hand-over near the panel, thumbstick scroll direction,
|
||||
the stylus tip vs the drawn cursor dot alignment, and whether 0.3 m summon distance is in reach.
|
||||
- **Locomotion direction undecided** — owner's lean: full physics thumbstick movement if
|
||||
the body track goes the Nimso way (stick pushes the physics body; sway/jog detection
|
||||
class of solutions). Anchor-grab locomotion was a scene-viewer-era design; the fix-or-
|
||||
@@ -422,8 +474,15 @@ Floor StaticBody mask 1|3|4|5 (collision_mask 29).
|
||||
| `LaserMinDistance` | Locomotion.cs | 0.35 m | laser hidden nearer than this (tip touch instead) |
|
||||
| `MarkerNearDistance` | Locomotion.cs | 1.2 m | near markers hide + become untargetable |
|
||||
| Fade times | Locomotion.cs | 0.12 s in / 0.25 s out | teleport fade |
|
||||
| `PressContactDistance` / `PressCooldownMs` | WorldPanel.cs | 0.045 m / 400 ms | knob press on contact |
|
||||
| `DirectGrabRadius` / `TipHoverRadius` / `TipLength` | WorldPanel.cs | 0.45 / 0.07 / 0.12 m | panel grab / tip hover / stylus reach |
|
||||
| `LaserStart` | Locomotion.cs | 0.12 m | beam starts at the stylus tip; beam length = hit distance − this |
|
||||
| `DirectGrabRadius` | WorldPanel.cs | 0.45 m | direct grip-drag range + `IsBlocking` range |
|
||||
| `SummonDistance` / `HeadRecallDistance` / `HeadRecallDrop` | WorldPanel.cs | 0.3 / 0.45 / 0.12 m | menu placement on summon (ahead of hand) / on Return to spawn (ahead of + below head) |
|
||||
| `SizeMeters` / `SizePixels` | MenuScreen.cs | 0.36×0.24 m / 960×640 | menu panel size / SubViewport resolution (~2.7 px/mm) |
|
||||
| `TipLength` | MenuScreen.cs | 0.12 m | stylus tip ahead of the controller = touch cursor |
|
||||
| `HoverDepth` / `PushThroughDepth` / `TouchEdgeMargin` | MenuScreen.cs | 0.06 / 0.08 / 0.015 m | touch-cursor zone in front of / behind / around the panel |
|
||||
| `ContactDepth` / `ReleaseDepth` | MenuScreen.cs | 0.006 / 0.018 m | touch press / release hysteresis |
|
||||
| `ScrollDeadzone` / `ScrollNotchesPerSecond` | MenuScreen.cs | 0.3 / 14 | thumbstick → wheel scrolling |
|
||||
| `FontSize` / `ButtonHeight` | MainMenu.cs | 26 px / 64 px | menu text and button size |
|
||||
| `MaximumRefreshRate` | Main.cs | 90 | physics tick = chosen refresh rate |
|
||||
|
||||
## 7. Next steps (in order)
|
||||
@@ -437,8 +496,8 @@ Floor StaticBody mask 1|3|4|5 (collision_mask 29).
|
||||
2-anchor based), simultaneous two-hand movement acts weird, teleport + lasers broken
|
||||
(experiments, not current issues). Owner's candidate replacement: physics thumbstick
|
||||
movement if the body track lands. Fix-or-replace rides on item 1's outcome; teleport/
|
||||
laser machinery gets resurrected-or-deleted with it (WorldPanel's laser-hover knobs
|
||||
must decouple first if deleted).
|
||||
laser machinery gets resurrected-or-deleted with it (the menu's laser cursor is
|
||||
self-contained in MenuScreen; only its drawn beam lives in `Locomotion.UpdatePointer`).
|
||||
3. **Grab points on special objects** — grab is palm-proximity to the prop origin today;
|
||||
special objects may want authored grab anchors (see `research/vr-grabbing.md` taxonomy).
|
||||
4. **Body Stage 2b (feel-driven)** — after the owner feels the 2a arms: couple obstruction
|
||||
@@ -449,8 +508,9 @@ Floor StaticBody mask 1|3|4|5 (collision_mask 29).
|
||||
6. **Neck/head look system** — `LookAtModifier3D` head→neck distribution (needs a real skeleton).
|
||||
7. **EMBODYING** — view-follow + avatar remapping (chest calibration transform); experiments only
|
||||
after head/neck look override exists. Theory until proven.
|
||||
8. **UI milestone** — deliberate full action-map pass + SubViewport/composition-layer decision for
|
||||
dense screens (at most ONE such screen; everything else stays props).
|
||||
8. **UI milestone** — the ONE SubViewport screen now exists (in-VR menu, `MenuScreen`); remaining:
|
||||
owner playtest of it, deliberate full action-map pass, fingertip (hand tracking) feeding the same
|
||||
`MenuScreen.PointerInput` as the stylus, `SubViewportCompositionLayer` only if sharpness demands.
|
||||
|
||||
## 8. Working agreements
|
||||
|
||||
|
||||
Reference in new issue
Block a user