@genex-ai/cli-demo 0.93.0-dev.247 → 0.94.0-dev.249

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.
Files changed (53) hide show
  1. package/dist/index.js +25 -4
  2. package/package.json +1 -1
  3. package/templates/skills/genex-ai-model/SKILL.md +3 -2
  4. package/templates/skills/genex-ai-skybox/SKILL.md +3 -3
  5. package/templates/skills/genex-game-director/SKILL.md +321 -411
  6. package/templates/skills/genex-game-director/references/design-contract.md +130 -179
  7. package/templates/skills/genex-game-director/references/routing-map.md +242 -362
  8. package/templates/skills/genex-threejs-exposure-color-grading/SKILL.md +3 -7
  9. package/templates/skills/genex-threejs-procedural-assets/SKILL.md +112 -0
  10. package/templates/skills/genex-threejs-procedural-materials/SKILL.md +3 -7
  11. package/templates/skills/genex-threejs-procedural-vfx/SKILL.md +6 -11
  12. package/templates/skills/genex-threejs-shadow-systems/SKILL.md +2 -3
  13. package/templates/skills/genex-threejs-visual-validation/SKILL.md +0 -4
  14. package/templates/skills/genex-threejs-atmosphere-aerial-perspective/SKILL.md +0 -46
  15. package/templates/skills/genex-threejs-atmosphere-aerial-perspective/references/atmosphere.md +0 -213
  16. package/templates/skills/genex-threejs-bloom/SKILL.md +0 -44
  17. package/templates/skills/genex-threejs-bloom/references/bloom.md +0 -187
  18. package/templates/skills/genex-threejs-exposure-color-grading/references/exposure-grading.md +0 -205
  19. package/templates/skills/genex-threejs-game-content/SKILL.md +0 -198
  20. package/templates/skills/genex-threejs-game-content/references/content-tables.md +0 -269
  21. package/templates/skills/genex-threejs-image-pipeline/SKILL.md +0 -56
  22. package/templates/skills/genex-threejs-image-pipeline/references/image-pipeline.md +0 -197
  23. package/templates/skills/genex-threejs-lighting-design/SKILL.md +0 -144
  24. package/templates/skills/genex-threejs-lighting-design/references/light-recipes.md +0 -137
  25. package/templates/skills/genex-threejs-open-world/SKILL.md +0 -149
  26. package/templates/skills/genex-threejs-open-world/references/terrain-streaming.md +0 -215
  27. package/templates/skills/genex-threejs-precipitation-surfaces/SKILL.md +0 -59
  28. package/templates/skills/genex-threejs-precipitation-surfaces/references/precipitation-surfaces.md +0 -181
  29. package/templates/skills/genex-threejs-procedural-architecture/SKILL.md +0 -50
  30. package/templates/skills/genex-threejs-procedural-architecture/references/architecture-systems.md +0 -509
  31. package/templates/skills/genex-threejs-procedural-fields/SKILL.md +0 -61
  32. package/templates/skills/genex-threejs-procedural-fields/references/field-systems.md +0 -232
  33. package/templates/skills/genex-threejs-procedural-geometry/SKILL.md +0 -39
  34. package/templates/skills/genex-threejs-procedural-geometry/references/mesh-systems.md +0 -202
  35. package/templates/skills/genex-threejs-procedural-materials/references/material-systems.md +0 -198
  36. package/templates/skills/genex-threejs-procedural-planets/SKILL.md +0 -48
  37. package/templates/skills/genex-threejs-procedural-planets/references/planet-systems.md +0 -498
  38. package/templates/skills/genex-threejs-procedural-vegetation/SKILL.md +0 -52
  39. package/templates/skills/genex-threejs-procedural-vegetation/references/vegetation-systems.md +0 -314
  40. package/templates/skills/genex-threejs-procedural-vfx/references/vfx-systems.md +0 -218
  41. package/templates/skills/genex-threejs-raymarched-space-effects/SKILL.md +0 -37
  42. package/templates/skills/genex-threejs-raymarched-space-effects/references/space-effects.md +0 -194
  43. package/templates/skills/genex-threejs-screen-space-ambient-occlusion/SKILL.md +0 -35
  44. package/templates/skills/genex-threejs-screen-space-ambient-occlusion/references/ambient-occlusion.md +0 -441
  45. package/templates/skills/genex-threejs-spectral-ocean/SKILL.md +0 -52
  46. package/templates/skills/genex-threejs-spectral-ocean/references/spectral-ocean.md +0 -473
  47. package/templates/skills/genex-threejs-temporal-surfaces/SKILL.md +0 -41
  48. package/templates/skills/genex-threejs-temporal-surfaces/references/temporal-surfaces.md +0 -207
  49. package/templates/skills/genex-threejs-visual-validation/references/visual-validation.md +0 -427
  50. package/templates/skills/genex-threejs-volumetric-clouds/SKILL.md +0 -49
  51. package/templates/skills/genex-threejs-volumetric-clouds/references/volumetric-clouds.md +0 -579
  52. package/templates/skills/genex-threejs-water-optics/SKILL.md +0 -57
  53. package/templates/skills/genex-threejs-water-optics/references/water-optics.md +0 -195
@@ -1,269 +0,0 @@
1
- # Genex game content — copy-paste systems
2
-
3
- Four small modules that turn contract lines into shipped content: an event
4
- bus, a quest engine, a dialogue walker with quest hooks, and items/economy +
5
- XP. Copy them, fill the tables, and keep content OUT of engine code — adding
6
- a quest, an item, or a vendor must always be a table edit.
7
-
8
- All snippets are plain vanilla-ts (no enums, no decorators) and assume a
9
- single shared game-state object `G` you already have (player stats, wallet,
10
- the scene). Adapt names, not shapes.
11
-
12
- ## 1. Event bus — the only coupling
13
-
14
- ```ts
15
- // events.ts — combat, loot, and movement EMIT; quests and UI SUBSCRIBE.
16
- type GameEvent =
17
- | { type: "enemy:died"; enemyKind: string }
18
- | { type: "item:pickup"; item: string; count: number }
19
- | { type: "chest:opened"; chestId: string }
20
- | { type: "npc:talked"; npc: string }
21
- | { type: "zone:entered"; location: string };
22
-
23
- type Handler = (e: GameEvent) => void;
24
- const handlers: Handler[] = [];
25
-
26
- export function onEvent(h: Handler): void {
27
- handlers.push(h);
28
- }
29
- export function emit(e: GameEvent): void {
30
- for (const h of handlers) h(e);
31
- }
32
- ```
33
-
34
- ## 2. Quest engine — defs are data, progress is events
35
-
36
- ```ts
37
- // quests.ts
38
- import { onEvent } from "./events.ts";
39
-
40
- type Objective =
41
- | { kind: "kill"; target: string; count: number }
42
- | { kind: "collect"; item: string; count: number }
43
- | { kind: "reach"; location: string }
44
- | { kind: "talk"; npc: string };
45
-
46
- type QuestDef = {
47
- id: string;
48
- giver: string;
49
- prereq?: string;
50
- title: string;
51
- brief: string;
52
- turnIn: string; // authored prose for the hand-in moment
53
- objective: Objective;
54
- rewards: { gold?: number; xp?: number; items?: string[] };
55
- };
56
-
57
- // CONTENT — the whole quest list lives here. Main chain via prereq; the
58
- // prose is authored, not generated filler. Counts must match the contract.
59
- export const QUESTS: QuestDef[] = [
60
- {
61
- id: "q_wolves",
62
- giver: "elder",
63
- title: "Teeth in the Tall Grass",
64
- brief:
65
- "Wolves have taken the shepherd's flock and now they circle the palisade at dusk. Thin the pack before someone's child is next.",
66
- turnIn: "Five pelts. The flock sleeps easy tonight — and so do we. Take this.",
67
- objective: { kind: "kill", target: "wolf", count: 5 },
68
- rewards: { gold: 40, xp: 60 },
69
- },
70
- {
71
- id: "q_amulet",
72
- giver: "elder",
73
- prereq: "q_wolves",
74
- title: "What the Crypt Keeps",
75
- brief:
76
- "Our founder's amulet lies in the old crypt east of the fields. The dead have grown restless around it — bring it back, and mind the narrow dark.",
77
- turnIn: "The amulet… after all these years. You have the village's gratitude, and its coin.",
78
- objective: { kind: "collect", item: "founder_amulet", count: 1 },
79
- rewards: { gold: 80, xp: 120, items: ["potion_health"] },
80
- },
81
- // …side quests: no prereq, different givers, different objective kinds.
82
- ];
83
-
84
- type QuestStatus = "locked" | "available" | "active" | "ready" | "done";
85
- type QuestState = { status: QuestStatus; progress: number };
86
-
87
- export const questState = new Map<string, QuestState>();
88
- for (const q of QUESTS) {
89
- questState.set(q.id, { status: q.prereq ? "locked" : "available", progress: 0 });
90
- }
91
-
92
- const def = (id: string): QuestDef => QUESTS.find((q) => q.id === id)!;
93
-
94
- export function accept(id: string): void {
95
- const s = questState.get(id)!;
96
- if (s.status !== "available") return;
97
- s.status = "active";
98
- // Contract check "can't dead-end": count pre-acceptance progress where the
99
- // fiction allows it (kills already made), or reset knowingly.
100
- }
101
-
102
- export function turnIn(id: string): { gold: number; xp: number; items: string[] } | null {
103
- const s = questState.get(id)!;
104
- if (s.status !== "ready") return null;
105
- s.status = "done";
106
- for (const q of QUESTS) {
107
- if (q.prereq === id && questState.get(q.id)!.status === "locked") {
108
- questState.get(q.id)!.status = "available"; // next chain link opens
109
- }
110
- }
111
- const r = def(id).rewards;
112
- return { gold: r.gold ?? 0, xp: r.xp ?? 0, items: r.items ?? [] };
113
- }
114
-
115
- function bump(q: QuestDef, s: QuestState, amount = 1): void {
116
- const needed = "count" in q.objective ? q.objective.count : 1;
117
- s.progress = Math.min(needed, s.progress + amount);
118
- if (s.progress >= needed) s.status = "ready"; // journal + marker flip to "return"
119
- }
120
-
121
- onEvent((e) => {
122
- for (const q of QUESTS) {
123
- const s = questState.get(q.id)!;
124
- if (s.status !== "active") continue;
125
- const o = q.objective;
126
- if (o.kind === "kill" && e.type === "enemy:died" && e.enemyKind === o.target) bump(q, s);
127
- if (o.kind === "collect" && e.type === "item:pickup" && e.item === o.item) bump(q, s, e.count);
128
- if (o.kind === "reach" && e.type === "zone:entered" && e.location === o.location) bump(q, s);
129
- if (o.kind === "talk" && e.type === "npc:talked" && e.npc === o.npc) bump(q, s);
130
- }
131
- });
132
- ```
133
-
134
- Journal UI, the tracked-quest compass marker, and toasts subscribe to the
135
- same state — inventory them as screen elements at `$genex-threejs-game-ui`'s
136
- gate. Persist `[...questState]` (plus world side-effects like opened chests)
137
- in the per-player slot via `$genex-threejs-embed-auth`.
138
-
139
- ## 3. Dialogue walker — quest states become options
140
-
141
- ```ts
142
- // dialogue.ts
143
- import { QUESTS, questState, accept, turnIn } from "./quests.ts";
144
-
145
- type DialogueOption = { label: string; next?: DialogueNode; action?: () => void };
146
- type DialogueNode = { text: string; options: DialogueOption[] };
147
-
148
- // CONTENT — per-NPC roots: greeting + a couple of authored lore branches.
149
- // Three real lines is the difference between a person and a signpost.
150
- const ROOTS: Record<string, DialogueNode> = {
151
- elder: {
152
- text: "Maren watches the road as she talks. “Strangers used to mean trade. Lately they mean trouble.”",
153
- options: [
154
- {
155
- label: "Tell me about this village.",
156
- next: {
157
- text: "“Three generations behind this palisade. The crypt east of here is older than all of it — and lately, louder.”",
158
- options: [],
159
- },
160
- },
161
- ],
162
- },
163
- // blacksmith, herbalist… — every giver and merchant has a root.
164
- };
165
-
166
- // The whole walker: render node.text + numbered options; a click (or the
167
- // 1–9 key) runs option.action?.(), then shows option.next or closes. Emit
168
- // { type: "npc:talked", npc } when a conversation OPENS — that's what "talk"
169
- // objectives listen for.
170
- export function pickOption(node: DialogueNode, index: number): DialogueNode | null {
171
- const opt = node.options[index];
172
- if (!opt) return node;
173
- opt.action?.();
174
- return opt.next ?? null; // null = close the dialogue panel
175
- }
176
-
177
- export function openDialogue(npc: string): DialogueNode {
178
- const root = ROOTS[npc];
179
- const options = [...root.options];
180
- for (const q of QUESTS) {
181
- if (q.giver !== npc) continue;
182
- const s = questState.get(q.id)!;
183
- if (s.status === "available") {
184
- options.unshift({
185
- label: `[Quest] ${q.title}`,
186
- next: {
187
- text: q.brief,
188
- options: [
189
- { label: "I'll do it.", action: () => accept(q.id) },
190
- { label: "Not now." },
191
- ],
192
- },
193
- });
194
- } else if (s.status === "active") {
195
- options.unshift({ label: `[${q.title}] Remind me.`, next: { text: q.brief, options: [] } });
196
- } else if (s.status === "ready") {
197
- options.unshift({
198
- label: `[Complete] ${q.title}`,
199
- next: { text: q.turnIn, options: [] },
200
- action: () => {
201
- const r = turnIn(q.id);
202
- // …grant r.gold / r.xp / r.items through your wallet + inventory.
203
- },
204
- });
205
- }
206
- }
207
- return { text: root.text, options };
208
- }
209
- ```
210
-
211
- ## 4. Items, merchants, XP — the economy that closes
212
-
213
- ```ts
214
- // items.ts
215
- type ItemDef = {
216
- id: string;
217
- kind: "weapon" | "armor" | "potion" | "quest" | "tome";
218
- name: string;
219
- price: number; // what merchants charge — the SINK side of the economy
220
- stats?: { dmg?: number; armor?: number; heal?: number };
221
- };
222
-
223
- export const ITEMS: ItemDef[] = [
224
- { id: "sword_iron", kind: "weapon", name: "Iron Sword", price: 120, stats: { dmg: 18 } },
225
- { id: "potion_health", kind: "potion", name: "Health Draught", price: 25, stats: { heal: 40 } },
226
- { id: "founder_amulet", kind: "quest", name: "Founder's Amulet", price: 0 },
227
- // …the catalog. Price the first upgrade to land right after the first quest's gold.
228
- ];
229
-
230
- // Merchant stock is per-NPC data — a second vendor is one more entry.
231
- export const STOCKS: Record<string, string[]> = {
232
- blacksmith: ["sword_iron", "armor_leather"],
233
- herbalist: ["potion_health", "potion_mana"],
234
- };
235
-
236
- // XP curve + level rewards: one growth axis, wired to quest/enemy rewards.
237
- export const xpNext = (level: number): number => Math.round(100 * level ** 1.4);
238
- export function addXp(player: { level: number; xp: number; maxHp: number; hp: number }, amount: number): void {
239
- player.xp += amount;
240
- while (player.xp >= xpNext(player.level)) {
241
- player.xp -= xpNext(player.level);
242
- player.level += 1;
243
- player.maxHp += 12;
244
- player.hp = player.maxHp; // level-up heals — a reward the player FEELS
245
- }
246
- }
247
- ```
248
-
249
- ## Seeded placement — bulk content for free
250
-
251
- Scatter the non-authored bulk (chests, camps, flavor spawns) with a seeded
252
- RNG so the world is deterministic across loads and machines:
253
-
254
- ```ts
255
- export function seededRng(seed: number): () => number {
256
- let s = seed >>> 0;
257
- return () => {
258
- s = (s * 1664525 + 1013904223) >>> 0;
259
- return s / 0xffffffff;
260
- };
261
- }
262
- // const rng = seededRng(1337): place 10 chests at rng()-driven offsets around
263
- // locations, skip water/steep slopes via the world's height/biome lookups
264
- // ($genex-threejs-open-world), and hand-place only the authored few.
265
- ```
266
-
267
- Hand-authored where it matters (quest prose, boss mechanics, unique loot),
268
- tables for the bulk, seeds for the scatter — that split is what lets one
269
- session ship the whole contract.
@@ -1,56 +0,0 @@
1
- ---
2
- name: genex-threejs-image-pipeline
3
- description: Design the final image pipeline for Genex Three.js games. Use for render-target ownership, depth and normal signals, albedo/history buffers, pass ordering, post-processing composition, AO, bloom, exposure, tone mapping, grading, diagnostics, and avoiding conflicting render effects.
4
- ---
5
-
6
- # Genex Three.js Image Pipeline
7
-
8
- Use this skill only when composing several image-space systems or defining shared buffers. For one effect, load its atomic skill instead.
9
-
10
- Load:
11
-
12
- - `$genex-threejs-screen-space-ambient-occlusion` for GTAO, bent normals, denoising, or AO application;
13
- - `$genex-threejs-bloom` for HDR extraction and bloom;
14
- - `$genex-threejs-exposure-color-grading` for metering, adaptation, tone mapping, LUTs, and output conversion.
15
-
16
- The pipeline must expose its signals and ordering. Do not install a pile of effects and tune the final frame blindly.
17
-
18
- ## Signal order
19
-
20
- ```text
21
- scene HDR color + depth + normals + albedo where required
22
- → lighting-related screen effects
23
- → atmosphere/transparency composition
24
- → bloom
25
- → exposure
26
- → tone mapping
27
- → grading
28
- → lens/presentation effects
29
- → output conversion
30
- ```
31
-
32
- Read [references/image-pipeline.md](references/image-pipeline.md)
33
- for four production pass graphs, their buffer/resolution contracts, and the
34
- ownership boundaries between whole-scene and effect-local graphs.
35
-
36
- ## Rules
37
-
38
- - Tone-map once.
39
- - Keep HDR bloom before tone mapping.
40
- - Meter exposure from a small luminance target, not the final 8-bit screen.
41
- - Separate direct and indirect light before applying bent-normal ambient tint when possible.
42
- - Upsample low-resolution effects with depth/normal-aware weights.
43
- - Build pass toggles and effect-only views before tuning.
44
- - UI rendered in the same target needs an explicit protection strategy.
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.
51
-
52
- ## Routing boundary
53
-
54
- Use this skill when multiple image-space systems must share buffers, ordering,
55
- or output ownership. For one isolated effect, use its atomic skill without
56
- loading this coordinator.
@@ -1,197 +0,0 @@
1
- # Production image-pipeline contracts
2
-
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
-
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
-
7
- ## Contents
8
-
9
- - production WebGPU pipeline WebGPU graph
10
- - selective gallery pipeline selective gallery graph
11
- - atlas-based renderer composer graph
12
- - Temporal-surface effect-local graph
13
- - Buffer and ownership rules
14
- - Resolution policies
15
- - Failure analysis
16
- - Diagnostics
17
-
18
-
19
- ## production WebGPU pipeline WebGPU graph
20
-
21
- When GTAO is enabled, the scene pass writes MRT:
22
-
23
- ```text
24
- output HDR color
25
- view-space normal
26
- diffuse albedo
27
- depth
28
- ```
29
-
30
- Graph:
31
-
32
- ```text
33
- scene MRT
34
- -> reduced GTAO + bent normal
35
- -> full-resolution bilateral/lighting composite
36
- -> atmosphere
37
- -> bloom
38
- -> adapted exposure
39
- -> renderOutput / tone map
40
- -> 3D LUT
41
- -> FXAA
42
- ```
43
-
44
- Near/far values, environment intensity, and environment texture remain
45
- updateable inputs. AO is applied through its dedicated composite rather than
46
- blindly multiplying the final image.
47
-
48
- The atmosphere pass reconstructs view/world position from depth,
49
- classifies sky, and owns aerial haze, height fog, sun disc/shaft, lens flare,
50
- and distance grading. Its optional post-process cloud shadow is explicitly
51
- disabled in its configuration, avoiding duplicate ownership with material
52
- lighting.
53
-
54
- ## selective gallery pipeline selective gallery graph
55
-
56
- The gallery owns three composers:
57
-
58
- ```text
59
- neon selective bloom
60
- chandelier selective bloom
61
- base + final additive composite + OutputPass
62
- ```
63
-
64
- Selective renders use layer membership plus temporary black material
65
- substitution with `try/finally` restoration. CSS3D content is rendered by a
66
- separate renderer after WebGL when invalidated.
67
-
68
- Shadows use VSM and manual invalidation:
69
-
70
- ```text
71
- shadowMap.autoUpdate = false
72
- shadowMap.needsUpdate = true only after relevant scene/light changes
73
- ```
74
-
75
- This is a bounded-scene optimization. It depends on every moving caster and
76
- light correctly invalidating the cache.
77
-
78
- ## atlas-based renderer composer graph
79
-
80
- atlas-based renderer performs a separate depth prepass into a depth-stencil target before
81
- the composer:
82
-
83
- ```text
84
- depth prepass target
85
- main render
86
- SSAO
87
- volumetric lighting
88
- bloom
89
- lens flare
90
- fog/color grading
91
- ```
92
-
93
- The depth target uses nearest filtering and a
94
- `DepthStencilFormat`/`UnsignedInt248Type` depth texture. Every depth consumer
95
- receives that same texture.
96
-
97
- The composer recalculates effective pixel dimensions from renderer pixel ratio
98
- and resizes the depth target and all passes together.
99
-
100
- The composer can exist alongside another application post path. Verify the
101
- actual render-loop call path before claiming that this graph owns runtime
102
- output.
103
-
104
- ## Temporal-surface effect-local graph
105
-
106
- The temporal frost graph is not a whole-scene post stack. It is a
107
- self-contained material effect:
108
-
109
- ```text
110
- scene at full resolution
111
- -> vertical blur at 0.4 DPR
112
- -> horizontal blur at 0.4 DPR
113
- -> frost composite at full resolution
114
- -> pointer history write/swap at full resolution
115
- -> final normal/refraction output
116
- ```
117
-
118
- Three static procedural texture targets render once. This is a feature-local
119
- example of mixing persistent, static, low-resolution, and full-resolution
120
- signals in one feature.
121
-
122
- ## Buffer and ownership rules
123
-
124
- Before implementation, write:
125
-
126
- | Signal | Producer | Consumers | Space/format | Resolution | History |
127
- | --- | --- | --- | --- | --- | --- |
128
- | HDR scene | scene pass | AO/atmosphere/bloom | linear HDR | full | no |
129
- | depth | scene or prepass | AO/fog/flare | renderer-defined | full | no |
130
- | normal | MRT/geometry | AO composite | view space | full | no |
131
- | albedo | MRT | indirect composite | linear | full | no |
132
- | bloom contributions | selective passes | final composite | HDR | full/pyramid | no |
133
- | exposure | meter | final color | scalar | 64x36 source | adapted |
134
- | interaction | ping-pong pass | frost/output | half-float | full | yes |
135
-
136
- Every signal has one producer. If a scene pass already owns depth and normal,
137
- do not add an uncoordinated duplicate prepass without measuring the reason.
138
-
139
- ## Resolution policies
140
-
141
- selective gallery pipeline caps DPR from both device and pixel budget:
142
-
143
- ```text
144
- mobile budget = 1,000,000 pixels, max DPR 1.25
145
- desktop budget = 1,650,000 pixels, max DPR 1.5
146
- minimum DPR = 1
147
- budget DPR = sqrt(pixelBudget / CSS pixel count)
148
- ```
149
-
150
- All composers receive the same selected DPR and CSS size.
151
-
152
- The temporal frost graph instead gives individual passes fixed roles:
153
-
154
- ```text
155
- blur and coarse noise = 0.4 DPR
156
- composite, history, output = display DPR
157
- ```
158
-
159
- Choose global DPR budgeting for scene cost and per-pass scaling for effect
160
- bandwidth. They solve different problems.
161
-
162
- ## Failure analysis
163
-
164
- - production WebGPU pipeline API names are version-sensitive; `PostProcessing` was renamed
165
- and deprecated in favor of `RenderPipeline` in current Three.js history.
166
- - selective gallery pipeline selective bloom renders the scene multiple times.
167
- - selective gallery pipeline manual shadow invalidation can freeze unregistered motion.
168
- - atlas-based renderer depth prepass renders regular scene materials, not an explicit depth
169
- override; verify transparent and alpha-tested behavior.
170
- - atlas-based renderer composer may not be the active runtime path.
171
- - The frost blur has a zero-weight division risk and pointer decay is frame
172
- based.
173
- - None of these graphs provides a complete velocity/motion-vector
174
- contract for general temporal effects.
175
- - Do not advertise velocity ownership or TAA merely because a generic pipeline
176
- could include them.
177
-
178
- ## Diagnostics
179
-
180
- Expose a graph inspector or equivalent stable views:
181
-
182
- ```text
183
- scene HDR
184
- depth raw and reconstructed
185
- normal/albedo MRT
186
- GTAO and bent normal
187
- atmosphere only
188
- each selective bloom contribution
189
- exposure meter and current exposure
190
- pre/post tone map and LUT
191
- frost static/history/composite targets
192
- pass resolution, format, memory, and GPU time
193
- manual invalidation state
194
- ```
195
-
196
- The pipeline is accepted only when every enabled pass has a named input,
197
- output, owner, resolution, and disable path.
@@ -1,144 +0,0 @@
1
- ---
2
- name: genex-threejs-lighting-design
3
- description: Light Genex Three.js games from visible causes, and decide WHERE light belongs — walk the scene's sources one by one, the way the visual-direction gate walks surfaces and the moment gate walks moments. Use for light rigs, sun/moon key lights, practical lights (campfire, torch, neon, lava), emissive-to-light coupling, light shafts and visible beams, fog mood, gameplay light signals, flicker, light budgets, and scenes that read flat or uniformly lit.
4
- ---
5
-
6
- # Genex Three.js Lighting Design
7
-
8
- Light from causes — every light in the frame is the visible consequence of
9
- something the player can point at. Avoid the uniform ambient wash that lights
10
- a cave like an office.
11
-
12
- ## The source gate — run this BEFORE writing any lighting code
13
-
14
- The visual-direction gate walks **surfaces**; the moment gate walks
15
- **moments**. Light lives on neither — it lives on **sources**: what the sky
16
- pours in and what the fiction burns, glows, or screens. Walk the sources the
17
- same way, and decide each one.
18
-
19
- List the sources the scene contract already names — never invent them. Three
20
- places to look:
21
-
22
- - **the sky** — day, night, overcast, underground, deep space? That one answer
23
- sets the rig: a warm sun key with cool sky fill, a dim hard moon, or no sky
24
- at all — a cave is keyed by its practicals;
25
- - **the fiction's emitters** — campfire, torch, lava, neon sign, a monitor,
26
- headlights, a portal, a crack of daylight in the roof;
27
- - **the gameplay signals** — the objective beacon, the telegraph, the
28
- checkpoint. Light is information the player navigates by, and it is the
29
- cheapest wayfinding you own.
30
-
31
- When gameplay needs light where the fiction names no source, add the cause
32
- first — a lamp prop, embers, a fissure — and then light it; the light alone is
33
- still a bug. Then judge each source:
34
-
35
- > **Does it change what the player sees AROUND it — on the ground, the walls,
36
- > the player — or does it only need to be seen itself?**
37
-
38
- Four verdicts, all legitimate, one forbidden:
39
-
40
- - **Seen itself** — an emissive material, no light object. Most sources land
41
- here, and that is what keeps the frame cheap; how much it glows is
42
- `$genex-threejs-bloom`'s question.
43
- - **Lights its surroundings** — a real `PointLight` or `SpotLight`, coupled to
44
- its emitter (below). A campfire that doesn't paint the ground orange is a
45
- prop, not a fire.
46
- - **Shapes the whole frame** — the key: a sun or moon `DirectionalLight`, the
47
- arena floods. One or two per scene, and they own the shadows.
48
- - **The air itself is lit** — the beam is the point: the cave crack, a dusty
49
- window shaft, canopy rays. Build the shaft as geometry first — fog and
50
- volumetric passes are scene-wide decisions, never per-beam tools (recipes
51
- in the reference).
52
- - **Not deciding — the only wrong answer.** One white `AmbientLight` over
53
- everything is not a rig; it is the unlit look with extra steps, and it is
54
- why shipped scenes read the same at noon and at midnight.
55
-
56
- "The neon is emissive-only — it doesn't reach the street" is a real answer;
57
- say it in one line and move on. Then check the inverse: **a light with no
58
- visible cause reads as a bug**, not mood — the player asks why the floor
59
- glows. And when the walk returns more real lights than the scene can afford,
60
- demote the dimmest back to emissive-only: dynamic lights are the scarcest
61
- resource in the frame, and the eye forgives an unlit distant torch far sooner
62
- than a dropped frame.
63
-
64
- ## A practical is coupled, not placed
65
-
66
- The light a source throws and the mesh that emits it are one thing. Drive both
67
- from one envelope — separate flickers, or a light hovering near an unlit prop,
68
- read instantly as fake:
69
-
70
- ```ts
71
- // Values assume a locked renderer baseline — relationships, not mandates.
72
- const fire = new THREE.PointLight(0xff9142, 14, 18, 2); // range-capped, physical falloff
73
- fire.position.set(0, 0.9, 0); // inside the flame, above the fuel
74
- campfire.add(fire);
75
- const flameMat = flame.material as THREE.MeshStandardMaterial;
76
- flameMat.emissive.set(0xff7a1e);
77
-
78
- function flicker(t: number): number {
79
- // two incommensurate sines — coherent, frame-rate independent
80
- return 0.82 + 0.12 * Math.sin(t * 11.3) + 0.06 * Math.sin(t * 23.7 + 1.7);
81
- }
82
-
83
- function updateFire(elapsed: number): void {
84
- const e = flicker(elapsed);
85
- fire.intensity = 14 * e;
86
- flameMat.emissiveIntensity = 2.4 * e; // mesh and light breathe together
87
- fire.position.x = 0.05 * Math.sin(elapsed * 7.1); // the pool sways with the flame
88
- }
89
- ```
90
-
91
- The same couple serves a neon sign that hums, a monitor whose picture spills
92
- onto the desk, a muzzle flash (one envelope drives light, emissive, and the
93
- sound cue). And when `npx genex skybox` has set `scene.environment`, that
94
- image IS the ambient fill — do not stack an `AmbientLight` on top of it.
95
-
96
- Read [references/light-recipes.md](references/light-recipes.md) for rig
97
- baselines per environment (day, night, interior, cave, space), the shaft and
98
- fog recipes, light-as-information patterns, budget relationships, and the
99
- kill-switch diagnostic.
100
-
101
- ## Rules
102
-
103
- - Every light names its cause; a light nothing explains is a bug, not mood.
104
- - **The light population is fixed at scene build — never add, remove, or hide
105
- a light (or a group containing one) during play.** Three.js counts lights
106
- into every lit material's shader; a changed count recompiles ALL of them
107
- synchronously — a multi-second freeze on the exact frame a pickup, door, or
108
- death "removes" a light (a real pilot game froze ~3 s per relic collected
109
- this way). Turn a light off by driving `intensity` to 0 and leave it in the
110
- graph; per-level swaps rebuild the whole scene anyway, which is the one
111
- legitimate time the population changes.
112
- - Couple a practical to its emitter: one color, one envelope, one on/off state.
113
- - The key owns shadows; practicals cast none until a shot proves they must.
114
- - **A shadow-casting practical that never moves gets a FROZEN map**: a
115
- PointLight shadow is a 6-face cube render, re-drawn every frame by default —
116
- four static lanterns cost 24 shadow passes/frame over your densest props for
117
- zero visual change (a pilot game submitted 66M triangles/frame this way).
118
- Set `light.shadow.autoUpdate = false; light.shadow.needsUpdate = true;` so
119
- the cube renders once; keep only the key (which shadows the movers) dynamic,
120
- and budget frozen practicals at ≤1024 map size.
121
- - Tune intensities only after the renderer baseline is locked — retuning the
122
- whole rig after a tone-mapping change is self-inflicted.
123
- - Never repair unbalanced light ratios with exposure — fix the lights.
124
- - Flicker from elapsed time, never per-frame randomness.
125
- - Dispose lights and their shadow maps with the level that spawned them — and
126
- the rest of the level with them: traverse the outgoing scene and dispose
127
- geometry, materials, AND textures separately (three frees nothing; material
128
- dispose does not touch textures). Leaked levels march phones toward the
129
- OS memory kill — `$genex-threejs-adaptive-quality` owns the budget watch.
130
- - The rig must read with post off: time of day and where-to-go, before bloom.
131
-
132
- ## Routing boundary
133
-
134
- Tone mapping, exposure, and grading are
135
- `$genex-threejs-exposure-color-grading`. How much an emissive glows is
136
- `$genex-threejs-bloom`; whether it also illuminates is this skill. Authoring
137
- the emissive surface itself — lava, hot rock, a screen's picture — is
138
- `$genex-threejs-procedural-materials`. `$genex-threejs-shadow-systems` owns
139
- large roaming-world shadows (cascades, clipmaps) — a bounded scene's one
140
- shadow-casting key with a tight frustum lives here. Sky scattering, haze, and the sun disc are
141
- `$genex-threejs-atmosphere-aerial-perspective`; shafts inside clouds are
142
- `$genex-threejs-volumetric-clouds`; ordering a volumetric pass among other
143
- post effects is `$genex-threejs-image-pipeline`; the environment map that
144
- feeds the fill is `$genex-ai-skybox`.