102 lines
5.5 KiB
Markdown
102 lines
5.5 KiB
Markdown
# Pages
|
||
|
||
Every HTML page the exhibit serves, and what it's for.
|
||
|
||
**Production base URL: `https://newburynights.hideawaygaming.com.au/`**
|
||
|
||
**Keep this current.** When a page is added, renamed, or removed, update this
|
||
file in the same commit — it's the index there is, since there's no `/admin/`
|
||
landing page (admin URLs must be typed in full).
|
||
|
||
## Visitor
|
||
|
||
| Path | Page | What it does |
|
||
|------|------|--------------|
|
||
| `/` | Exhibit | Landing screen → camera AR view. The thing visitors use. Tracking HUD appears in dev mode. |
|
||
|
||
## Admin
|
||
|
||
Open in `EXHIBIT_MODE=dev`; password-protected in `EXHIBIT_MODE=final`
|
||
(`ADMIN_PASSWORD` in `.env`).
|
||
|
||
| Path | Page | What it does |
|
||
|------|------|--------------|
|
||
| `/admin/layout.html` | Layout Editor (3D) | Marker anchors, building occluders, spawn points, wander paths, table size. Save/load named layouts. |
|
||
| `/admin/playlist.html` | Playlist | Tracks (set, spawn timing, concurrency, priority), residents, behaviour tuning, time windows. |
|
||
| `/admin/characters.html` | Characters | Per-ghost visuals: model, part placement, gradient colours, opacity, height, scale, face/torso textures. |
|
||
| `/admin/landing.html` | Landing Page | Visitor landing screen: title, logo, background, accent colour, about text, disclaimer. |
|
||
| `/admin/preview.html` | Virtual Viewport | Live orbit view of whatever ghosts are currently active, straight off the same WebSocket feed as the exhibit. Ground truth when the AR view disagrees. |
|
||
| `/admin/testsheet.html` | A3 Test Rig | Printable A3 calibration/evaluation sheet, and a one-click load of the matching scene + playlist. See below. |
|
||
| `/admin/plan.html` | Placement Plan | Measured plan for physically positioning crests and models on the real table. |
|
||
| `/admin/print.html` | Print Crests | Generates printable ArUco markers ("Newbury Crests") at the configured `sizeMM`. |
|
||
| `/admin/calibrate.html` | Calibrate | Camera/marker calibration helper. Detects crests **without** filtering against the scene, so it's the quickest way to prove a printed crest is readable at all. |
|
||
| `/admin/errors.html` | Client Errors | Errors reported by visitor devices (JS errors, unhandled rejections, ghost build and scene-fetch failures). First stop when something works on one phone but not another. |
|
||
| `/admin/login.html` | Login | Only relevant in final mode. |
|
||
|
||
## The A3 test rig
|
||
|
||
`/admin/testsheet.html` is a self-contained test environment for evaluating
|
||
tracking, occlusion and lighting without the LEGO town in the way.
|
||
|
||
- **Print A3 landscape at 100% ("Actual size"), never "Fit to page."** Check the
|
||
100 mm scale bar first: if it doesn't measure 100 mm, every crest is the wrong
|
||
size and every pose is scaled with it.
|
||
- The sheet *is* the table — A3 landscape is exactly 42.0 × 29.7 cm. Origin is
|
||
the front-left corner, matching the exhibit's world frame.
|
||
- **6 crests** at 60 mm, spread 38.7 cm corner to corner (the spread is what
|
||
makes the joint solve well-conditioned).
|
||
- **Cylinder occluder**: stand an 85 mm ⌀ × 160 mm tall object on the dashed
|
||
circle at centre.
|
||
- **Static ghost** at (21, 22) cm, deliberately *behind* the cylinder from the
|
||
front — walk around it and the ghost should disappear behind the post.
|
||
- **Walking ghost** on an 18 × 12 cm rectangular loop around the cylinder;
|
||
60 cm perimeter, ~40 s per lap at the default 1.5 cm/s.
|
||
- "Load this layout onto the server" writes both the scene and the playlist,
|
||
built from the same constants that drew the sheet, so print and config can
|
||
never drift apart. It **replaces** the live scene and playlist — save your
|
||
current layout first.
|
||
|
||
Known gap: the layout editor doesn't understand cylinder occluders, so opening
|
||
and saving this layout there will drop the cylinder.
|
||
|
||
## Useful API endpoints
|
||
|
||
Read-only ones are handy to open directly in a phone browser while debugging;
|
||
the rest need auth in final mode.
|
||
|
||
| Endpoint | Returns |
|
||
|----------|---------|
|
||
| `/api/info` | Mode (`dev`/`final`) and build info |
|
||
| `/api/scene` | Live scene: anchors, spawns, paths, buildings, table |
|
||
| `/api/playlist` | Live playlist config (tracks, residents, behaviours) |
|
||
| `/api/ghosts` | Full ghost roster + colour gradients |
|
||
| `/api/characters` | Per-ghost visual overrides |
|
||
| `/api/models` | Uploaded OBJ model manifest |
|
||
| `/api/landing` | Landing page content |
|
||
| `/api/layouts` | Saved named layouts |
|
||
| `/api/client-errors` | Reported client errors (auth in final mode) |
|
||
| `/ws` | WebSocket: `scene`, `active`, `spawn`, `despawn`, `characters`, `pong` |
|
||
|
||
## Gotchas worth remembering
|
||
|
||
**Detection filters against the active scene.** A crest that isn't an anchor in
|
||
the current layout is detected and then discarded, which used to look exactly
|
||
like "the camera can't see anything".
|
||
|
||
- The scene is fetched over HTTP at start, so a down or flapping WebSocket no
|
||
longer breaks tracking.
|
||
- The HUD reports `scene N anchors` (or `NOT LOADED`) and `markers 1/2 seen
|
||
[0,7]` — usable over raw — so a filtered-out crest is visible at a glance.
|
||
- The tracking chip says "Crest N not in this layout" rather than "Point at a
|
||
Newbury Crest" when this happens.
|
||
|
||
**Motion amounts live on the server.** `playlist.js` sends explicit `bobAmp`,
|
||
`radius`, `speed` values with every spawn, so the client's defaults never apply
|
||
to a real ghost. Tune motion in the playlist config (`behaviors.bobAmp`,
|
||
`wanderRadius`, `pathSpeed`), not in `behavior.js`.
|
||
|
||
**An upstream proxy can override cache headers.** The app sends `no-cache` for
|
||
JS and HTML, but openresty in front has been seen replacing it with a daily
|
||
`expires`. If a deploy doesn't reach a device, check the response headers and
|
||
test in a private tab.
|