@genex-ai/cli-demo 0.59.0-dev.133 → 0.59.0-dev.136
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 +56 -5
- package/package.json +2 -2
- package/templates/README.md +5 -4
- package/templates/agents/genex-helper.md +3 -2
- package/templates/commands/genex-status.md +2 -2
- package/templates/skills/genex-getting-started/SKILL.md +10 -5
- package/templates/skills/genex-threejs-lighting-design/SKILL.md +125 -0
- package/templates/skills/genex-threejs-lighting-design/references/light-recipes.md +137 -0
- package/templates/skills/genex-threejs-skill-router/SKILL.md +4 -2
- package/templates/skills/genex-threejs-skill-router/references/routing-map.md +15 -3
package/dist/index.js
CHANGED
|
@@ -72,16 +72,20 @@ function resolveAgentTargets(opts = {}) {
|
|
|
72
72
|
return [{ id: "custom", label: "workspace", baseDir: path.resolve(opts.dir), full: true }];
|
|
73
73
|
}
|
|
74
74
|
const home = os.homedir();
|
|
75
|
+
const projectRoot = process.cwd();
|
|
75
76
|
let ids;
|
|
76
77
|
if (opts.agents && opts.agents.length > 0) {
|
|
77
78
|
ids = opts.agents.filter((id) => KNOWN_AGENTS[id]);
|
|
78
79
|
} else {
|
|
79
|
-
ids = KNOWN_AGENT_IDS.filter((id) =>
|
|
80
|
+
ids = KNOWN_AGENT_IDS.filter((id) => {
|
|
81
|
+
const dirName = KNOWN_AGENTS[id].dirName;
|
|
82
|
+
return isDir(path.join(home, dirName)) || isDir(path.join(projectRoot, dirName));
|
|
83
|
+
});
|
|
80
84
|
if (ids.length === 0) ids = ["claude"];
|
|
81
85
|
}
|
|
82
86
|
return ids.map((id) => {
|
|
83
87
|
const def = KNOWN_AGENTS[id];
|
|
84
|
-
return { id, label: def.label, baseDir: path.join(
|
|
88
|
+
return { id, label: def.label, baseDir: path.join(projectRoot, def.dirName), full: def.full };
|
|
85
89
|
});
|
|
86
90
|
}
|
|
87
91
|
function isDir(p) {
|
|
@@ -242,6 +246,7 @@ async function exists(p) {
|
|
|
242
246
|
|
|
243
247
|
// src/lib/updates.ts
|
|
244
248
|
import fs3 from "fs/promises";
|
|
249
|
+
import os3 from "os";
|
|
245
250
|
import path3 from "path";
|
|
246
251
|
function parseSemver(v) {
|
|
247
252
|
const m = /^(\d+)\.(\d+)\.(\d+)$/.exec(v.trim());
|
|
@@ -292,8 +297,53 @@ async function syncSkillsForTarget(target, templatesDir = getTemplatesDir(), ver
|
|
|
292
297
|
await writeSkillsMarker(skillsDir, version);
|
|
293
298
|
return true;
|
|
294
299
|
}
|
|
300
|
+
async function cleanupLegacyGlobalSkills(log) {
|
|
301
|
+
try {
|
|
302
|
+
const home = os3.homedir();
|
|
303
|
+
const [realCwd, realHome] = await Promise.all([
|
|
304
|
+
fs3.realpath(process.cwd()).catch(() => path3.resolve(process.cwd())),
|
|
305
|
+
fs3.realpath(home).catch(() => path3.resolve(home))
|
|
306
|
+
]);
|
|
307
|
+
if (realCwd === realHome) return false;
|
|
308
|
+
let removed = false;
|
|
309
|
+
for (const dirName of [".claude", ".codex", ".cursor"]) {
|
|
310
|
+
const skillsDir = path3.join(home, dirName, "skills");
|
|
311
|
+
let entries = [];
|
|
312
|
+
try {
|
|
313
|
+
entries = await fs3.readdir(skillsDir);
|
|
314
|
+
} catch {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
for (const name of entries) {
|
|
318
|
+
if (!name.startsWith("genex-")) continue;
|
|
319
|
+
try {
|
|
320
|
+
await fs3.rm(path3.join(skillsDir, name), { recursive: true });
|
|
321
|
+
removed = true;
|
|
322
|
+
} catch {
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
for (const rel of [
|
|
327
|
+
path3.join("agents", "genex-helper.md"),
|
|
328
|
+
path3.join("commands", "genex-status.md")
|
|
329
|
+
]) {
|
|
330
|
+
try {
|
|
331
|
+
await fs3.rm(path3.join(home, ".claude", rel));
|
|
332
|
+
removed = true;
|
|
333
|
+
} catch {
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (removed) {
|
|
337
|
+
log?.plain("\u{1F9F9} Removed legacy GLOBAL Genex skills \u2014 installs are project-local now");
|
|
338
|
+
}
|
|
339
|
+
return removed;
|
|
340
|
+
} catch {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
295
344
|
async function syncSkills(log) {
|
|
296
345
|
try {
|
|
346
|
+
await cleanupLegacyGlobalSkills(log);
|
|
297
347
|
const version = getCliVersion();
|
|
298
348
|
const templatesDir = getTemplatesDir();
|
|
299
349
|
let refreshed = false;
|
|
@@ -1123,6 +1173,7 @@ async function runInit(opts) {
|
|
|
1123
1173
|
const log = createLogger({ quiet: opts.quiet });
|
|
1124
1174
|
log.plain(c.bold("genex init"));
|
|
1125
1175
|
log.plain("");
|
|
1176
|
+
await cleanupLegacyGlobalSkills(log);
|
|
1126
1177
|
const templatesDir = getTemplatesDir();
|
|
1127
1178
|
const targets = resolveAgentTargets({ dir: opts.dir, agents: opts.agents });
|
|
1128
1179
|
log.step(
|
|
@@ -1439,7 +1490,7 @@ function relTime(iso) {
|
|
|
1439
1490
|
import { spawn as spawn3 } from "child_process";
|
|
1440
1491
|
import crypto3 from "crypto";
|
|
1441
1492
|
import fs9 from "fs/promises";
|
|
1442
|
-
import
|
|
1493
|
+
import os4 from "os";
|
|
1443
1494
|
import path10 from "path";
|
|
1444
1495
|
function run(cmd, args, env) {
|
|
1445
1496
|
return new Promise((resolve) => {
|
|
@@ -1666,7 +1717,7 @@ async function pushWorktree(cwd, pushUrl, managed, log) {
|
|
|
1666
1717
|
log.error("Couldn't save your game's source \u2014 please try again.");
|
|
1667
1718
|
return false;
|
|
1668
1719
|
};
|
|
1669
|
-
const gitDir = await fs9.mkdtemp(path10.join(
|
|
1720
|
+
const gitDir = await fs9.mkdtemp(path10.join(os4.tmpdir(), "genex-source-"));
|
|
1670
1721
|
const base = { GIT_DIR: gitDir };
|
|
1671
1722
|
const ident = {
|
|
1672
1723
|
GIT_AUTHOR_NAME: "genex",
|
|
@@ -13545,7 +13596,7 @@ async function uiSeams(opts, log) {
|
|
|
13545
13596
|
|
|
13546
13597
|
// src/index.ts
|
|
13547
13598
|
var GEN_KINDS = /* @__PURE__ */ new Set(["model", "skybox", "sfx", "texture", "image", "video"]);
|
|
13548
|
-
var HELP = `${c.bold("genex")} \u2014 set up your
|
|
13599
|
+
var HELP = `${c.bold("genex")} \u2014 set up your project's agent workspace, authorize, and publish 3D games.
|
|
13549
13600
|
|
|
13550
13601
|
${c.bold("Usage")}
|
|
13551
13602
|
genex init [<name>] [options] Scaffold + authorize + create the draft project.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genex-ai/cli-demo",
|
|
3
|
-
"version": "0.59.0-dev.
|
|
4
|
-
"description": "Set up your
|
|
3
|
+
"version": "0.59.0-dev.136",
|
|
4
|
+
"description": "Set up your project's agent workspace (.claude/.codex/.cursor in the game folder), authorize, create a game project, generate AI assets, and publish (genex CLI).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"genex": "./dist/index.js"
|
package/templates/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# Genex workspace
|
|
2
2
|
|
|
3
|
-
These files were installed by `genex init` into
|
|
4
|
-
Claude Code (
|
|
5
|
-
(
|
|
6
|
-
|
|
3
|
+
These files were installed by `genex init` into THIS project's agent
|
|
4
|
+
workspace — Claude Code (`.claude/`), Codex (`.codex/skills/`), and Cursor
|
|
5
|
+
(`.cursor/skills/`) inside the game folder, whichever agents it detected.
|
|
6
|
+
Nothing is installed globally; delete the folder and every trace is gone.
|
|
7
|
+
They give your agent superpowers for making 3D games in the browser.
|
|
7
8
|
|
|
8
9
|
Genex is built around agent superpowers for browser games: Three.js skills,
|
|
9
10
|
one-click publishing, multiplayer-ready architecture, and team workflows.
|
|
@@ -6,8 +6,9 @@ tools: Read, Grep, Glob
|
|
|
6
6
|
|
|
7
7
|
You are the Genex helper agent — an example subagent installed by `genex init`.
|
|
8
8
|
|
|
9
|
-
Your job is to help the user understand and navigate
|
|
10
|
-
workspace: the skills, agents, and commands installed
|
|
9
|
+
Your job is to help the user understand and navigate this project's `.claude`
|
|
10
|
+
workspace: the skills, agents, and commands Genex installed into the game
|
|
11
|
+
folder (Genex never installs agent files globally).
|
|
11
12
|
|
|
12
13
|
Guidelines:
|
|
13
14
|
- Be concise and concrete. Point at real files with paths.
|
|
@@ -4,8 +4,8 @@ description: Show the current Genex setup — workspace location, installed skil
|
|
|
4
4
|
|
|
5
5
|
Report the user's Genex status:
|
|
6
6
|
|
|
7
|
-
1. Confirm the workspace directory (
|
|
8
|
-
`skills/`, `agents/`, and `commands/` it contains.
|
|
7
|
+
1. Confirm the project's workspace directory (`.claude/` in the game folder)
|
|
8
|
+
exists and list the `skills/`, `agents/`, and `commands/` it contains.
|
|
9
9
|
2. Check whether a `GENEX_TOKEN` is present in the project's `.env` (do not
|
|
10
10
|
print the token value — only whether it is set).
|
|
11
11
|
3. If anything looks missing, suggest running `npx genex init`.
|
|
@@ -11,11 +11,16 @@ architecture, and team-ready workflows.
|
|
|
11
11
|
|
|
12
12
|
## What got installed
|
|
13
13
|
|
|
14
|
-
`genex init` installs the Genex skills into
|
|
15
|
-
Claude Code (
|
|
16
|
-
same skills are available whichever agent you
|
|
14
|
+
`genex init` installs the Genex skills **into this project's folder** for
|
|
15
|
+
every coding agent it detects — Claude Code (`.claude/`), Codex (`.codex/`),
|
|
16
|
+
and Cursor (`.cursor/`) — so the same skills are available whichever agent you
|
|
17
|
+
build with, and nothing is ever installed globally on the machine (only your
|
|
18
|
+
auth token lives outside the project, in `~/.genex/env`). After that, every
|
|
17
19
|
`genex` command keeps the genex-owned skills in sync with the installed CLI
|
|
18
20
|
automatically (re-running `init` does too); your own files are never touched.
|
|
21
|
+
If an older Genex version ever installed skills globally into `~/.claude`,
|
|
22
|
+
`~/.codex` or `~/.cursor`, any `genex` command now sweeps those legacy copies
|
|
23
|
+
out automatically — only `genex-*`-named files are removed, never yours.
|
|
19
24
|
|
|
20
25
|
- **skills/** - reusable Genex skills for 3D browser-game work (all agents).
|
|
21
26
|
- **agents/** - example subagent definitions (Claude Code).
|
|
@@ -23,8 +28,8 @@ automatically (re-running `init` does too); your own files are never touched.
|
|
|
23
28
|
|
|
24
29
|
Start with `$genex-threejs-skill-router` for broad game or graphics requests.
|
|
25
30
|
It routes the agent to focused skills for cameras, procedural geometry,
|
|
26
|
-
materials, atmosphere, water, weather, VFX, post-processing, and
|
|
27
|
-
validation.
|
|
31
|
+
materials, atmosphere, water, weather, VFX, lighting, post-processing, and
|
|
32
|
+
visual validation.
|
|
28
33
|
|
|
29
34
|
## Generating real assets
|
|
30
35
|
|
|
@@ -0,0 +1,125 @@
|
|
|
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
|
+
- Couple a practical to its emitter: one color, one envelope, one on/off state.
|
|
105
|
+
- The key owns shadows; practicals cast none until a shot proves they must.
|
|
106
|
+
- Tune intensities only after the renderer baseline is locked — retuning the
|
|
107
|
+
whole rig after a tone-mapping change is self-inflicted.
|
|
108
|
+
- Never repair unbalanced light ratios with exposure — fix the lights.
|
|
109
|
+
- Flicker from elapsed time, never per-frame randomness.
|
|
110
|
+
- Dispose lights and their shadow maps with the level that spawned them.
|
|
111
|
+
- The rig must read with post off: time of day and where-to-go, before bloom.
|
|
112
|
+
|
|
113
|
+
## Routing boundary
|
|
114
|
+
|
|
115
|
+
Tone mapping, exposure, and grading are
|
|
116
|
+
`$genex-threejs-exposure-color-grading`. How much an emissive glows is
|
|
117
|
+
`$genex-threejs-bloom`; whether it also illuminates is this skill. Authoring
|
|
118
|
+
the emissive surface itself — lava, hot rock, a screen's picture — is
|
|
119
|
+
`$genex-threejs-procedural-materials`. `$genex-threejs-shadow-systems` owns
|
|
120
|
+
large roaming-world shadows (cascades, clipmaps) — a bounded scene's one
|
|
121
|
+
shadow-casting key with a tight frustum lives here. Sky scattering, haze, and the sun disc are
|
|
122
|
+
`$genex-threejs-atmosphere-aerial-perspective`; shafts inside clouds are
|
|
123
|
+
`$genex-threejs-volumetric-clouds`; ordering a volumetric pass among other
|
|
124
|
+
post effects is `$genex-threejs-image-pipeline`; the environment map that
|
|
125
|
+
feeds the fill is `$genex-ai-skybox`.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Light Recipes
|
|
2
|
+
|
|
3
|
+
Contents: rig baselines by environment, the practical couple, shafts and
|
|
4
|
+
visible air, light as information, budgets and lifecycle, the kill-switch
|
|
5
|
+
diagnostic.
|
|
6
|
+
|
|
7
|
+
Numbers below are relationships, not mandates — validate them against the
|
|
8
|
+
project's renderer baseline (`$genex-threejs-exposure-color-grading`), because
|
|
9
|
+
tone mapping and exposure change what every intensity means.
|
|
10
|
+
|
|
11
|
+
## Rig baselines by environment
|
|
12
|
+
|
|
13
|
+
- **Outdoor day** — a warm sun `DirectionalLight` key plus cool sky fill
|
|
14
|
+
(`HemisphereLight`, or the skybox IBL when one is set). Fill sits well below
|
|
15
|
+
the key: when fill approaches key strength, shadow shapes die and the scene
|
|
16
|
+
flattens. The sun owns shadows: size the shadow camera's ortho bounds to the
|
|
17
|
+
playfield edges, fix acne at contact points with `bias`/`normalBias`, and
|
|
18
|
+
bump `mapSize` before loosening the frustum. A roaming world (racer, open
|
|
19
|
+
map) outgrows one frustum — that cascade question is
|
|
20
|
+
`$genex-threejs-shadow-systems`.
|
|
21
|
+
- **Outdoor night** — the moon is a dim, cool, hard key, not a gray day.
|
|
22
|
+
Practicals carry visibility; the moon's job is silhouette and geography.
|
|
23
|
+
Push the fiction's emitters up the ladder before brightening the moon. No
|
|
24
|
+
moon (a city street)? The settlement's own skyglow is the fill — a dim,
|
|
25
|
+
warm-tinted hemisphere — and the emitters carry the rest.
|
|
26
|
+
- **Interior** — the openings and fixtures ARE the rig: a window is a
|
|
27
|
+
`SpotLight` aimed the way the sun outside would aim (or a shaft, below);
|
|
28
|
+
fixtures are coupled practicals. IBL still fills, well below the windows.
|
|
29
|
+
When the hard sun-rectangle on the floor IS the shot (a big window, a long
|
|
30
|
+
room), swap the spot for a shadow-casting `DirectionalLight` through the
|
|
31
|
+
opening — parallel light keeps the patch's edges straight where a spot's
|
|
32
|
+
cone diverges.
|
|
33
|
+
- **Cave / underground** — no sky, no free fill. The brightest practical or
|
|
34
|
+
the crack shaft is the key; darkness is part of the palette, and the
|
|
35
|
+
player's own torch is a gameplay object. Ration the black, don't erase it.
|
|
36
|
+
- **Space** — one hard star key, black fill, bounce only from IBL or
|
|
37
|
+
planetshine. The harsh terminator IS the look; softening it reads as a
|
|
38
|
+
studio shoot.
|
|
39
|
+
|
|
40
|
+
## The practical couple
|
|
41
|
+
|
|
42
|
+
- Contain the light: set `distance` so the falloff dies inside the space the
|
|
43
|
+
source serves, and keep `decay` at the physical `2`. An uncontained point
|
|
44
|
+
light climbs walls three rooms away, and the scene creeps toward flat.
|
|
45
|
+
- Position the light inside the emitter, slightly above the visible flame or
|
|
46
|
+
tube — the pool it throws must sit centered under the thing that explains it.
|
|
47
|
+
- An area emitter — neon tube, screen, softbox — is by default a tinted
|
|
48
|
+
`PointLight` or `SpotLight` sunk into it. Reach for `RectAreaLight` only
|
|
49
|
+
when the rectangular wash on a nearby wall IS the shot, and know its terms:
|
|
50
|
+
`RectAreaLightUniformsLib.init()` first, no shadows, standard/physical
|
|
51
|
+
materials only.
|
|
52
|
+
- When the player roams, the budget follows them — but never add or remove a
|
|
53
|
+
light mid-play: changing the light count recompiles every shader that sees
|
|
54
|
+
it, a visible hitch. Allocate a small fixed pool up front, reassign members'
|
|
55
|
+
position and color to the nearest sources, and retire one by driving its
|
|
56
|
+
intensity to 0.
|
|
57
|
+
|
|
58
|
+
## Shafts and visible air
|
|
59
|
+
|
|
60
|
+
The default shaft is geometry, not a render pass — an open cone or tapered
|
|
61
|
+
cylinder from the aperture, additive, fading along its length:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
function beamGradient(): THREE.Texture {
|
|
65
|
+
const c = document.createElement("canvas");
|
|
66
|
+
c.width = 1; c.height = 64;
|
|
67
|
+
const ctx = c.getContext("2d")!;
|
|
68
|
+
const g = ctx.createLinearGradient(0, 0, 0, 64);
|
|
69
|
+
g.addColorStop(0, "rgba(255,255,255,0.9)");
|
|
70
|
+
g.addColorStop(1, "rgba(255,255,255,0)");
|
|
71
|
+
ctx.fillStyle = g;
|
|
72
|
+
ctx.fillRect(0, 0, 1, 64);
|
|
73
|
+
const t = new THREE.Texture(c);
|
|
74
|
+
t.needsUpdate = true;
|
|
75
|
+
return t;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const shaft = new THREE.Mesh(
|
|
79
|
+
new THREE.CylinderGeometry(0.25, 1.6, 7, 24, 1, true), // narrow at the crack, wide at the floor
|
|
80
|
+
new THREE.MeshBasicMaterial({
|
|
81
|
+
map: beamGradient(), // flip the gradient if the bright end lands on the floor
|
|
82
|
+
transparent: true,
|
|
83
|
+
opacity: 0.35,
|
|
84
|
+
blending: THREE.AdditiveBlending,
|
|
85
|
+
depthWrite: false,
|
|
86
|
+
side: THREE.DoubleSide,
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- Pair the shaft with a real `SpotLight` from the same aperture: the bright
|
|
92
|
+
pool at its foot is what sells the beam. A shaft with nothing at its foot
|
|
93
|
+
reads as a hologram.
|
|
94
|
+
- If the scene earns its one ambient-particle layer (the moment gate's
|
|
95
|
+
budget), inside the beam is where it pays — slow dust motes, the
|
|
96
|
+
`$genex-threejs-procedural-vfx` dot-texture recipe.
|
|
97
|
+
- `scene.fog` (or `FogExp2`) is a scene-wide mood decision: every light pool
|
|
98
|
+
becomes a cone and contrast falls everywhere — choose it for the whole look.
|
|
99
|
+
- A full-screen shaft hanging off the sun disc rides the atmosphere pass
|
|
100
|
+
(`$genex-threejs-atmosphere-aerial-perspective`); this skill's shafts are
|
|
101
|
+
per-aperture beams built as geometry. A real raymarched volumetric pass is
|
|
102
|
+
for scenes whose identity is shafts.
|
|
103
|
+
|
|
104
|
+
## Light as information
|
|
105
|
+
|
|
106
|
+
- Reserve a signal hue: pickups and objectives get a color no environment
|
|
107
|
+
light uses, and it never changes meaning mid-game.
|
|
108
|
+
- Aim the player with brightness before UI: the lit doorway beats the arrow.
|
|
109
|
+
The eye lands on the highest contrast in frame — put it where the player
|
|
110
|
+
should go.
|
|
111
|
+
- Telegraph danger with the light's shape — a red cone where the boss will
|
|
112
|
+
sweep, a pulsing ring under the falling crate — timed to the same event
|
|
113
|
+
feedback the moment gate placed.
|
|
114
|
+
- A vertical beacon (shaft recipe above) reads across the whole map and over
|
|
115
|
+
occluding walls — the map's own "you are here".
|
|
116
|
+
|
|
117
|
+
## Budgets and lifecycle
|
|
118
|
+
|
|
119
|
+
- The whole rig of most shipped scenes: one shadow-casting key, IBL or
|
|
120
|
+
hemisphere fill, and a handful of coupled practicals. Each real light
|
|
121
|
+
beyond that carries a stated reason.
|
|
122
|
+
- A shadow-casting point light renders the scene six more times; a
|
|
123
|
+
shadow-casting spot, once. Prefer the spot — and prefer no shadow at all on
|
|
124
|
+
a flickering source, because the moving pool already sells it.
|
|
125
|
+
- `scene.remove(light)` does not free it: call `light.dispose()` when the
|
|
126
|
+
level unloads, and the shadow map goes with it.
|
|
127
|
+
|
|
128
|
+
## The kill-switch diagnostic
|
|
129
|
+
|
|
130
|
+
Wire a debug toggle per light, the same way the other systems expose debug
|
|
131
|
+
views. Turn each light off alone and name what died — "the campfire pool",
|
|
132
|
+
"the corridor signal" — watching the frame time as well as the frame: a light
|
|
133
|
+
whose absence changes nothing is dead weight to delete, and a light that costs
|
|
134
|
+
more milliseconds than the mood it adds is over budget. That observation is
|
|
135
|
+
the budget — there is no magic count. Then take two screenshots with post
|
|
136
|
+
off — the darkest playable corner and the brightest — and check both: if the
|
|
137
|
+
subject and the way forward read in each, the rig holds.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: genex-threejs-skill-router
|
|
3
|
-
description: Route Genex 3D browser-game work to the smallest useful Three.js skill set. Use for new game scenes, graphics upgrades, visual reference matching, game-feel passes, render-pipeline work, publishing readiness, multiplayer-aware scene architecture, or requests spanning cameras, geometry, materials, atmosphere, water, VFX, shadows, post-processing, and validation.
|
|
3
|
+
description: Route Genex 3D browser-game work to the smallest useful Three.js skill set. Use for new game scenes, graphics upgrades, visual reference matching, game-feel passes, render-pipeline work, publishing readiness, multiplayer-aware scene architecture, or requests spanning cameras, geometry, materials, atmosphere, water, VFX, lighting, shadows, post-processing, and validation.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Genex Three.js Skill Router
|
|
@@ -36,6 +36,7 @@ map, execution order, and acceptance gate.
|
|
|
36
36
|
| curved-ray black holes, accretion disks, wormholes | `$genex-threejs-raymarched-space-effects` |
|
|
37
37
|
| particles, trails, plasma, shockwaves, layered event effects | `$genex-threejs-procedural-vfx` |
|
|
38
38
|
| accumulated screen frost, touch clearing, reduced blur, and refraction masks | `$genex-threejs-temporal-surfaces` |
|
|
39
|
+
| the light rig and where light belongs: sun/moon key, practical lights (campfire, torch, neon, lava), emissive-to-light coupling, light shafts and visible beams, fog mood, light signals, flicker, a scene that reads flat or uniformly lit | `$genex-threejs-lighting-design` |
|
|
39
40
|
| stable large-world shadows, cascades, clipmaps, cached updates | `$genex-threejs-shadow-systems` |
|
|
40
41
|
| GTAO, bent normals, bilateral reconstruction | `$genex-threejs-screen-space-ambient-occlusion` |
|
|
41
42
|
| HDR bloom and selective emission contribution | `$genex-threejs-bloom` |
|
|
@@ -94,7 +95,8 @@ it in the running game), one named
|
|
|
94
95
|
**ambient-motion loop** that keeps
|
|
95
96
|
the scene alive at rest (emissive pulse, shimmer, drifting dust — shader
|
|
96
97
|
work, zero generations), and the lighting/atmosphere mood from that
|
|
97
|
-
same brief
|
|
98
|
+
same brief — walked source by source, every light with a visible cause
|
|
99
|
+
(`$genex-threejs-lighting-design` owns that gate). Planning is not building — effects still land last in the
|
|
98
100
|
execution order.
|
|
99
101
|
|
|
100
102
|
**Multiplayer is mandatory routing:** if the game has 2+ players sharing a world, loading
|
|
@@ -105,8 +105,18 @@ Three.js release or branch, and do not blindly copy demo architecture.
|
|
|
105
105
|
texture goes on this box" but "what shape is this energy": a primitive
|
|
106
106
|
standing in for an effect is a placeholder no matter how deliberate the
|
|
107
107
|
comment says it is. `$genex-threejs-procedural-vfx` owns this gate;
|
|
108
|
-
- **
|
|
109
|
-
|
|
108
|
+
- **every light has a cause — walk the sources one by one**: the
|
|
109
|
+
lighting/atmosphere mood comes from the SAME shared style brief the UI
|
|
110
|
+
gate wrote — one art direction across scene and UI — but a mood is not a
|
|
111
|
+
rig. Walk the sources the contract already names: the sky (day, night,
|
|
112
|
+
underground — that one answer sets the key), the fiction's emitters
|
|
113
|
+
(campfire, torch, neon, a monitor, a crack of daylight in the roof), and
|
|
114
|
+
the gameplay signals (beacon, telegraph, checkpoint). For each: does it
|
|
115
|
+
change what the player sees around it, or only need to be seen itself?
|
|
116
|
+
Emissive-only is a real answer said in one line; a coupled practical
|
|
117
|
+
light is the step up; one white ambient wash over everything is the
|
|
118
|
+
unlit look with extra steps, and it reads the same at noon and at
|
|
119
|
+
midnight. `$genex-threejs-lighting-design` owns this gate;
|
|
110
120
|
Planning is not building: effects still land LAST (steps 10–11); this step
|
|
111
121
|
only fixes the target so the look isn't improvised pass-by-pass at the end.
|
|
112
122
|
5. Wire the gameplay layer for the player verb: the physics world via
|
|
@@ -137,7 +147,9 @@ Three.js release or branch, and do not blindly copy demo architecture.
|
|
|
137
147
|
8. Add procedural animation when object motion needs authored phases,
|
|
138
148
|
convergence, looping, or deterministic timelines.
|
|
139
149
|
9. Add shared fields before writing multiple independent noise layers.
|
|
140
|
-
10. Add lighting, atmosphere, and shadows only after the no-post baseline
|
|
150
|
+
10. Add lighting, atmosphere, and shadows only after the no-post baseline
|
|
151
|
+
reads: the step-4 source walk builds now — `$genex-threejs-lighting-design`
|
|
152
|
+
owns the rig.
|
|
141
153
|
11. Add image-pipeline, bloom, exposure, grading, or AO last — building out the
|
|
142
154
|
step-4 post plan, not inventing one now. This is a completion gate: the
|
|
143
155
|
named post stack must be BUILT before the game is called done, published,
|