@genex-ai/cli-demo 0.68.0 → 0.70.0-dev.182
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +366 -15
- package/package.json +2 -1
- package/templates/controllers/character/vrm/vrm-loader.ts +74 -11
- package/templates/controllers/quality/governor.ts +147 -0
- package/templates/controllers/quality/pick-asset.ts +57 -0
- package/templates/controllers/quality/tier.ts +170 -0
- package/templates/skills/genex-ai-menu/SKILL.md +7 -1
- package/templates/skills/genex-ai-skybox/SKILL.md +15 -4
- package/templates/skills/genex-ai-texture/SKILL.md +1 -1
- package/templates/skills/genex-ai-video/SKILL.md +1 -1
- package/templates/skills/genex-explore/SKILL.md +1 -1
- package/templates/skills/genex-getting-started/SKILL.md +2 -2
- package/templates/skills/genex-threejs-adaptive-quality/SKILL.md +141 -0
- package/templates/skills/genex-threejs-adaptive-quality/references/adaptive-quality.md +105 -0
- package/templates/skills/genex-threejs-bloom/SKILL.md +4 -1
- package/templates/skills/genex-threejs-bloom/references/bloom.md +1 -1
- package/templates/skills/genex-threejs-camera-direction/SKILL.md +48 -8
- package/templates/skills/genex-threejs-camera-direction/references/camera-rigs.md +62 -0
- package/templates/skills/genex-threejs-embed-auth/SKILL.md +4 -1
- package/templates/skills/genex-threejs-game-feel/SKILL.md +4 -1
- package/templates/skills/genex-threejs-game-ui/SKILL.md +42 -8
- package/templates/skills/genex-threejs-image-pipeline/SKILL.md +5 -0
- package/templates/skills/genex-threejs-image-pipeline/references/image-pipeline.md +1 -1
- package/templates/skills/genex-threejs-lighting-design/SKILL.md +5 -1
- package/templates/skills/genex-threejs-multiplayer/SKILL.md +7 -1
- package/templates/skills/genex-threejs-multiplayer/references/host-physics.md +6 -3
- package/templates/skills/genex-threejs-physics-rapier/references/colliders-from-assets.md +1 -0
- package/templates/skills/genex-threejs-screen-space-ambient-occlusion/references/ambient-occlusion.md +1 -1
- package/templates/skills/genex-threejs-shadow-systems/SKILL.md +6 -0
- package/templates/skills/genex-threejs-shadow-systems/references/shadow-systems.md +1 -1
- package/templates/skills/genex-threejs-skill-router/SKILL.md +26 -5
- package/templates/skills/genex-threejs-skill-router/references/routing-map.md +21 -7
- package/templates/skills/genex-threejs-spectral-ocean/references/spectral-ocean.md +1 -1
- package/templates/skills/genex-threejs-touch-controls/SKILL.md +11 -0
- package/templates/skills/genex-threejs-visual-validation/SKILL.md +29 -12
- package/templates/skills/genex-threejs-water-optics/references/water-optics.md +1 -1
- package/templates/skills/genex-updates/SKILL.md +1 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Genex adaptive quality — tiers, knobs, and the governor in depth
|
|
2
|
+
|
|
3
|
+
Use this reference when tuning the tier ladder, deciding which knob may change
|
|
4
|
+
when, or teaching the governor a game-specific step.
|
|
5
|
+
|
|
6
|
+
## Why boot-conservative
|
|
7
|
+
|
|
8
|
+
The phone budget is a hard ceiling that includes GPU memory (textures,
|
|
9
|
+
framebuffers), and the OS kill arrives with no catchable event. Boot is the
|
|
10
|
+
danger window: skybox + models + post targets decode together. So phone tiers
|
|
11
|
+
START one notch below what the heuristics suggest and the governor steps UP
|
|
12
|
+
after ~20 smooth seconds. The cost of guessing low is moments of softness; the
|
|
13
|
+
cost of guessing high is a dead page.
|
|
14
|
+
|
|
15
|
+
## The tier ladder
|
|
16
|
+
|
|
17
|
+
| Knob | phone-low | phone | desktop |
|
|
18
|
+
|---|---|---|---|
|
|
19
|
+
| DPR cap | 1.0 | 1.5 | 2 |
|
|
20
|
+
| antialias (context) | off | off | on |
|
|
21
|
+
| Shadow map | 512 (static-cached) | 1024 | 2048 |
|
|
22
|
+
| Post level | tone map only | + FXAA/vignette | full named stack |
|
|
23
|
+
| Skybox rung | @2048 (~11 MB) | @4096 (~45 MB) | original |
|
|
24
|
+
| Texture rung (props) | @1024 | @2048 | original |
|
|
25
|
+
| Particles/scatter | 0.25× | 0.5× | 1× |
|
|
26
|
+
| Draw distance | 0.5× | 0.75× | 1× |
|
|
27
|
+
| Frame target | stable 30 | 60 | 60 |
|
|
28
|
+
| Remote avatars animated | 4 | 8 | all |
|
|
29
|
+
| Prop colliders | hull/cuboid | hull | as designed |
|
|
30
|
+
|
|
31
|
+
A DPR drop from 3 (raw iPhone) to 1.5 cuts every full-screen surface — color,
|
|
32
|
+
depth, and each post target — to a quarter of the bytes. It is the single
|
|
33
|
+
strongest lever the tier owns.
|
|
34
|
+
|
|
35
|
+
## The knob split — what may change when
|
|
36
|
+
|
|
37
|
+
Getting this wrong produces silent no-ops or a session stuck ugly:
|
|
38
|
+
|
|
39
|
+
- **Context-creation-fixed (never changes live):** `antialias`, `alpha`,
|
|
40
|
+
`stencil`, `powerPreference` on WebGL. Changing them means a new context and
|
|
41
|
+
a full re-init — the tier must decide them BEFORE the renderer exists.
|
|
42
|
+
(WebGPU differs: MSAA is per-render-target sample count and is runtime-
|
|
43
|
+
changeable.)
|
|
44
|
+
- **Load-time (fixed for the session once fetched):** asset rungs (skybox and
|
|
45
|
+
texture resolutions), model LOD sets. `pickAsset` decides them from the tier
|
|
46
|
+
at load; switching later means a re-fetch — treat as fixed.
|
|
47
|
+
- **Runtime-free (the governor's domain):** `setPixelRatio`, post passes on/off
|
|
48
|
+
and their target resolutions, shadow map size (realloc), draw distance and
|
|
49
|
+
fog, LOD bias, particle counts, mixer update rates, frame cap, remote-avatar
|
|
50
|
+
animation count.
|
|
51
|
+
|
|
52
|
+
## Governor mechanics
|
|
53
|
+
|
|
54
|
+
- Slow = frame delta over budget for a SUSTAINED window (4 s) — never single
|
|
55
|
+
spikes, which are usually shader compiles or GC. Precompiling with
|
|
56
|
+
`renderer.compileAsync` during the loader screen removes most spikes at the
|
|
57
|
+
source (and keeps first-frame jank from reading as a stall to the platform's
|
|
58
|
+
telemetry).
|
|
59
|
+
- Step-down order: DPR ×0.8 → post off → draw distance ×0.6 → 30 fps cap.
|
|
60
|
+
Each step is the cheapest remaining lever with the biggest headroom return.
|
|
61
|
+
- Step-up needs 20 smooth seconds (hysteresis), and a step that had to be
|
|
62
|
+
re-applied twice is pinned for the session — oscillating quality reads worse
|
|
63
|
+
than stable-low.
|
|
64
|
+
- The governor never stops: thermal throttling degrades phones after minutes
|
|
65
|
+
of play, so a boot-time benchmark alone always ends up wrong.
|
|
66
|
+
- A stable 30 fps cap beats a stuttery 40–50: consistent frame pacing reads
|
|
67
|
+
smoother and halves GPU work per second (heat, battery, memory bandwidth).
|
|
68
|
+
- Backgrounded tab (`visibilitychange`): pause the render loop and audio, not
|
|
69
|
+
just the governor — a hidden game burning GPU is pure thermal debt on the
|
|
70
|
+
device class that can least afford it.
|
|
71
|
+
|
|
72
|
+
## Detection honesty
|
|
73
|
+
|
|
74
|
+
- Apple devices mask the GPU renderer string ("Apple GPU") — screen dims + DPR
|
|
75
|
+
+ iOS major version are the usable signals there, and the governor corrects
|
|
76
|
+
the rest from measured frames.
|
|
77
|
+
- Android exposes real renderer strings (Adreno/Mali/Xclipse); the vendored
|
|
78
|
+
lookup in `tier.ts` promotes strong GPUs to the `phone` tier. It is a
|
|
79
|
+
heuristic on purpose — extend the regex when field data shows a
|
|
80
|
+
misclassified family, and let the governor absorb the rest.
|
|
81
|
+
- Never burn a probe context on a memory-strapped phone at play time; the one
|
|
82
|
+
probe in `tier.ts` runs at boot and frees its context immediately.
|
|
83
|
+
|
|
84
|
+
## Memory discipline that rides the tier
|
|
85
|
+
|
|
86
|
+
- Dispose on every level swap: traverse the outgoing scene and call
|
|
87
|
+
`.dispose()` on geometry, material, AND each material's textures — material
|
|
88
|
+
dispose does not free textures, and three frees nothing automatically.
|
|
89
|
+
- Watch `renderer.info.memory.{textures,geometries}` across swaps in dev; a
|
|
90
|
+
monotonic climb is a leak marching toward the OS kill. The governor
|
|
91
|
+
publishes these counts for the platform's field telemetry.
|
|
92
|
+
- Prefer meshopt/instanced geometry for repeats; `BatchedMesh` batches
|
|
93
|
+
HETEROGENEOUS static meshes into one draw where instancing (identical
|
|
94
|
+
meshes only) can't.
|
|
95
|
+
- Half-resolution transparency: render heavy particle/transparency passes to a
|
|
96
|
+
half-size target and composite up — fill-rate is the phone bottleneck.
|
|
97
|
+
|
|
98
|
+
## Multiplayer at tier
|
|
99
|
+
|
|
100
|
+
Remotes are visual-only; with the shared avatar file, `loadVrmClone` gives N
|
|
101
|
+
remotes one set of GPU geometry/textures. Animate and fully draw only the
|
|
102
|
+
nearest `tier.remoteAvatarCap`; beyond it, freeze the mixer and billboard or
|
|
103
|
+
hide. Matchmade games can also declare a lower `maxPlayers` in
|
|
104
|
+
`genex.matchmaking` for phone-heavy audiences — capacity is a server-owned
|
|
105
|
+
knob.
|
|
@@ -32,7 +32,10 @@ reference before adding selective bloom to a composed scene.
|
|
|
32
32
|
- selective bloom requires mutating scene materials every frame without restoration guarantees;
|
|
33
33
|
- transparent particles disappear from extraction because pass ownership is unclear;
|
|
34
34
|
- bloom radius changes wildly with resolution;
|
|
35
|
-
- highlights become gray because energy is clamped too early
|
|
35
|
+
- highlights become gray because energy is clamped too early;
|
|
36
|
+
- bloom ships un-tiered: phone tiers run the light post level
|
|
37
|
+
(`$genex-threejs-adaptive-quality`) — bloom is a desktop-tier pass, and its
|
|
38
|
+
full-res HDR target is exactly the allocation phones get killed for.
|
|
36
39
|
|
|
37
40
|
## Routing boundary
|
|
38
41
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Use this reference to choose bloom ownership, signal order, selective contribution, and scene-relative emissive ranges without making bloom responsible for the underlying form.
|
|
4
4
|
|
|
5
|
-
> **Renderer note:** this reference assumes `WebGPURenderer` + TSL node materials. Check the project's actual renderer first — the Genex scaffold ships vanilla WebGL three.js. On WebGL, adapt the technique with standard materials / `EffectComposer` passes or pick a simpler alternative; never switch renderers mid-project.
|
|
5
|
+
> **Renderer note:** this reference assumes `WebGPURenderer` + TSL node materials. Check the project's actual renderer first — the Genex scaffold ships vanilla WebGL three.js. On WebGL, adapt the technique with standard materials / `EffectComposer` passes or pick a simpler alternative; never switch renderers mid-project. Either way the effect obeys the device tier (`$genex-threejs-adaptive-quality`): expensive passes are desktop-tier, and on WebGPU the per-target MSAA sample count is a runtime knob the governor may drive.
|
|
6
6
|
|
|
7
7
|
## Contents
|
|
8
8
|
|
|
@@ -35,9 +35,12 @@ rules, floating-origin shot, pointer controls, and implementation limits.
|
|
|
35
35
|
|
|
36
36
|
## Aiming and pointer lock
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
The rule is binary: during play, **the cursor is either a gameplay tool or it is
|
|
39
|
+
locked away**. An OS arrow parked over the action in a game that never uses it is
|
|
40
|
+
a shipped defect, not a default. On the bundled `FollowCamera`, pointer-lock aim
|
|
41
|
+
is ON by default on desktop — a click locks the pointer and raw mouse movement
|
|
42
|
+
drives the view — so mostly you decide whether to turn it OFF. Name the bucket in
|
|
43
|
+
the build plan:
|
|
41
44
|
|
|
42
45
|
- **MANDATORY** — first-person of any kind (FPS, walking sim, horror) and any
|
|
43
46
|
mouse-aimed action (third-person shooter, turret/range). Lock is on by default;
|
|
@@ -47,11 +50,16 @@ it on — you decide whether to turn it OFF. Name the bucket in the build plan:
|
|
|
47
50
|
`genex controller character` game). On by default; leave it on. Opt out with
|
|
48
51
|
`pointerLockAim: false` only for a stated reason (a cursor-heavy UI at the core
|
|
49
52
|
of play).
|
|
50
|
-
- **
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
- **Keyboard-driven games lock too** — a racer, platformer, or runner that never
|
|
54
|
+
reads the mouse still locks the pointer on the play/Start click: the cursor is
|
|
55
|
+
not a tool there, so lock it away (hidden cursor, no stray clicks, Esc = pause
|
|
56
|
+
as usual). Bundled-controller games get this for free; a hand-rolled game uses
|
|
57
|
+
the minimal lock recipe in `$genex-threejs-game-ui` (~6 lines).
|
|
58
|
+
- **NEVER** — cursor-core games where the pointer IS the gameplay tool (top-down
|
|
59
|
+
click-to-move, tower defense, builders, card/board/puzzle) and spectator/orbit
|
|
60
|
+
showcases. These **must pass `pointerLockAim: false`** — otherwise the bundled
|
|
61
|
+
camera grabs the cursor on the first click. (Touch needs nothing: pointer lock
|
|
62
|
+
doesn't exist there and the mode no-ops on coarse pointers.)
|
|
55
63
|
|
|
56
64
|
**Mechanism — games on the bundled controller (most games):** do NOT hand-roll
|
|
57
65
|
lock handling. Aim is already enabled; the kit ships a ready-made cue overlay —
|
|
@@ -113,6 +121,36 @@ grants the permission.
|
|
|
113
121
|
(trackpad "natural scrolling" only flips the wheel). If look feels inverted it's a
|
|
114
122
|
sign bug in the rig, not a device quirk — fix the sign, don't sniff the trackpad.
|
|
115
123
|
|
|
124
|
+
## The screen-direction contract
|
|
125
|
+
|
|
126
|
+
Every input axis has one correct on-screen direction, for every rig, bundled or
|
|
127
|
+
hand-rolled. These four invariants are testable and non-negotiable:
|
|
128
|
+
|
|
129
|
+
1. Mouse/touchpad RIGHT turns the view right; mouse UP looks up (down only behind
|
|
130
|
+
an explicit invert option the player chose).
|
|
131
|
+
2. `KeyD`/ArrowRight moves or turns the player toward screen-RIGHT; `KeyA`/
|
|
132
|
+
ArrowLeft toward screen-left. Same for a touch stick's +x.
|
|
133
|
+
3. Drag-pan picks ONE convention — grab-the-world (terrain follows the pointer)
|
|
134
|
+
or move-the-camera — and BOTH axes obey it. One axis each is the
|
|
135
|
+
"diagonals feel twisted" bug.
|
|
136
|
+
4. See the OS-setting note above: inversion is always your sign, never the device.
|
|
137
|
+
|
|
138
|
+
The formula that settles every sign argument: `screenRight = cross(cameraForward, worldUp)`.
|
|
139
|
+
For a Y-up world and forward `(sin yaw, 0, cos yaw)`, screen-right is
|
|
140
|
+
`(-cos yaw, 0, sin yaw)`. **Warning — `(cos yaw, 0, -sin yaw)` is the LEFT
|
|
141
|
+
vector** (that's `cross(worldUp, cameraForward)`), and writing it as "right" is
|
|
142
|
+
the single most-shipped direction bug in generated games: two independent
|
|
143
|
+
projects inverted their A/D exactly this way. Related trap: positive
|
|
144
|
+
`rotation.y` turns a +Z-facing object toward +X, which is screen-LEFT from a
|
|
145
|
+
chase camera behind it — so "positive yaw = turn right" is false in this basis.
|
|
146
|
+
|
|
147
|
+
Never derive signs by intuition — intuition about right-handed frames is wrong
|
|
148
|
+
about half the time and has been wrong in every shipped instance. Copy a
|
|
149
|
+
verified pair (sign AND basis together) from
|
|
150
|
+
[references/camera-rigs.md](references/camera-rigs.md), then confirm with the
|
|
151
|
+
input-direction part of the smoke check: hold D and watch which way the world
|
|
152
|
+
answers.
|
|
153
|
+
|
|
116
154
|
## Non-negotiable rules
|
|
117
155
|
|
|
118
156
|
- Use subject dimensions to derive offsets; do not tune one fixed distance for
|
|
@@ -122,6 +160,8 @@ sign bug in the rig, not a device quirk — fix the sign, don't sniff the trackp
|
|
|
122
160
|
- During an explicit handoff, use one interpolation stage. Do not stack a
|
|
123
161
|
transition blend and a second follow smoother over the same interval.
|
|
124
162
|
- Re-sync yaw/pitch from the camera when pointer lock is acquired.
|
|
163
|
+
- Hand-rolled steering/pan/look math copies a verified basis from the reference
|
|
164
|
+
and passes the input-direction check — signs are never derived by intuition.
|
|
125
165
|
- Update the projection matrix whenever FOV, near, far, or aspect changes.
|
|
126
166
|
- Keep stars or infinite backgrounds camera-relative when large translation
|
|
127
167
|
would create false parallax or precision loss.
|
|
@@ -11,6 +11,7 @@ Use this reference for scale-aware chase, side, orbit, authored-shot, pointer-lo
|
|
|
11
11
|
- Explicit camera handoffs
|
|
12
12
|
- cinematic implementation shot ownership
|
|
13
13
|
- Pointer-look and movement constraints
|
|
14
|
+
- Verified screen-direction bases
|
|
14
15
|
- Floating origin and background handling
|
|
15
16
|
- Projection and lifecycle ownership
|
|
16
17
|
- Failure modes and diagnostics
|
|
@@ -166,6 +167,9 @@ yaw -= mouseDeltaX * 0.0022
|
|
|
166
167
|
pitch -= mouseDeltaY * 0.0018
|
|
167
168
|
```
|
|
168
169
|
|
|
170
|
+
Expected on screen: mouse-right orbits the view right, mouse-up tilts it up —
|
|
171
|
+
verify both axes against the screen-direction contract before retuning the scales.
|
|
172
|
+
|
|
169
173
|
Pitch bounds vary by flight mode. The implementation also enforces camera height above
|
|
170
174
|
the ship:
|
|
171
175
|
|
|
@@ -281,6 +285,9 @@ distance = movementSpeed * dt
|
|
|
281
285
|
|
|
282
286
|
Default speed is `9`, sensitivity `0.0023`.
|
|
283
287
|
|
|
288
|
+
Expected on screen: mouse-right turns the view right, mouse-up looks up — assert
|
|
289
|
+
both axes (yaw-only evidence has let inverted pitch ship).
|
|
290
|
+
|
|
284
291
|
Keys are cleared on:
|
|
285
292
|
|
|
286
293
|
- pointer-lock exit;
|
|
@@ -295,6 +302,61 @@ Scene-specific constraints then run after controls:
|
|
|
295
302
|
|
|
296
303
|
Input control and spatial constraint are separate layers.
|
|
297
304
|
|
|
305
|
+
## Verified screen-direction bases
|
|
306
|
+
|
|
307
|
+
Copy these pairs whole — the sign and the basis are only correct TOGETHER. Each
|
|
308
|
+
was derived from `screenRight = cross(cameraForward, worldUp)` and verified
|
|
309
|
+
against the on-screen result; if you change one half, re-verify with the
|
|
310
|
+
input-direction check instead of reasoning about it.
|
|
311
|
+
|
|
312
|
+
**Chase-cam steering** (vehicle/character heading, camera behind):
|
|
313
|
+
|
|
314
|
+
```text
|
|
315
|
+
heading = (sin yaw, 0, cos yaw) // matches rotation.y for a +Z-front model
|
|
316
|
+
yaw -= steer * rate * dt // steer: D/right = +1, A/left = -1
|
|
317
|
+
position += heading * speed * dt
|
|
318
|
+
camera at position - heading * dist, lookAt(position)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Why the minus: the camera looks along `heading`, so screen-right is
|
|
322
|
+
`cross(heading, up) = (-cos yaw, 0, sin yaw)`, while `d(heading)/d(yaw) =
|
|
323
|
+
(cos yaw, 0, -sin yaw)` — exactly screen-LEFT. Increasing yaw always veers the
|
|
324
|
+
nose left on screen, so "D turns right" needs `yaw -=`. (Equivalently
|
|
325
|
+
`yaw += steer` is correct only with heading `(-sin yaw, 0, cos yaw)` — a pair,
|
|
326
|
+
never a lone sign.)
|
|
327
|
+
|
|
328
|
+
**RTS / overhead pan camera** (fixed pitch, yaw-orbiting):
|
|
329
|
+
|
|
330
|
+
```text
|
|
331
|
+
right = (-cos yaw, 0, sin yaw) // pitch-independent screen-right on the ground
|
|
332
|
+
forwardGround = (sin yaw, 0, cos yaw) // into the screen along the ground
|
|
333
|
+
D / ArrowRight: target += right * pan A / ArrowLeft: target -= right * pan
|
|
334
|
+
W / ArrowUp: target += forwardGround * pan S: target -= forwardGround * pan
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Drag-pan, grab-the-world (terrain follows the pointer; both axes, one
|
|
338
|
+
convention — `movementY` is positive DOWNWARD):
|
|
339
|
+
|
|
340
|
+
```text
|
|
341
|
+
target -= right * movementX * k
|
|
342
|
+
target += forwardGround * movementY * k
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Move-the-camera convention = flip BOTH signs, never one. The classic shipped bug
|
|
346
|
+
writes `right = (cos yaw, 0, -sin yaw)` — that is `cross(up, forward)`, the LEFT
|
|
347
|
+
vector — inverting A/D and the horizontal drag while W/S stay correct.
|
|
348
|
+
|
|
349
|
+
**Pointer-look** (locked mouse driving yaw/pitch, Euler order `YXZ`):
|
|
350
|
+
|
|
351
|
+
```text
|
|
352
|
+
yaw -= movementX * sensitivity // mouse-right -> view turns RIGHT
|
|
353
|
+
pitch -= movementY * sensitivity // mouse-up -> view tilts UP
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Expected on screen, both axes, before any tuning: a straight-ahead landmark
|
|
357
|
+
slides LEFT when the mouse moves right (the view pans right) and slides DOWN
|
|
358
|
+
when the mouse moves up (the view tilts up).
|
|
359
|
+
|
|
298
360
|
## Floating origin and background handling
|
|
299
361
|
|
|
300
362
|
The Saturn scene first computes a virtual camera pose, stores its orientation
|
|
@@ -162,7 +162,10 @@ From `@genex-ai/embed-sdk/sentry` (crash reporting; exactly these two):
|
|
|
162
162
|
- `sentryCanvasSnapshot(canvas)` — session replay records the DOM, not the 3D
|
|
163
163
|
canvas; call this once per frame at the END of the render loop so replays
|
|
164
164
|
show actual gameplay. Works for BOTH WebGL and WebGPU renderers; internally
|
|
165
|
-
throttled, so calling at 60fps is fine
|
|
165
|
+
throttled, so calling at 60fps is fine. On TOUCH devices it is a deliberate
|
|
166
|
+
no-op (and session replay/tracing sample down): each capture is a full-canvas
|
|
167
|
+
GPU readback, exactly the overhead phones get memory-killed for — mobile
|
|
168
|
+
replays are DOM-only by design, on-error replays still record everywhere:
|
|
166
169
|
|
|
167
170
|
```ts
|
|
168
171
|
function animate() {
|
|
@@ -37,7 +37,10 @@ no matter how good it looks.
|
|
|
37
37
|
unlocked drag-to-turn camera feels imprecise no matter how tight the numbers
|
|
38
38
|
are. The bucket rule + the bundled `FollowCamera` aim mode live in
|
|
39
39
|
`$genex-threejs-camera-direction`; on the bundled controller it's ON by default
|
|
40
|
-
(with a ready-made cue), not hand-rolled events.
|
|
40
|
+
(with a ready-made cue), not hand-rolled events. Direction is half of it: a
|
|
41
|
+
movement key or look axis whose on-screen direction contradicts its label is a
|
|
42
|
+
defect, not a tuning issue — the screen-direction contract and verified bases
|
|
43
|
+
are in `$genex-threejs-camera-direction`.
|
|
41
44
|
|
|
42
45
|
## Movement: snappy beats realistic
|
|
43
46
|
|
|
@@ -27,7 +27,7 @@ in another.
|
|
|
27
27
|
| Pause | always — opens on the Escape key | 1 — menu backdrop under a dark overlay |
|
|
28
28
|
| Fail / retry | always | 1 — a *graded* variant of the menu screen |
|
|
29
29
|
| Win / next | always | 1 — graded variant, warm |
|
|
30
|
-
| Settings |
|
|
30
|
+
| Settings | always — every game carries the Quality picker (Auto/Low/Medium/High, `$genex-threejs-adaptive-quality`), plus whatever else it has to set | 1 |
|
|
31
31
|
| Lobby / waiting | multiplayer only | 1 over the menu backdrop |
|
|
32
32
|
| HUD | always | 2 — generated sprite HUD (`$genex-ai-hud`), enqueued at this gate for EVERY game; plain CSS is the placeholder until the sprites land, never the final HUD |
|
|
33
33
|
|
|
@@ -250,8 +250,15 @@ document.addEventListener("keydown", (e) => {
|
|
|
250
250
|
so ALSO keep the pointer-lock-loss → pause path (`pointerlockchange`: if
|
|
251
251
|
unlocked while `playing`, `setPhase("paused")`). Escape then always pauses; it
|
|
252
252
|
only *also* drops fullscreen on browsers without the lock — unavoidable there.
|
|
253
|
-
- **
|
|
254
|
-
|
|
253
|
+
- **Skip Keyboard Lock + fullscreen only when the cursor stays a tool.**
|
|
254
|
+
Cursor-core / top-down / menu-driven games never capture the pointer — skip
|
|
255
|
+
`enterImmersive` and Keyboard Lock entirely. But ANY game that locks the
|
|
256
|
+
pointer during play — aim games AND keyboard-only racers/platformers/runners
|
|
257
|
+
under the lock-or-tool rule (next section) — keeps the Escape → pause path and
|
|
258
|
+
the `pointerlockchange` fallback (if unlocked while `playing`, pause): without
|
|
259
|
+
it, Esc frees the cursor while the game keeps running. Keyboard-only games may
|
|
260
|
+
still skip Keyboard Lock + fullscreen; the lock + pause/resume path is the
|
|
261
|
+
non-negotiable part.
|
|
255
262
|
- **Dashboard embed:** no setup needed — the platform's game frame grants keyboard
|
|
256
263
|
lock (and pointer lock + fullscreen), so Escape-to-pause works the same inside
|
|
257
264
|
`/world/` + `/draft/` as it does standalone (`<slug>.genex.technology`).
|
|
@@ -261,6 +268,28 @@ menu — trackpads feel slower than mice, so let the player tune it. On the bund
|
|
|
261
268
|
camera the setter is live: `slider.oninput = () => { followCam.aimSensitivity = +slider.value; };`
|
|
262
269
|
(radians per pixel; default `0.0023`, a usable range is ~`0.0008`–`0.005`).
|
|
263
270
|
|
|
271
|
+
## The cursor during play: locked or a tool
|
|
272
|
+
|
|
273
|
+
During play the OS cursor is either the gameplay tool (cursor-core: click-to-move,
|
|
274
|
+
tower defense, builders, card/board — it stays visible, that's correct) or it is
|
|
275
|
+
**locked away — including keyboard-only games** (racer, platformer, runner): an
|
|
276
|
+
arrow parked over the action for the whole session is a shipped defect. Games on
|
|
277
|
+
the bundled `FollowCamera` get the lock free (on by default). A hand-rolled game
|
|
278
|
+
locks with ~6 lines, reusing this section's Escape flow:
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
// In the Play/Start/Resume CLICK handler (lock needs a user gesture):
|
|
282
|
+
canvas.requestPointerLock?.(); // hides the cursor, focuses the game
|
|
283
|
+
// Already in the Escape recipe above: pointerlockchange → if unlocked while
|
|
284
|
+
// playing, pause; the Resume click re-locks. Keyboard games need nothing more —
|
|
285
|
+
// no reticle, no aim code; the lock just parks the cursor.
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
If the lock is genuinely unavailable (a third-party embed without
|
|
289
|
+
`allow="pointer-lock"`), fall back to hiding the idle cursor over the canvas:
|
|
290
|
+
`canvas.style.cursor = "none"` after ~2s without `pointermove`, restored
|
|
291
|
+
instantly on move. Menus and pause screens always keep their cursor.
|
|
292
|
+
|
|
264
293
|
## The loader
|
|
265
294
|
|
|
266
295
|
The loader is the first thing every player sees — a bare "Loading… 3/5" over
|
|
@@ -372,11 +401,13 @@ Order the HUD by what the player loses the game for ignoring:
|
|
|
372
401
|
`vUv` grain shimmers.
|
|
373
402
|
- **Desktop first.** Verify at desktop sizes and survive window resizes
|
|
374
403
|
without clipping; don't design phone layouts or test mobile viewports unless
|
|
375
|
-
the user asks.
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
`navigator.maxTouchPoints > 0`,
|
|
379
|
-
|
|
404
|
+
the user asks. Two exceptions ship by default precisely BECAUSE you don't
|
|
405
|
+
test on phones: touch *input* when a recipe fits — a bundled controller's
|
|
406
|
+
built-in touch controls, or the touch kit + recipes in
|
|
407
|
+
`$genex-threejs-touch-controls` — behind `navigator.maxTouchPoints > 0`,
|
|
408
|
+
invisible on desktop (skipping needs a one-line reason, not silence); and
|
|
409
|
+
the adaptive-quality tier at boot (`$genex-threejs-adaptive-quality`), which
|
|
410
|
+
keeps the shared link from being a dead OR crashing link on a phone.
|
|
380
411
|
|
|
381
412
|
## Wire UI to game state, never the reverse
|
|
382
413
|
|
|
@@ -399,6 +430,9 @@ architecture and consume the shared style brief.
|
|
|
399
430
|
- HUD sprites never enqueued at the gate — the placeholder CSS shipped as the
|
|
400
431
|
final HUD.
|
|
401
432
|
- No pause screen, or a pause that isn't bound to Escape.
|
|
433
|
+
- The OS arrow parked over the action for the whole session in a keyboard-driven
|
|
434
|
+
game (the cursor is either a gameplay tool or locked away — see the cursor
|
|
435
|
+
section).
|
|
402
436
|
- A style brief whose fonts were never actually loaded (a system-stack display
|
|
403
437
|
font at runtime).
|
|
404
438
|
- A micro-element (reticle, cue, toast, damage number) left as default CSS
|
|
@@ -43,6 +43,11 @@ ownership boundaries between whole-scene and effect-local graphs.
|
|
|
43
43
|
- Build pass toggles and effect-only views before tuning.
|
|
44
44
|
- UI rendered in the same target needs an explicit protection strategy.
|
|
45
45
|
- Do not load all atomic post skills by default. Route only the effects actually requested.
|
|
46
|
+
- Budget the pipeline per device tier (`$genex-threejs-adaptive-quality`):
|
|
47
|
+
phone pixel budget ≈ 1,000,000 px at DPR ≤ 1.25–1.5, desktop ≈ 1,650,000 px —
|
|
48
|
+
every full-res pass target multiplies that cost, so phone tiers run the light
|
|
49
|
+
post level and per-pass resolution scales (0.4–0.5 DPR blurs) are the norm,
|
|
50
|
+
not an optimization.
|
|
46
51
|
|
|
47
52
|
## Routing boundary
|
|
48
53
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Use this reference to compose shared scene buffers, lighting effects, atmosphere, bloom, exposure, tone mapping, grading, and feature-local render targets with explicit ownership.
|
|
4
4
|
|
|
5
|
-
> **Renderer note:** this reference assumes `WebGPURenderer` + TSL node materials. Check the project's actual renderer first — the Genex scaffold ships vanilla WebGL three.js. On WebGL, adapt the technique with standard materials / `EffectComposer` passes or pick a simpler alternative; never switch renderers mid-project.
|
|
5
|
+
> **Renderer note:** this reference assumes `WebGPURenderer` + TSL node materials. Check the project's actual renderer first — the Genex scaffold ships vanilla WebGL three.js. On WebGL, adapt the technique with standard materials / `EffectComposer` passes or pick a simpler alternative; never switch renderers mid-project. Either way the effect obeys the device tier (`$genex-threejs-adaptive-quality`): expensive passes are desktop-tier, and on WebGPU the per-target MSAA sample count is a runtime knob the governor may drive.
|
|
6
6
|
|
|
7
7
|
## Contents
|
|
8
8
|
|
|
@@ -107,7 +107,11 @@ kill-switch diagnostic.
|
|
|
107
107
|
whole rig after a tone-mapping change is self-inflicted.
|
|
108
108
|
- Never repair unbalanced light ratios with exposure — fix the lights.
|
|
109
109
|
- Flicker from elapsed time, never per-frame randomness.
|
|
110
|
-
- Dispose lights and their shadow maps with the level that spawned them
|
|
110
|
+
- Dispose lights and their shadow maps with the level that spawned them — and
|
|
111
|
+
the rest of the level with them: traverse the outgoing scene and dispose
|
|
112
|
+
geometry, materials, AND textures separately (three frees nothing; material
|
|
113
|
+
dispose does not touch textures). Leaked levels march phones toward the
|
|
114
|
+
OS memory kill — `$genex-threejs-adaptive-quality` owns the budget watch.
|
|
111
115
|
- The rig must read with post off: time of day and where-to-go, before bloom.
|
|
112
116
|
|
|
113
117
|
## Routing boundary
|
|
@@ -536,7 +536,13 @@ fights (many writers). A ball on `objects` glides and has one owner. That's the
|
|
|
536
536
|
`stateRaw` is the raw latest (hit-tests, discrete values). A reconnect-grace seat remains in this
|
|
537
537
|
map with `connected: false`. `avatarUrl` is that player's verified VRM pick (server-set, `''`
|
|
538
538
|
when unknown) — in a VRM-lane game render each remote with
|
|
539
|
-
`
|
|
539
|
+
`loadVrmClone(p.avatarUrl || "./assets/avatar.vrm")` (the vendored loader's shared-GPU clone:
|
|
540
|
+
N remotes share one set of geometry/textures instead of re-parsing per player — retarget
|
|
541
|
+
clips once on `loadVrmClone.base(url)` and play them on each clone's own mixer); never
|
|
542
|
+
publish avatar URLs through `me.set`. On phone tiers, animate and fully draw only the
|
|
543
|
+
nearest `tier.remoteAvatarCap` remotes (`$genex-threejs-adaptive-quality`) — freeze the
|
|
544
|
+
mixer and billboard or hide the rest; a room allows up to 64 players and 64 live avatars
|
|
545
|
+
is a real phone memory kill on its own.
|
|
540
546
|
- `room.activePlayers` — the connected-only subset of `room.players`; use its size for live quorum.
|
|
541
547
|
- `room.objects` — shared objects nobody owns until claimed (a ball, an NPC):
|
|
542
548
|
- `claim(id)` — **legacy** optimistic request. It flips local ownership immediately and is corrected
|
|
@@ -275,9 +275,12 @@ room.me.set({
|
|
|
275
275
|
|
|
276
276
|
// Remote players: a VISUAL-ONLY avatar — NO Rapier body, NO controller instance for remotes.
|
|
277
277
|
// Build each remote's visual from THEIR OWN model: in a VRM-lane game that is
|
|
278
|
-
//
|
|
279
|
-
// replicates ('' = unknown → fall back; deepDispose the
|
|
280
|
-
//
|
|
278
|
+
// loadVrmClone(pl.avatarUrl || "./assets/avatar.vrm") — the verified per-player pick the
|
|
279
|
+
// relay replicates ('' = unknown → fall back; deepDispose the clone on 'leave'). The clone
|
|
280
|
+
// shares parsed GPU geometry/textures across remotes with the same file (most rooms: all of
|
|
281
|
+
// them) — retarget clips once against loadVrmClone.base(url).vrm and drive each clone's own
|
|
282
|
+
// AnimationMixer. Never reuse your own avatar OBJECT for a remote (clones, not references).
|
|
283
|
+
// Phone tiers animate only the nearest tier.remoteAvatarCap remotes (adaptive-quality skill). Position/rotation from smoothed state; animation from the
|
|
281
284
|
// synced flags via the avatar's own update(flags, dt). The character-controller skill's
|
|
282
285
|
// animations reference owns the flag set.
|
|
283
286
|
const pl = room.players.get(id)!;
|
|
@@ -26,6 +26,7 @@ already resolved — constructing a collider before WASM init throws.
|
|
|
26
26
|
| tall dynamic props, posts | `capsuleCollider` / `cylinderCollider` | stable standing shapes |
|
|
27
27
|
| pickups, triggers, zones | any shape + `{ sensor: true, mass: 0 }` | overlap events, no contact forces |
|
|
28
28
|
| the player, cars, drones | none of the above — `npx genex controller` | controllers own their collider recipes |
|
|
29
|
+
| any prop on a PHONE tier (`$genex-threejs-adaptive-quality`) | prefer `cuboidCollider`/hull over trimesh | trimesh contacts scale with triangle count — a phone CPU/memory tax; keep trimesh for static level geometry only |
|
|
29
30
|
|
|
30
31
|
## Explicit primitive helpers
|
|
31
32
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Use this reference for a bounded-cost WebGPU/TSL ambient-visibility pass with half-resolution horizon integration, bent normals, bilateral reconstruction, and directional ambient tint.
|
|
4
4
|
|
|
5
|
-
> **Renderer note:** this reference assumes `WebGPURenderer` + TSL node materials. Check the project's actual renderer first — the Genex scaffold ships vanilla WebGL three.js. On WebGL, adapt the technique with standard materials / `EffectComposer` passes or pick a simpler alternative; never switch renderers mid-project.
|
|
5
|
+
> **Renderer note:** this reference assumes `WebGPURenderer` + TSL node materials. Check the project's actual renderer first — the Genex scaffold ships vanilla WebGL three.js. On WebGL, adapt the technique with standard materials / `EffectComposer` passes or pick a simpler alternative; never switch renderers mid-project. Either way the effect obeys the device tier (`$genex-threejs-adaptive-quality`): expensive passes are desktop-tier, and on WebGPU the per-target MSAA sample count is a runtime knob the governor may drive.
|
|
6
6
|
|
|
7
7
|
## Contents
|
|
8
8
|
|
|
@@ -12,6 +12,12 @@ and shadows shimmer, swim, or run out of coverage.
|
|
|
12
12
|
|
|
13
13
|
Use a single shadow map only when its receiver region is genuinely bounded. For large moving views, make shadow coverage an explicit spatial hierarchy.
|
|
14
14
|
|
|
15
|
+
**Phone budgets ride the quality tier** (`$genex-threejs-adaptive-quality`):
|
|
16
|
+
shadow maps ≤1024² on phones (512² on the low tier — a 4096² map alone is
|
|
17
|
+
~67 MB of the phone's whole GPU budget), at most 2 cascades where desktop runs
|
|
18
|
+
4, and `shadowMap.autoUpdate = false` for static scenes (re-render on demand:
|
|
19
|
+
a shadow pass is a full extra scene render every frame otherwise).
|
|
20
|
+
|
|
15
21
|
## Cached clipmap workflow
|
|
16
22
|
|
|
17
23
|
1. Define concentric light-space square levels.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Use this reference for stable directional shadows across a large procedural scene using committed light-space centers, texel snapping, bounded refresh budgets, cross-level blending, and targeted invalidation.
|
|
4
4
|
|
|
5
|
-
> **Renderer note:** this reference assumes `WebGPURenderer` + TSL node materials. Check the project's actual renderer first — the Genex scaffold ships vanilla WebGL three.js. On WebGL, adapt the technique with standard materials / `EffectComposer` passes or pick a simpler alternative; never switch renderers mid-project.
|
|
5
|
+
> **Renderer note:** this reference assumes `WebGPURenderer` + TSL node materials. Check the project's actual renderer first — the Genex scaffold ships vanilla WebGL three.js. On WebGL, adapt the technique with standard materials / `EffectComposer` passes or pick a simpler alternative; never switch renderers mid-project. Either way the effect obeys the device tier (`$genex-threejs-adaptive-quality`): expensive passes are desktop-tier, and on WebGPU the per-target MSAA sample count is a runtime knob the governor may drive.
|
|
6
6
|
|
|
7
7
|
## Contents
|
|
8
8
|
|
|
@@ -15,11 +15,12 @@ map, execution order, and acceptance gate.
|
|
|
15
15
|
|
|
16
16
|
| Work needed | Load |
|
|
17
17
|
| --- | --- |
|
|
18
|
-
| shot composition, chase/side/orbit rigs, camera handoffs, projection ownership, pointer look, mouse-aimed action (shooter, FPS/first-person, sniper, turret, crosshair/reticle), mouse-look, floating origins | `$genex-threejs-camera-direction` |
|
|
18
|
+
| shot composition, chase/side/orbit rigs, camera handoffs, projection ownership, pointer look, mouse-aimed action (shooter, FPS/first-person, sniper, turret, crosshair/reticle), mouse-look, hand-rolled steering/pan/look input signs (screen-direction contract), floating origins | `$genex-threejs-camera-direction` |
|
|
19
19
|
| on-foot player movement: walk/run/jump/crouch, third-person character, slopes, stairs, moving platforms, personal VRM animation, directional locomotion, transitions, action motion | `$genex-threejs-character-controller` |
|
|
20
20
|
| a custom generated playable humanoid or Meshy animation coverage beyond UAL: reference-informed A-pose concepts, explicit candidate + 10k-remesh approvals, exact action IDs, same-rig adapter | `$genex-ai-character` + `$genex-threejs-character-controller` |
|
|
21
21
|
| the player drives or flies something: cars, drones, vehicle physics, gearbox, enter/exit between character and vehicle | `$genex-threejs-vehicle-controllers` |
|
|
22
22
|
| playable on phones: touch/mobile input for any game — joystick, virtual buttons, drag zones, per-genre touch recipes, rotate-device overlay — wired by default for every NEW game when a recipe fits (skip with a one-line reason) | `$genex-threejs-touch-controls` |
|
|
23
|
+
| phone-survivable rendering — device tiers, DPR/shadow/post budgets, the runtime quality governor, per-tier asset rungs for generated skyboxes/textures, the Quality picker, dispose-on-swap discipline — **mandatory for every game at boot wiring** | `$genex-threejs-adaptive-quality` |
|
|
23
24
|
| anything falls, collides, gets pushed, or needs physics: Rapier world setup, colliders for meshes and GLBs, collision events | `$genex-threejs-physics-rapier` |
|
|
24
25
|
| launch and docking timelines, procedural transform phases, springs, staging, rotating-frame alignment, debris motion | `$genex-threejs-procedural-animation` |
|
|
25
26
|
| reusable scalar/vector fields, domain warping, causal masks, procedural normals | `$genex-threejs-procedural-fields` |
|
|
@@ -58,6 +59,14 @@ map, execution order, and acceptance gate.
|
|
|
58
59
|
mentions sign-in, saves, progress, per-player state, a persistent world, or
|
|
59
60
|
leaderboards. Multiplayer auth (`getColyseusAuth`) comes from it too.
|
|
60
61
|
|
|
62
|
+
**Adaptive quality is mandatory routing:** every game wires the device tier at
|
|
63
|
+
boot — `$genex-threejs-adaptive-quality` (three lines: `detectTier()` before the
|
|
64
|
+
renderer, tier-capped `setPixelRatio`, the governor in the loop; generated
|
|
65
|
+
skyboxes/textures load through their rungs). Phones enforce a hard GPU-memory
|
|
66
|
+
kill desktops never show you; the tier is what keeps a phone boot alive while
|
|
67
|
+
desktop keeps the full look. This is a completion gate like the post stack —
|
|
68
|
+
cheap to wire, not a new testing burden (desktop-only verification stands).
|
|
69
|
+
|
|
61
70
|
**Character-animation routing:** use the existing VRM + UAL character
|
|
62
71
|
controller by default. Use `npx genex character` when the game needs a custom
|
|
63
72
|
generated humanoid or an action unavailable in UAL. Before generating a Meshy
|
|
@@ -122,7 +131,11 @@ stack this game ships — read off the concept frame's OWN look (the grade, bloo
|
|
|
122
131
|
level, haze, grain it already shows) and which post the 2–3 AAA references lean
|
|
123
132
|
on, not a default single bloom; ONE built render-pass effect is the FLOOR
|
|
124
133
|
against no-post for EVERY game, never the target — ship the richness the concept
|
|
125
|
-
implies
|
|
134
|
+
implies. The floor is TIER-AWARE (`$genex-threejs-adaptive-quality`): on phone
|
|
135
|
+
tiers it is satisfied by the built tone-mapping/output pass with the light
|
|
136
|
+
additions the tier allows (FXAA/vignette), while the full named stack remains
|
|
137
|
+
the desktop floor — never ship the heavy stack undropped to phones (the UI
|
|
138
|
+
vignette div or a CSS canvas filter does not count on any tier;
|
|
126
139
|
`$genex-threejs-image-pipeline` owns ordering when 2+ compose), a decision for
|
|
127
140
|
**every primitive surface** the game builds — walls, barriers, kerbs and
|
|
128
141
|
platforms each get a real texture or a **shader** where that surface wants motion
|
|
@@ -204,8 +217,16 @@ concept-driven — a richer first build beats a grey-box one.
|
|
|
204
217
|
drag-orbit (`pointerLockAim: false`) only with a stated reason (e.g. a
|
|
205
218
|
cursor-heavy UI core). **Never** — cursor-core games (click-to-move, tower
|
|
206
219
|
defense, builder, card/puzzle), orbit showcases, touch-only; these MUST pass
|
|
207
|
-
`pointerLockAim: false`.
|
|
208
|
-
|
|
220
|
+
`pointerLockAim: false`. Keyboard-only games (racer, platformer) lock too —
|
|
221
|
+
the cursor is either a gameplay tool or locked away during play. The mechanism
|
|
222
|
+
and the full aim contract live in `$genex-threejs-camera-direction`.
|
|
223
|
+
- Input direction (always — every game that moves with keys or pointer):
|
|
224
|
+
D/ArrowRight must move or turn the player screen-RIGHT, mouse-right must turn
|
|
225
|
+
the view right, drag-pan axes share one convention. The screen-direction
|
|
226
|
+
contract and verified copy-paste bases live in
|
|
227
|
+
`$genex-threejs-camera-direction` — hand-rolled steering/pan/look math copies
|
|
228
|
+
one instead of deriving signs, and the smoke check's input-direction pass
|
|
229
|
+
verifies it.
|
|
209
230
|
- Art direction follows THIS game's concept. The style examples inside skills
|
|
210
231
|
are examples, not defaults — never default to neon/cyberpunk/synthwave (or any
|
|
211
232
|
other single register) unless the concept calls for it.
|
|
@@ -222,7 +243,7 @@ concept-driven — a richer first build beats a grey-box one.
|
|
|
222
243
|
- Use `$genex-threejs-visual-validation` before declaring graphics/procedural-system
|
|
223
244
|
work done. **Game fast path:** for game tasks that loaded no procedural/visual-system
|
|
224
245
|
skill, done = a screenshot plus an interaction smoke check (load the page, press each
|
|
225
|
-
control, see the visible response) — don't run the full diagnostic gate — PLUS the
|
|
246
|
+
control, see the visible response in its labeled direction) — don't run the full diagnostic gate — PLUS the
|
|
226
247
|
floors from the routing-map's acceptance gate: the generated sprite HUD wired in
|
|
227
248
|
(not the CSS placeholder), pause on Escape, the branded loader with its key-art
|
|
228
249
|
background, the brief's fonts actually loaded, the renderer baseline + at least one
|