Skip to content

60-play: Q1 Play here + Q5 reference image underlays - #433

Merged
AlexZ005 merged 3 commits into
feat/60from
feat/60-play
Oct 23, 2026
Merged

AlexZ005 merged 3 commits into
feat/60from
feat/60-play

Conversation

@AlexZ005

Copy link
Copy Markdown
Collaborator

Lane 60-play (roadmap 60 quick wins: Q1 + Q5). Base v1.41.0, PR into feat/60.

Q1: Play here

Starts Play where the editor camera is: X/Z under the camera, standing on whatever surface is below it, facing the view. Stop restores the camera pose, the orbit target and the selection. If nothing is under the camera it uses the scene's spawn, and with no spawn either it uses the camera's own eye. The override lasts one round.

  • Entry points: Ctrl+Shift+Enter; a "Play here" row in Play's right-click menu and phone long-press menu; an optional toolbar roster button (playhere, via Customize); phone More ▸ Tools & View; viewport menu Tools ▸ Play here; VR radial Tools ▸ Play here / Back to edit.
  • playHereCore.js (pure, raycast + heading), playHere.js (snapshot/restore). playSpawn gets a session-only override that currentSpawn() reads first. The VR walker reads currentSpawn().
  • Height follows the scene's locomotion. A grounded scene (the default) keeps its own eye height. With flying allowed, or a walk controller with physics running, the player stands on the surface.

Q5: Reference images

Drop an image on the viewport and pick Use as reference. It becomes a locked, unpickable, see-through plane, either on the ground or facing the view.

  • Tools ▸ Reference images… has controls for opacity, size, height, placement, show/hide and remove.
  • Hidden in Play and Interact. It sits on the editor helper layer outside sceneObjects, so it is never in GLTF, .tpscene, Export or Publish. Viewport and room thumbnails render without the helper layer.
  • Kept per scene on this device (IndexedDB, underlays:v1). Share with session sends it to your room, where peers see it read-only with one writer per row (the owner). It leaves when the owner unshares, removes it or disconnects.
  • Wire: underlay / underlaydelete are ROOM_SCOPED and validated. getunderlays is a full-state request on the cloudHooks floor. No format change.

Tests

  • unit: playHere (16), underlays (6), phoneBar updated. Full unit suite: 2512 green.
  • e2e: play-here (desktop + N6 folded phone), play-here-iwer, underlay (39 checks: drop, unpickable with a counterfactual ray, opacity, live render vs thumbnail, hidden in Play, export exclusion, peer share/follow/unshare, reload, late joiner, remove). All pass.
  • svelte-check ratchet 299/39 (= baseline); npm run build green.

Known and left

  • An underlay shared in scene A stays drawn for peers in A after the owner travels to B, because the room gate withholds the delete.
  • Session and room thumbnails no longer show light helpers either; they now follow the same rule as the gallery capture.

🤖 Generated with Claude Code

AlexZ005 and others added 3 commits October 23, 2026 15:52
…ngs the view back

The player starts where the editor camera is: X/Z under it, the feet on the first solid
surface straight below (scene meshes on the shared BVH trees, then the physics ground
plane; hidden objects, sensors, water and reference images do not count), facing the
view's heading. Nothing below → the scene's spawn; no spawn either → the camera's eye.
A session-only override that currentSpawn() returns first, so desktop Play, the VR
walker, api.respawnPlayer and the game shell all honour it for that round; leaving Play
clears it and restores the editor camera, its orbit target and the selection.

Entry points: Ctrl+Shift+Enter, a row in Play's right-click / phone long-press menu, an
optional toolbar roster button (Customize), phone More ▸ Tools & View, the viewport
menu's Tools, and the VR radial Tools ▸ Play here / Back to edit (Interact from where
you stand; Edit restores the exact reference space and world grab).

Height follows the scene's locomotion: desktop Play is grounded by default (37 R24 pins
the eye), and the VR walker stands on real surfaces only while a simulation runs.

Tests: unit playHere (the spawn raycast through a real three scene), e2e play-here
(desktop + phone N6 folded) and play-here-iwer (IWER, applied-offset spy).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rop menu (untested)

underlays.js (LOCAL per scene in IndexedDB; Share with session = underlay /
underlaydelete / getunderlays, owner-only rows, ROOM_SCOPED, request on the floor,
validated), UnderlayLayer (under the world rig, outside sceneObjects, on the editor
helper layer, hidden in Play/Interact, no-op raycast), UnderlayWindow (opacity, size,
height, ground / facing the view, share, remove) + the image-drop 'Use as reference'
menu, viewport Tools rows. Viewport/room thumbnails now render without the helper
layer. NOT yet run: svelte-check ratchet after the type fix, the underlay e2e suite.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… grid, build-safe store cast, suites

Completes the Q5 WIP (7585d83). Fixes found by the new suite: each image's
block in Tools ▸ Reference images was a single 36px cell of the toolbox body's
button grid, so every slider measured 0px wide (now grid-column: 1 / -1, the
.tbx-row rule); the $peers JSDoc cast inside a chain (which breaks the rolldown
build) is typed through its own $derived. debugHooks gains underlays +
underlayPose.

Tests: unit underlays (normalizer + aspect, 6); e2e underlay (39, ALL PASS):
image-only drop → Use as reference on the ground; plane outside sceneObjects on
the helper layer; a ray through it misses (an identical plane hits) and a click
selects nothing; opacity slider drives the material; the live render shows it,
the viewport thumbnail does not; hidden in Play; GLTF export + .tpscene payload
carry none of it; B sees nothing until Share, then follows A's opacity
read-only, unshare drops it; reload keeps it; a later joiner gets it from the
full-state reply; Remove takes it from everyone. npm run build green, ratchet
299/39 = baseline, unit 2512 green.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AlexZ005
AlexZ005 merged commit e32dc08 into feat/60 Oct 23, 2026
9 checks passed
@AlexZ005
AlexZ005 deleted the feat/60-play branch October 23, 2026 23:08
@AlexZ005

Copy link
Copy Markdown
Collaborator Author

In main via 60-int: released in v1.44.0 (#476).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant