create-aura3d 1.1.2 → 1.3.0
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.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/package.json +11 -9
- package/templates/{cartoon-channel → animation-channel}/README.md +11 -11
- package/templates/{cartoon-studio → animation-channel}/index.html +1 -1
- package/templates/{cartoon-channel → animation-channel}/package.json +2 -2
- package/templates/{cartoon-channel → animation-channel}/src/episode.ts +22 -22
- package/templates/{cartoon-channel → animation-channel}/src/experimental/README.md +5 -5
- package/templates/{cartoon-channel → animation-channel}/src/experimental/concept-episode-2-5d.css +3 -3
- package/templates/{cartoon-channel → animation-channel}/src/experimental/concept-episode-2-5d.ts +7 -7
- package/templates/{cartoon-channel → animation-channel}/src/experimental/image-puppet-episode.css +4 -4
- package/templates/{cartoon-channel → animation-channel}/src/experimental/image-puppet-episode.ts +6 -6
- package/templates/{cartoon-channel → animation-channel}/src/experimental/puppet-episode-2d.css +3 -3
- package/templates/{cartoon-channel → animation-channel}/src/experimental/puppet-episode-2d.ts +7 -7
- package/templates/{cartoon-channel → animation-channel}/src/main.ts +32 -32
- package/templates/{cartoon-channel → animation-channel}/src/render-plan.ts +13 -13
- package/templates/{cartoon-channel → animation-channel}/src/sample-episode-visual.css +34 -34
- package/templates/{cartoon-channel → animation-channel}/src/sample-episode-visual.ts +20 -20
- package/templates/{cartoon-channel → animation-channel}/tests/concept-2-5d-animation.spec.ts +3 -3
- package/templates/{cartoon-channel → animation-channel}/tests/concept-2-5d.spec.ts +6 -6
- package/templates/{cartoon-channel → animation-channel}/tests/image-puppet-animation.spec.ts +3 -3
- package/templates/{cartoon-channel → animation-channel}/tests/image-puppet.spec.ts +5 -5
- package/templates/{cartoon-channel → animation-channel}/tests/puppet-2d-animation.spec.ts +3 -3
- package/templates/{cartoon-channel → animation-channel}/tests/puppet-2d.spec.ts +4 -4
- package/templates/{prompt-cartoon-channel → animation-channel}/tests/route-health.spec.ts +1 -1
- package/templates/{cartoon-channel → animation-channel}/tests/sample-episode-visual.spec.ts +5 -5
- package/templates/{cartoon-channel → animation-channel}/tests/storyboard-playback.spec.ts +7 -7
- package/templates/animation-studio/AGENTS.md +71 -0
- package/templates/{cartoon-studio → animation-studio}/ASSET-LICENSES.md +7 -7
- package/templates/{cartoon-studio → animation-studio}/README.md +6 -6
- package/templates/animation-studio/aura.assets.json +669 -0
- package/templates/animation-studio/dist/episodes/scene/episode-3d.webm +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/action.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/dialogue.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/final.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/first.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/mouth-closed.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/mouth-open.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/render-live-summary.json +10284 -0
- package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/worker-1.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/worker-2.png +0 -0
- package/templates/animation-studio/dist/generated/second-scene.document.json +1210 -0
- package/templates/animation-studio/dist/generated/studio-edited.document.json +1196 -0
- package/templates/animation-studio/dist/scene/inshader-accept.document.json +1 -0
- package/templates/animation-studio/dist/scene/inshader-test.document.json +1 -0
- package/templates/animation-studio/dist/scene/integ.document.json +1 -0
- package/templates/animation-studio/dist/scene/working.document.json +723 -0
- package/templates/animation-studio/dist/scene/working.history.json +1 -0
- package/templates/{cartoon-studio → animation-studio}/live-route.html +1 -1
- package/templates/{cartoon-studio → animation-studio}/package-lock.json +2 -2
- package/templates/animation-studio/package.json +32 -0
- package/templates/animation-studio/public/aura-assets/cast-a.catalog.glb +0 -0
- package/templates/animation-studio/public/aura-assets/cast-b.catalog.glb +0 -0
- package/templates/animation-studio/public/aura-assets/luma.catalog.glb +0 -0
- package/templates/animation-studio/public/aura-assets/luma2.catalog.glb +0 -0
- package/templates/animation-studio/public/aura-assets/miko.catalog.glb +0 -0
- package/templates/{cartoon-studio → animation-studio}/public/aura-assets/moonGarden.gltf +1 -1
- package/templates/animation-studio/public/aura-assets/mushroom.catalog.glb +0 -0
- package/templates/animation-studio/public/aura-assets/rusty.catalog.glb +0 -0
- package/templates/animation-studio/public/cast-library/cast-library.json +71 -0
- package/templates/animation-studio/public/clip-library/gesture.json +173903 -0
- package/templates/animation-studio/public/clip-library/idle.json +23035 -0
- package/templates/animation-studio/public/clip-library/manifest.json +154 -0
- package/templates/animation-studio/public/clip-library/nod.json +173903 -0
- package/templates/animation-studio/public/clip-library/point.json +69879 -0
- package/templates/animation-studio/public/clip-library/react.json +66084 -0
- package/templates/animation-studio/public/clip-library/run.json +9648 -0
- package/templates/animation-studio/public/clip-library/talk.json +69879 -0
- package/templates/animation-studio/public/clip-library/walk.json +8283 -0
- package/templates/animation-studio/public/hifi-cast/BrainStem.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/CesiumMan.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/RiggedFigure.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/kaykit/Barbarian.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/kaykit/Knight.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/kaykit/Mage.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/kaykit/Rogue.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/kaykit/Rogue_Hooded.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/khronos-report.json +295 -0
- package/templates/animation-studio/public/hifi-cast/quaternius-report.json +449 -0
- package/templates/animation-studio/public/hifi-cast/rpm/cesiumman.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/rpm/fox.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/rpm/riggedfigure.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/rpm-report.json +301 -0
- package/templates/animation-studio/scripts/anim.ts +103 -0
- package/templates/animation-studio/scripts/animation-scene.ts +457 -0
- package/templates/animation-studio/scripts/asset-motion-probe.ts +450 -0
- package/templates/animation-studio/scripts/asset-render-probe.ts +152 -0
- package/templates/animation-studio/scripts/author-office-scene.ts +65 -0
- package/templates/animation-studio/scripts/build-cast-library.ts +97 -0
- package/templates/animation-studio/scripts/build-characters.ts +1543 -0
- package/templates/animation-studio/scripts/build-clip-library.ts +972 -0
- package/templates/animation-studio/scripts/clip-decision-cli.ts +51 -0
- package/templates/animation-studio/scripts/determinism-check.ts +48 -0
- package/templates/animation-studio/scripts/generate-scene.ts +95 -0
- package/templates/animation-studio/scripts/hifi-cast-validate.ts +572 -0
- package/templates/animation-studio/scripts/motion-evidence-cli.ts +150 -0
- package/templates/animation-studio/scripts/motion-evidence.ts +362 -0
- package/templates/animation-studio/scripts/preview-server.ts +52 -0
- package/templates/animation-studio/scripts/regen-manifest.ts +156 -0
- package/templates/animation-studio/scripts/render-core.ts +360 -0
- package/templates/animation-studio/scripts/render-live.ts +813 -0
- package/templates/animation-studio/scripts/render-parallel.ts +70 -0
- package/templates/animation-studio/scripts/render-server.ts +119 -0
- package/templates/animation-studio/scripts/resolve-asset.ts +669 -0
- package/templates/animation-studio/scripts/skeleton-overlay-cli.ts +82 -0
- package/templates/animation-studio/scripts/skeleton-overlay.ts +293 -0
- package/templates/animation-studio/scripts/studio-tools-demo.ts +45 -0
- package/templates/animation-studio/scripts/texture-bake.ts +297 -0
- package/templates/{cartoon-studio → animation-studio}/scripts/validate-characters.ts +0 -0
- package/templates/animation-studio/scripts/verify-extracted-overlay-cli.ts +264 -0
- package/templates/animation-studio/src/animation-episode-validator.ts +97 -0
- package/templates/animation-studio/src/animation-performance.ts +556 -0
- package/templates/animation-studio/src/aura-assets.ts +71 -0
- package/templates/animation-studio/src/character.ts +40 -0
- package/templates/animation-studio/src/director/compile-episode-document.ts +45 -0
- package/templates/animation-studio/src/director/director-heuristics.ts +697 -0
- package/templates/animation-studio/src/director/prompt-to-scene.ts +369 -0
- package/templates/animation-studio/src/empty-document.ts +77 -0
- package/templates/animation-studio/src/episode-document.ts +442 -0
- package/templates/animation-studio/src/examples/moon-garden.example.ts +183 -0
- package/templates/animation-studio/src/fidelity.ts +129 -0
- package/templates/animation-studio/src/graph.ts +14 -0
- package/templates/animation-studio/src/procedural-hdri.ts +243 -0
- package/templates/animation-studio/src/profile.ts +97 -0
- package/templates/animation-studio/src/render-live-route.ts +42 -0
- package/templates/animation-studio/src/render-modes.ts +137 -0
- package/templates/animation-studio/src/scene-player.ts +867 -0
- package/templates/animation-studio/src/set-templates.ts +361 -0
- package/templates/animation-studio/src/studio/episode-document-store.ts +72 -0
- package/templates/animation-studio/src/studio/scene-tool-registry.ts +86 -0
- package/templates/animation-studio/src/studio/scene-tools.ts +169 -0
- package/templates/animation-studio/tests/reports/animation-studio/hifi-cast-validation.json +938 -0
- package/templates/animation-studio/tsconfig.json +26 -0
- package/templates/{cartoon-studio → animation-studio}/vite.config.ts +6 -1
- package/templates/character-controller/README.md +33 -0
- package/templates/character-controller/index.html +19 -0
- package/templates/character-controller/package.json +23 -0
- package/templates/character-controller/playwright.config.ts +14 -0
- package/templates/character-controller/src/controller.ts +44 -0
- package/templates/character-controller/src/main.ts +62 -0
- package/templates/character-controller/tests/route-health.spec.ts +26 -0
- package/templates/character-controller/tests/screenshot.spec.ts +9 -0
- package/templates/character-controller/tsconfig.json +15 -0
- package/templates/cinematic-scene/package.json +1 -1
- package/templates/episode-builder/README.md +4 -4
- package/templates/episode-builder/package.json +1 -1
- package/templates/episode-builder/src/aura-assets.ts +1 -1
- package/templates/episode-builder/src/builder.ts +4 -4
- package/templates/episode-builder/src/episode.ts +21 -21
- package/templates/episode-builder/src/main.ts +20 -20
- package/templates/episode-builder/src/render-plan.ts +13 -13
- package/templates/episode-builder/tests/storyboard-playback.spec.ts +7 -7
- package/templates/fighting-game/package.json +1 -1
- package/templates/mini-game/package.json +1 -1
- package/templates/product-viewer/package.json +1 -1
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/README.md +5 -5
- package/templates/{cartoon-channel → prompt-animation-channel}/index.html +1 -1
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/package.json +2 -2
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/src/aura-assets.ts +1 -1
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/src/episode.ts +22 -22
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/src/main.ts +28 -28
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/src/render-plan.ts +13 -13
- package/templates/{cartoon-channel → prompt-animation-channel}/tests/route-health.spec.ts +1 -1
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/tests/storyboard-playback.spec.ts +8 -8
- package/templates/cartoon-studio/aura.assets.json +0 -400
- package/templates/cartoon-studio/package.json +0 -33
- package/templates/cartoon-studio/public/aura-assets/luma.authored.glb +0 -0
- package/templates/cartoon-studio/public/aura-assets/miko.authored.glb +0 -0
- package/templates/cartoon-studio/scripts/build-characters.ts +0 -738
- package/templates/cartoon-studio/scripts/build-dialogue-audio.ts +0 -262
- package/templates/cartoon-studio/scripts/episode.ts +0 -323
- package/templates/cartoon-studio/scripts/render-live.ts +0 -873
- package/templates/cartoon-studio/src/aura-assets.ts +0 -64
- package/templates/cartoon-studio/src/characters.ts +0 -65
- package/templates/cartoon-studio/src/episode-renderer.ts +0 -605
- package/templates/cartoon-studio/src/episode.ts +0 -306
- package/templates/cartoon-studio/src/main.ts +0 -400
- package/templates/cartoon-studio/src/render-live-route.ts +0 -913
- package/templates/cartoon-studio/src/render-plan.ts +0 -567
- package/templates/cartoon-studio/src/review.ts +0 -99
- package/templates/cartoon-studio/src/studio.ts +0 -101
- package/templates/cartoon-studio/test-results/.last-run.json +0 -4
- package/templates/cartoon-studio/tests/episode-render.spec.ts +0 -44
- package/templates/cartoon-studio/tests/motion-quality.spec.ts +0 -45
- package/templates/cartoon-studio/tests/route-health.spec.ts +0 -7
- package/templates/cartoon-studio/tests/storyboard-playback.spec.ts +0 -99
- package/templates/cartoon-studio/tests/visual-review.spec.ts +0 -21
- package/templates/cartoon-studio/tsconfig.json +0 -14
- package/templates/prompt-cartoon-channel/index.html +0 -13
- package/templates/prompt-cartoon-channel/src/sets.ts +0 -14
- /package/templates/{cartoon-channel → animation-channel}/aura.assets.json +0 -0
- /package/templates/{cartoon-channel → animation-channel}/playwright.config.ts +0 -0
- /package/templates/{cartoon-channel → animation-channel}/public/aura-assets/luma.047f5e5f.glb +0 -0
- /package/templates/{cartoon-channel → animation-channel}/public/aura-assets/luma.thumb.svg +0 -0
- /package/templates/{cartoon-channel → animation-channel}/public/aura-assets/miko.047f5e5f.glb +0 -0
- /package/templates/{cartoon-channel → animation-channel}/public/aura-assets/miko.thumb.svg +0 -0
- /package/templates/{cartoon-channel → animation-channel}/public/aura-assets/moon-garden-feature-frame.png +0 -0
- /package/templates/{cartoon-channel → animation-channel}/src/aura-assets.ts +0 -0
- /package/templates/{cartoon-channel → animation-channel}/src/characters.ts +0 -0
- /package/templates/{cartoon-channel → animation-channel}/src/contract.ts +0 -0
- /package/templates/{cartoon-channel → animation-channel}/src/sets.ts +0 -0
- /package/templates/{cartoon-channel → animation-channel}/test-results/.last-run.json +0 -0
- /package/templates/{cartoon-channel → animation-channel}/tests/reports/prompt-animation/cartoon-sample-episode.png +0 -0
- /package/templates/{cartoon-channel → animation-channel}/tsconfig.json +0 -0
- /package/templates/{cartoon-studio → animation-studio}/playwright.config.ts +0 -0
- /package/templates/{cartoon-studio → animation-studio}/public/aura-assets/luma.thumb.svg +0 -0
- /package/templates/{cartoon-studio → animation-studio}/public/aura-assets/miko.thumb.svg +0 -0
- /package/templates/{cartoon-studio → animation-studio}/src/contract.ts +0 -0
- /package/templates/{prompt-cartoon-channel → prompt-animation-channel}/playwright.config.ts +0 -0
- /package/templates/{prompt-cartoon-channel → prompt-animation-channel}/src/characters.ts +0 -0
- /package/templates/{prompt-cartoon-channel → prompt-animation-channel}/src/contract.ts +0 -0
- /package/templates/{cartoon-studio → prompt-animation-channel}/src/sets.ts +0 -0
- /package/templates/{prompt-cartoon-channel → prompt-animation-channel}/tsconfig.json +0 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* set-templates.ts — a small library of SET templates (#4) so a scene's environment is
|
|
3
|
+
* chosen from the prompt, NOT always the Moon Garden. Each template returns a full SetSpec
|
|
4
|
+
* (floor / sky / set pieces / lights / environment) + the walkable bounds. The Director /
|
|
5
|
+
* `animation-scene set` picks one by name or by prompt keyword.
|
|
6
|
+
*
|
|
7
|
+
* Moon Garden is just an EXAMPLE scene; `moon-garden` here is ONE template among several and is
|
|
8
|
+
* NEVER the default. A prompt that matches no theme falls back to a neutral STUDIO set; a prompt
|
|
9
|
+
* about a space station gets the space set; a meadow prompt gets the meadow.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { SetSpec, Vec3, Vec4 } from "./episode-document";
|
|
13
|
+
|
|
14
|
+
export interface SetTemplate {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly keywords: readonly string[];
|
|
17
|
+
readonly set: SetSpec;
|
|
18
|
+
readonly walkableBounds: { readonly min: Vec3; readonly max: Vec3 };
|
|
19
|
+
/** Default prop the Director scatters as ground dressing for this set. */
|
|
20
|
+
readonly groundProp?: { readonly propId: string; readonly query: string; readonly scaleRange: readonly [number, number]; readonly feetOffset: number };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const BOUNDS = { min: [-3.6, 0, -2] as Vec3, max: [3.6, 0, 2] as Vec3 };
|
|
24
|
+
|
|
25
|
+
type Piece = SetSpec["pieces"][number];
|
|
26
|
+
|
|
27
|
+
function glow(id: string, geometry: Piece["geometry"], baseColor: Vec4, emissiveColor: Vec3, dim: number, full: number, position: Vec3, scale: Vec3): Piece {
|
|
28
|
+
return { id, geometry, position, scale, baseColor, metallic: 0, roughness: 0.85, emissiveColor, glow: { dim, full } };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Composition helpers (M4). The engine only ships cube/sphere/cylinder primitives, so
|
|
33
|
+
// "better geometry" means COMPOSING them: a large inward-emissive sphere DOME for the sky
|
|
34
|
+
// (so the horizon wraps the whole stage instead of a flat back wall), a curved horizon band,
|
|
35
|
+
// and a LAYERED ground (a wide base plate + a tinted near-camera apron) so scale/horizon read
|
|
36
|
+
// correctly. Each set then adds a few themed scenery pieces instead of a single lone sphere.
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A large CURVED emissive backdrop (a wide, tall, shallow sphere) placed BEHIND the stage so its
|
|
41
|
+
* camera-facing surface is a front face (renders reliably under back-face culling, unlike a
|
|
42
|
+
* camera-inside dome) while the curvature gives a wrapping sky rather than a flat wall — the
|
|
43
|
+
* horizon then reads as a continuous cyclorama, not a seam. Sits low + far so it fills the frame.
|
|
44
|
+
*/
|
|
45
|
+
function skyDome(id: string, color: Vec3, emissive: number): Piece {
|
|
46
|
+
return {
|
|
47
|
+
id, geometry: "sphere",
|
|
48
|
+
position: [0, 6, -22], scale: [100, 60, 30],
|
|
49
|
+
baseColor: [color[0], color[1], color[2], 1],
|
|
50
|
+
metallic: 0, roughness: 1,
|
|
51
|
+
emissiveColor: color, emissiveStrength: emissive
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** A curved horizon BAND (flattened sphere) that grounds the dome where sky meets earth. */
|
|
56
|
+
function horizonBand(id: string, color: Vec3, emissive: number, y: number): Piece {
|
|
57
|
+
return {
|
|
58
|
+
id, geometry: "sphere",
|
|
59
|
+
position: [0, y, -20], scale: [90, 14, 4],
|
|
60
|
+
baseColor: [color[0], color[1], color[2], 1],
|
|
61
|
+
metallic: 0, roughness: 1,
|
|
62
|
+
emissiveColor: color, emissiveStrength: emissive
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A layered ground: a wide base plate (auto-framed) plus a near-camera apron in a darker tint. */
|
|
67
|
+
function groundLayers(baseColor: Vec4, apronColor: Vec4, metallic: number, roughness: number): Piece[] {
|
|
68
|
+
return [
|
|
69
|
+
{ id: "ground-base", geometry: "cube", position: [0, -0.06, 0], scale: [120, 0.12, 120], baseColor, metallic, roughness, emissiveColor: [baseColor[0] * 0.1, baseColor[1] * 0.1, baseColor[2] * 0.1], emissiveStrength: 0.05, includeInAutoFrame: true },
|
|
70
|
+
{ id: "ground-apron", geometry: "cube", position: [0, -0.04, 3.2], scale: [16, 0.08, 6], baseColor: apronColor, metallic, roughness: Math.min(1, roughness + 0.05) }
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Moon garden (night, cyan, glowing moon) — the reference look.
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
const MOON_GARDEN: SetSpec = {
|
|
78
|
+
clearColor: [0.043, 0.058, 0.101, 1],
|
|
79
|
+
studioLightingScale: 0.4,
|
|
80
|
+
environment: {
|
|
81
|
+
color: [0.34, 0.4, 0.52],
|
|
82
|
+
intensity: 0.32,
|
|
83
|
+
proceduralMap: { skyColor: [0.16, 0.22, 0.34], horizonColor: [0.22, 0.26, 0.32], groundColor: [0.05, 0.06, 0.08], specularColor: [0.85, 0.9, 1], intensity: 0.4, specularIntensity: 0.7 },
|
|
84
|
+
// Cool night IBL with a soft moon disc high+right for a cyan rim reflection.
|
|
85
|
+
hdri: { skyColor: [0.04, 0.08, 0.2], horizonColor: [0.1, 0.16, 0.26], groundColor: [0.03, 0.05, 0.05], sun: { color: [0.55, 0.78, 1], intensity: 2.2, azimuth: 0.7, elevation: 0.7, angularRadius: 0.13 }, intensity: 0.5, specularIntensity: 0.7 }
|
|
86
|
+
},
|
|
87
|
+
pieces: [
|
|
88
|
+
skyDome("garden-sky", [0.05, 0.10, 0.22], 0.5),
|
|
89
|
+
horizonBand("garden-horizon", [0.10, 0.26, 0.34], 0.45, 1.5),
|
|
90
|
+
...groundLayers([0.09, 0.29, 0.245, 1], [0.06, 0.20, 0.17, 1], 0.02, 0.78),
|
|
91
|
+
glow("moon-halo", "sphere", [0.4, 0.72, 0.95, 1], [0.32, 0.62, 0.9], 0.35, 0.62, [2.5, 2.85, -6.2], [3.9, 3.9, 3.9]),
|
|
92
|
+
glow("moon-orb", "sphere", [0.62, 0.84, 1, 1], [0.36, 0.62, 0.88], 0.4, 0.62, [2.5, 2.85, -6.0], [2.1, 2.1, 2.1]),
|
|
93
|
+
// Distant glowing hillocks + a near garden hedge so the stage reads as a place, not a void.
|
|
94
|
+
glow("garden-hill-l", "sphere", [0.06, 0.20, 0.18, 1], [0.04, 0.14, 0.12], 0.2, 0.3, [-6.5, -0.6, -8], [6, 2.4, 6]),
|
|
95
|
+
glow("garden-hill-r", "sphere", [0.05, 0.18, 0.20, 1], [0.04, 0.12, 0.13], 0.2, 0.3, [6.0, -0.8, -9], [7, 2.8, 7]),
|
|
96
|
+
{ id: "garden-hedge", geometry: "cube", position: [0, 0.18, -2.6], scale: [7.2, 0.5, 0.4], baseColor: [0.08, 0.26, 0.20, 1], metallic: 0, roughness: 0.9, emissiveColor: [0.03, 0.10, 0.08], emissiveStrength: 0.2 }
|
|
97
|
+
],
|
|
98
|
+
lights: [
|
|
99
|
+
{ id: "key", kind: "point", color: [0.86, 0.93, 1], position: [-2.6, 3.6, 2.6], intensity: 4.4, range: 18 },
|
|
100
|
+
{ id: "fill", kind: "point", color: [0.49, 0.886, 1], position: [2.4, 2.0, 1.4], intensity: 2.0, range: 13 },
|
|
101
|
+
{ id: "rim", kind: "point", color: [1, 0.78, 0.45], position: [0.4, 1.8, -2.0], intensity: 2.8, range: 13 }
|
|
102
|
+
]
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// Space station (dark, stars, a planet, cool metal floor).
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
const SPACE_STATION: SetSpec = {
|
|
109
|
+
clearColor: [0.01, 0.012, 0.03, 1],
|
|
110
|
+
studioLightingScale: 0.35,
|
|
111
|
+
environment: {
|
|
112
|
+
color: [0.22, 0.26, 0.4],
|
|
113
|
+
intensity: 0.28,
|
|
114
|
+
proceduralMap: { skyColor: [0.02, 0.03, 0.08], horizonColor: [0.04, 0.05, 0.12], groundColor: [0.02, 0.02, 0.03], specularColor: [0.8, 0.85, 1], intensity: 0.35, specularIntensity: 0.9 },
|
|
115
|
+
// Near-black space IBL with a cold planet-glow key for crisp metal specular on the deck.
|
|
116
|
+
hdri: { skyColor: [0.01, 0.012, 0.04], horizonColor: [0.03, 0.04, 0.1], groundColor: [0.01, 0.01, 0.02], sun: { color: [0.4, 0.6, 1], intensity: 2.6, azimuth: 0.55, elevation: 0.55, angularRadius: 0.1 }, intensity: 0.45, specularIntensity: 1 }
|
|
117
|
+
},
|
|
118
|
+
pieces: [
|
|
119
|
+
skyDome("starfield", [0.01, 0.012, 0.04], 0.35),
|
|
120
|
+
...groundLayers([0.12, 0.13, 0.16, 1], [0.08, 0.09, 0.12, 1], 0.4, 0.35),
|
|
121
|
+
// A metal deck rim + two console pylons so the floor reads as a station, not a plane.
|
|
122
|
+
{ id: "deck-rim", geometry: "cylinder", position: [0, 0.02, 0], scale: [9, 0.06, 9], baseColor: [0.16, 0.18, 0.22, 1], metallic: 0.6, roughness: 0.3, emissiveColor: [0.03, 0.05, 0.09], emissiveStrength: 0.15 },
|
|
123
|
+
{ id: "pylon-l", geometry: "cube", position: [-3.2, 0.9, -2.4], scale: [0.4, 1.8, 0.4], baseColor: [0.14, 0.15, 0.2, 1], metallic: 0.5, roughness: 0.4, emissiveColor: [0.1, 0.3, 0.5], emissiveStrength: 0.4 },
|
|
124
|
+
{ id: "pylon-r", geometry: "cube", position: [3.2, 0.9, -2.4], scale: [0.4, 1.8, 0.4], baseColor: [0.14, 0.15, 0.2, 1], metallic: 0.5, roughness: 0.4, emissiveColor: [0.1, 0.3, 0.5], emissiveStrength: 0.4 },
|
|
125
|
+
glow("planet", "sphere", [0.3, 0.5, 0.85, 1], [0.18, 0.4, 0.85], 0.6, 1.1, [3.0, 3.6, -7.0], [3.4, 3.4, 3.4]),
|
|
126
|
+
glow("planet-ring", "cylinder", [0.7, 0.6, 0.4, 1], [0.6, 0.5, 0.35], 0.4, 0.7, [3.0, 3.6, -7.1], [5.2, 0.05, 5.2]),
|
|
127
|
+
glow("star-cluster", "sphere", [0.8, 0.85, 1, 1], [0.7, 0.78, 1], 0.5, 0.9, [-4.5, 4.8, -9], [0.5, 0.5, 0.5])
|
|
128
|
+
],
|
|
129
|
+
lights: [
|
|
130
|
+
{ id: "key", kind: "point", color: [0.8, 0.85, 1], position: [-2.4, 3.4, 2.6], intensity: 3.6, range: 18 },
|
|
131
|
+
{ id: "fill", kind: "point", color: [0.4, 0.6, 1], position: [2.6, 2.0, 1.2], intensity: 1.6, range: 12 },
|
|
132
|
+
{ id: "rim", kind: "point", color: [1, 0.5, 0.4], position: [0.2, 1.8, -2.2], intensity: 2.4, range: 12 }
|
|
133
|
+
]
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Sunny meadow (day, green, a warm sun).
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
const MEADOW: SetSpec = {
|
|
140
|
+
clearColor: [0.55, 0.78, 0.95, 1],
|
|
141
|
+
studioLightingScale: 0.7,
|
|
142
|
+
environment: {
|
|
143
|
+
color: [0.7, 0.78, 0.85],
|
|
144
|
+
intensity: 0.6,
|
|
145
|
+
proceduralMap: { skyColor: [0.45, 0.7, 0.95], horizonColor: [0.7, 0.85, 0.95], groundColor: [0.25, 0.4, 0.15], specularColor: [1, 0.98, 0.9], intensity: 0.7, specularIntensity: 0.5 },
|
|
146
|
+
// Bright blue-sky daylight IBL with a warm sun disc high+left to match the meadow key.
|
|
147
|
+
hdri: { skyColor: [0.4, 0.66, 0.98], horizonColor: [0.78, 0.88, 0.96], groundColor: [0.2, 0.34, 0.12], sun: { color: [1, 0.95, 0.78], intensity: 3, azimuth: -0.7, elevation: 0.85, angularRadius: 0.09 }, intensity: 0.85, specularIntensity: 0.55 }
|
|
148
|
+
},
|
|
149
|
+
pieces: [
|
|
150
|
+
skyDome("meadow-sky", [0.48, 0.72, 0.96], 0.7),
|
|
151
|
+
horizonBand("meadow-hills", [0.30, 0.54, 0.26], 0.25, 1.2),
|
|
152
|
+
...groundLayers([0.32, 0.6, 0.24, 1], [0.26, 0.52, 0.18, 1], 0, 0.9),
|
|
153
|
+
glow("sun", "sphere", [1, 0.95, 0.7, 1], [1, 0.9, 0.6], 1.1, 1.4, [-3.0, 4.2, -7.0], [2.4, 2.4, 2.4]),
|
|
154
|
+
// Two distant tree clusters + a near hedgerow so the meadow has depth, not one lone sun.
|
|
155
|
+
glow("tree-l", "sphere", [0.18, 0.42, 0.16, 1], [0.06, 0.16, 0.05], 0.1, 0.15, [-5.5, 1.2, -8], [2.6, 3.2, 2.6]),
|
|
156
|
+
{ id: "trunk-l", geometry: "cylinder", position: [-5.5, -0.2, -8], scale: [0.5, 2.4, 0.5], baseColor: [0.32, 0.22, 0.12, 1], metallic: 0, roughness: 1 },
|
|
157
|
+
glow("tree-r", "sphere", [0.20, 0.46, 0.18, 1], [0.07, 0.18, 0.06], 0.1, 0.15, [5.0, 1.0, -9], [3.0, 3.4, 3.0]),
|
|
158
|
+
{ id: "trunk-r", geometry: "cylinder", position: [5.0, -0.4, -9], scale: [0.55, 2.6, 0.55], baseColor: [0.30, 0.20, 0.11, 1], metallic: 0, roughness: 1 },
|
|
159
|
+
{ id: "hedgerow", geometry: "cube", position: [0, 0.22, -2.8], scale: [8.0, 0.6, 0.5], baseColor: [0.24, 0.5, 0.2, 1], metallic: 0, roughness: 0.95 }
|
|
160
|
+
],
|
|
161
|
+
lights: [
|
|
162
|
+
{ id: "sun-key", kind: "point", color: [1, 0.96, 0.85], position: [-2.8, 4.2, 3.0], intensity: 5.0, range: 22 },
|
|
163
|
+
{ id: "sky-fill", kind: "point", color: [0.6, 0.78, 1], position: [2.6, 2.4, 1.6], intensity: 2.2, range: 14 },
|
|
164
|
+
{ id: "warm-rim", kind: "point", color: [1, 0.85, 0.6], position: [0.4, 2.0, -2.2], intensity: 2.0, range: 12 }
|
|
165
|
+
]
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
// Neutral studio (content-free, soft grey) — the DEFAULT when a prompt matches no
|
|
170
|
+
// themed keyword. This guarantees a non-matching prompt never silently inherits the
|
|
171
|
+
// Moon Garden look; it gets a plain studio set instead.
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
const STUDIO: SetSpec = {
|
|
174
|
+
clearColor: [0.06, 0.07, 0.09, 1],
|
|
175
|
+
studioLightingScale: 0.5,
|
|
176
|
+
environment: {
|
|
177
|
+
color: [0.5, 0.52, 0.55],
|
|
178
|
+
intensity: 0.4,
|
|
179
|
+
proceduralMap: { skyColor: [0.18, 0.19, 0.21], horizonColor: [0.24, 0.25, 0.27], groundColor: [0.08, 0.08, 0.09], specularColor: [0.8, 0.82, 0.85], intensity: 0.4, specularIntensity: 0.5 },
|
|
180
|
+
// Neutral grey studio softbox IBL — even overhead key, no coloured cast (a clean cyc look).
|
|
181
|
+
hdri: { skyColor: [0.7, 0.71, 0.74], horizonColor: [0.34, 0.35, 0.38], groundColor: [0.12, 0.12, 0.14], sun: { color: [1, 1, 1], intensity: 1.4, azimuth: -0.3, elevation: 1.1, angularRadius: 0.22 }, intensity: 0.6, specularIntensity: 0.55 }
|
|
182
|
+
},
|
|
183
|
+
pieces: [
|
|
184
|
+
// A FAR backdrop wall placed well behind the stage (not a giant enclosing dome): it reads as an
|
|
185
|
+
// interior wall without flat-filling the frame or swallowing the cast. (An over-large cyclorama
|
|
186
|
+
// centered on the stage fills the whole camera frustum and hides the characters.)
|
|
187
|
+
{ id: "studio-cyc-backdrop", geometry: "sphere", position: [0, 6, -22], scale: [60, 34, 8], baseColor: [0.2, 0.2, 0.23, 1], metallic: 0, roughness: 1, emissiveColor: [0.13, 0.13, 0.15], emissiveStrength: 0.42 },
|
|
188
|
+
...groundLayers([0.18, 0.19, 0.21, 1], [0.13, 0.14, 0.16, 1], 0.05, 0.85),
|
|
189
|
+
// Two upright pillars flank the stage — depth + framing (read as posts/columns of the interior).
|
|
190
|
+
{ id: "pillar-l", geometry: "cube", position: [-3.4, 1.1, -2.6], scale: [0.42, 2.2, 0.42], baseColor: [0.16, 0.17, 0.2, 1], metallic: 0.3, roughness: 0.6, emissiveColor: [0.05, 0.05, 0.06], emissiveStrength: 0.12 },
|
|
191
|
+
{ id: "pillar-r", geometry: "cube", position: [3.4, 1.1, -2.6], scale: [0.42, 2.2, 0.42], baseColor: [0.16, 0.17, 0.2, 1], metallic: 0.3, roughness: 0.6, emissiveColor: [0.05, 0.05, 0.06], emissiveStrength: 0.12 }
|
|
192
|
+
],
|
|
193
|
+
lights: [
|
|
194
|
+
{ id: "key", kind: "point", color: [1, 0.97, 0.9], position: [-2.6, 3.8, 3.0], intensity: 4.4, range: 20 },
|
|
195
|
+
{ id: "fill", kind: "point", color: [0.82, 0.84, 0.92], position: [2.8, 2.2, 1.6], intensity: 2.2, range: 14 },
|
|
196
|
+
{ id: "rim", kind: "point", color: [0.9, 0.9, 1], position: [0.3, 2.2, -2.4], intensity: 2.4, range: 13 }
|
|
197
|
+
]
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// GARAGE / WORKSHOP (M4) — a concrete bay with a roll-up door, a workbench, a tool wall, and a
|
|
202
|
+
// shelf rack. Earthbound interior; warm utility lighting. Distinct from the neutral studio so a
|
|
203
|
+
// "garage"/"workshop" prompt no longer renders the same empty cyc as an "office".
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
const GARAGE: SetSpec = {
|
|
206
|
+
clearColor: [0.07, 0.07, 0.075, 1],
|
|
207
|
+
studioLightingScale: 0.55,
|
|
208
|
+
environment: {
|
|
209
|
+
color: [0.5, 0.49, 0.46],
|
|
210
|
+
intensity: 0.42,
|
|
211
|
+
proceduralMap: { skyColor: [0.16, 0.16, 0.17], horizonColor: [0.22, 0.21, 0.2], groundColor: [0.1, 0.1, 0.1], specularColor: [0.85, 0.82, 0.75], intensity: 0.4, specularIntensity: 0.55 },
|
|
212
|
+
hdri: { skyColor: [0.42, 0.42, 0.44], horizonColor: [0.26, 0.25, 0.24], groundColor: [0.1, 0.1, 0.1], sun: { color: [1, 0.92, 0.78], intensity: 1.8, azimuth: -0.4, elevation: 1, angularRadius: 0.18 }, intensity: 0.55, specularIntensity: 0.6 }
|
|
213
|
+
},
|
|
214
|
+
pieces: [
|
|
215
|
+
// Far concrete back wall (cyc) so the bay reads as enclosed without an enclosing dome.
|
|
216
|
+
{ id: "garage-cyc-wall", geometry: "sphere", position: [0, 6, -22], scale: [56, 32, 8], baseColor: [0.27, 0.27, 0.28, 1], metallic: 0, roughness: 1, emissiveColor: [0.16, 0.16, 0.17], emissiveStrength: 0.4 },
|
|
217
|
+
...groundLayers([0.22, 0.22, 0.23, 1], [0.16, 0.16, 0.17, 1], 0.05, 0.9),
|
|
218
|
+
// Roll-up door panel on the back wall (lighter banded metal).
|
|
219
|
+
{ id: "garage-door", geometry: "cube", position: [0, 1.6, -3.2], scale: [4.4, 3.0, 0.18], baseColor: [0.5, 0.52, 0.55, 1], metallic: 0.4, roughness: 0.5, emissiveColor: [0.08, 0.08, 0.09], emissiveStrength: 0.1 },
|
|
220
|
+
// Workbench (top + two legs) along the right.
|
|
221
|
+
{ id: "garage-bench-top", geometry: "cube", position: [3.0, 0.85, -2.2], scale: [2.4, 0.16, 0.9], baseColor: [0.34, 0.26, 0.18, 1], metallic: 0.1, roughness: 0.8 },
|
|
222
|
+
{ id: "garage-bench-leg-l", geometry: "cube", position: [2.1, 0.42, -2.2], scale: [0.14, 0.84, 0.8], baseColor: [0.2, 0.2, 0.22, 1], metallic: 0.5, roughness: 0.5 },
|
|
223
|
+
{ id: "garage-bench-leg-r", geometry: "cube", position: [3.9, 0.42, -2.2], scale: [0.14, 0.84, 0.8], baseColor: [0.2, 0.2, 0.22, 1], metallic: 0.5, roughness: 0.5 },
|
|
224
|
+
// Tool-wall pegboard (left) + a shelf rack with stacked boxes.
|
|
225
|
+
{ id: "garage-toolwall", geometry: "cube", position: [-3.4, 1.6, -2.6], scale: [1.8, 1.6, 0.1], baseColor: [0.2, 0.28, 0.3, 1], metallic: 0.2, roughness: 0.7 },
|
|
226
|
+
{ id: "garage-shelf", geometry: "cube", position: [-3.0, 0.5, -1.4], scale: [1.4, 0.1, 0.7], baseColor: [0.3, 0.3, 0.32, 1], metallic: 0.4, roughness: 0.5 },
|
|
227
|
+
{ id: "garage-box-1", geometry: "cube", position: [-3.2, 0.75, -1.5], scale: [0.4, 0.4, 0.4], baseColor: [0.45, 0.32, 0.18, 1], metallic: 0, roughness: 0.95 },
|
|
228
|
+
{ id: "garage-box-2", geometry: "cube", position: [-2.7, 0.72, -1.4], scale: [0.36, 0.36, 0.36], baseColor: [0.5, 0.45, 0.2, 1], metallic: 0, roughness: 0.95 },
|
|
229
|
+
// A tire (cylinder on its side) for unmistakable garage read.
|
|
230
|
+
{ id: "garage-tire", geometry: "cylinder", position: [1.6, 0.35, -2.5], scale: [0.7, 0.3, 0.7], roll: Math.PI / 2, baseColor: [0.06, 0.06, 0.07, 1], metallic: 0, roughness: 0.98 }
|
|
231
|
+
],
|
|
232
|
+
lights: [
|
|
233
|
+
{ id: "key", kind: "point", color: [1, 0.95, 0.86], position: [-2.4, 3.6, 2.8], intensity: 4.2, range: 18 },
|
|
234
|
+
{ id: "fill", kind: "point", color: [0.86, 0.88, 0.92], position: [2.8, 2.2, 1.6], intensity: 2.0, range: 13 },
|
|
235
|
+
{ id: "rim", kind: "point", color: [0.95, 0.86, 0.7], position: [0.4, 2.4, -2.6], intensity: 2.4, range: 13 }
|
|
236
|
+
]
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
// OFFICE (M4) — a clean workspace: desks, a partition wall, monitor cubes, a chair, a plant. Cooler,
|
|
241
|
+
// neutral office lighting. Distinct from garage/kitchen.
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
const OFFICE: SetSpec = {
|
|
244
|
+
// Bright, lit office — the background reads as a wall, not a dark void above the cast.
|
|
245
|
+
clearColor: [0.62, 0.66, 0.72, 1],
|
|
246
|
+
studioLightingScale: 0.6,
|
|
247
|
+
environment: {
|
|
248
|
+
color: [0.7, 0.72, 0.76],
|
|
249
|
+
intensity: 0.55,
|
|
250
|
+
proceduralMap: { skyColor: [0.62, 0.66, 0.72], horizonColor: [0.5, 0.53, 0.58], groundColor: [0.28, 0.29, 0.32], specularColor: [0.9, 0.92, 0.96], intensity: 0.5, specularIntensity: 0.5 },
|
|
251
|
+
hdri: { skyColor: [0.7, 0.74, 0.8], horizonColor: [0.5, 0.53, 0.58], groundColor: [0.24, 0.25, 0.28], sun: { color: [0.95, 0.97, 1], intensity: 1.5, azimuth: 0.5, elevation: 1.05, angularRadius: 0.2 }, intensity: 0.6, specularIntensity: 0.5 }
|
|
252
|
+
},
|
|
253
|
+
pieces: [
|
|
254
|
+
// Far wall backdrop — large + bright so it fills the frame top as a lit office wall (no dark void).
|
|
255
|
+
{ id: "office-cyc-wall", geometry: "sphere", position: [0, 8, -20], scale: [70, 44, 10], baseColor: [0.66, 0.69, 0.74, 1], metallic: 0.05, roughness: 0.9, emissiveColor: [0.5, 0.53, 0.58], emissiveStrength: 0.7 },
|
|
256
|
+
...groundLayers([0.26, 0.27, 0.3, 1], [0.2, 0.21, 0.24, 1], 0.1, 0.6),
|
|
257
|
+
// Desk (top + legs) centre-left.
|
|
258
|
+
{ id: "office-desk-top", geometry: "cube", position: [-2.4, 0.78, -2.0], scale: [2.6, 0.12, 1.1], baseColor: [0.72, 0.72, 0.74, 1], metallic: 0.05, roughness: 0.5 },
|
|
259
|
+
{ id: "office-desk-leg-l", geometry: "cube", position: [-3.5, 0.39, -2.0], scale: [0.12, 0.78, 1.0], baseColor: [0.3, 0.3, 0.33, 1], metallic: 0.4, roughness: 0.5 },
|
|
260
|
+
{ id: "office-desk-leg-r", geometry: "cube", position: [-1.3, 0.39, -2.0], scale: [0.12, 0.78, 1.0], baseColor: [0.3, 0.3, 0.33, 1], metallic: 0.4, roughness: 0.5 },
|
|
261
|
+
// Two monitors on the desk (screens emissive so they read as on).
|
|
262
|
+
{ id: "office-monitor-1", geometry: "cube", position: [-2.9, 1.18, -2.2], scale: [0.7, 0.44, 0.06], baseColor: [0.08, 0.09, 0.1, 1], metallic: 0.2, roughness: 0.4, emissiveColor: [0.2, 0.42, 0.6], emissiveStrength: 0.5 },
|
|
263
|
+
{ id: "office-monitor-2", geometry: "cube", position: [-1.9, 1.18, -2.2], scale: [0.7, 0.44, 0.06], baseColor: [0.08, 0.09, 0.1, 1], metallic: 0.2, roughness: 0.4, emissiveColor: [0.22, 0.4, 0.55], emissiveStrength: 0.5 },
|
|
264
|
+
// Partition divider + a second hot-desk on the right.
|
|
265
|
+
{ id: "office-partition", geometry: "cube", position: [0.3, 1.0, -2.6], scale: [0.12, 2.0, 1.6], baseColor: [0.4, 0.45, 0.5, 1], metallic: 0.1, roughness: 0.7 },
|
|
266
|
+
{ id: "office-desk2-top", geometry: "cube", position: [2.6, 0.78, -2.0], scale: [2.2, 0.12, 1.0], baseColor: [0.7, 0.7, 0.72, 1], metallic: 0.05, roughness: 0.5 },
|
|
267
|
+
// Office chair (seat + post).
|
|
268
|
+
{ id: "office-chair-seat", geometry: "cylinder", position: [-2.2, 0.5, -0.9], scale: [0.5, 0.12, 0.5], baseColor: [0.12, 0.14, 0.18, 1], metallic: 0.1, roughness: 0.8 },
|
|
269
|
+
{ id: "office-chair-post", geometry: "cylinder", position: [-2.2, 0.24, -0.9], scale: [0.1, 0.5, 0.1], baseColor: [0.2, 0.2, 0.22, 1], metallic: 0.6, roughness: 0.4 },
|
|
270
|
+
// A potted plant for warmth (trunk + canopy).
|
|
271
|
+
{ id: "office-plant-pot", geometry: "cylinder", position: [3.4, 0.3, -1.2], scale: [0.3, 0.6, 0.3], baseColor: [0.4, 0.3, 0.24, 1], metallic: 0, roughness: 0.9 },
|
|
272
|
+
{ id: "office-plant-leaves", geometry: "sphere", position: [3.4, 0.95, -1.2], scale: [0.6, 0.7, 0.6], baseColor: [0.16, 0.4, 0.18, 1], metallic: 0, roughness: 0.95 }
|
|
273
|
+
],
|
|
274
|
+
lights: [
|
|
275
|
+
{ id: "key", kind: "point", color: [0.97, 0.98, 1], position: [-2.6, 3.8, 3.0], intensity: 4.2, range: 19 },
|
|
276
|
+
{ id: "fill", kind: "point", color: [0.86, 0.9, 0.96], position: [2.8, 2.4, 1.8], intensity: 2.2, range: 14 },
|
|
277
|
+
{ id: "rim", kind: "point", color: [0.9, 0.92, 1], position: [0.3, 2.4, -2.6], intensity: 2.2, range: 13 }
|
|
278
|
+
]
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// ---------------------------------------------------------------------------
|
|
282
|
+
// KITCHEN (M4) — counters, upper cabinets, a fridge, a stove/hood, a sink. Warm domestic lighting.
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
const KITCHEN: SetSpec = {
|
|
285
|
+
clearColor: [0.09, 0.085, 0.08, 1],
|
|
286
|
+
studioLightingScale: 0.58,
|
|
287
|
+
environment: {
|
|
288
|
+
color: [0.58, 0.55, 0.5],
|
|
289
|
+
intensity: 0.46,
|
|
290
|
+
proceduralMap: { skyColor: [0.24, 0.22, 0.2], horizonColor: [0.3, 0.28, 0.25], groundColor: [0.14, 0.13, 0.12], specularColor: [0.9, 0.86, 0.78], intensity: 0.44, specularIntensity: 0.55 },
|
|
291
|
+
hdri: { skyColor: [0.66, 0.62, 0.56], horizonColor: [0.4, 0.37, 0.33], groundColor: [0.16, 0.15, 0.13], sun: { color: [1, 0.93, 0.8], intensity: 1.7, azimuth: -0.5, elevation: 1, angularRadius: 0.2 }, intensity: 0.6, specularIntensity: 0.6 }
|
|
292
|
+
},
|
|
293
|
+
pieces: [
|
|
294
|
+
// Far tiled backsplash wall (cyc).
|
|
295
|
+
{ id: "kitchen-cyc-wall", geometry: "sphere", position: [0, 6, -22], scale: [56, 32, 8], baseColor: [0.36, 0.34, 0.32, 1], metallic: 0.05, roughness: 0.85, emissiveColor: [0.22, 0.21, 0.2], emissiveStrength: 0.44 },
|
|
296
|
+
...groundLayers([0.3, 0.28, 0.26, 1], [0.22, 0.21, 0.2, 1], 0.05, 0.7),
|
|
297
|
+
// Base counter run (long cabinet + worktop) along the back.
|
|
298
|
+
{ id: "kitchen-counter", geometry: "cube", position: [-1.2, 0.45, -2.4], scale: [4.0, 0.9, 0.8], baseColor: [0.55, 0.5, 0.44, 1], metallic: 0.05, roughness: 0.7 },
|
|
299
|
+
{ id: "kitchen-worktop", geometry: "cube", position: [-1.2, 0.92, -2.4], scale: [4.1, 0.08, 0.85], baseColor: [0.18, 0.18, 0.2, 1], metallic: 0.2, roughness: 0.35 },
|
|
300
|
+
// Upper cabinets.
|
|
301
|
+
{ id: "kitchen-uppers", geometry: "cube", position: [-1.2, 2.0, -2.7], scale: [3.6, 0.8, 0.5], baseColor: [0.6, 0.55, 0.48, 1], metallic: 0.05, roughness: 0.7 },
|
|
302
|
+
// Sink basin (inset darker cube) + a faucet.
|
|
303
|
+
{ id: "kitchen-sink", geometry: "cube", position: [-2.4, 0.9, -2.4], scale: [0.7, 0.12, 0.5], baseColor: [0.62, 0.64, 0.68, 1], metallic: 0.7, roughness: 0.3 },
|
|
304
|
+
{ id: "kitchen-faucet", geometry: "cylinder", position: [-2.4, 1.12, -2.6], scale: [0.05, 0.4, 0.05], baseColor: [0.7, 0.72, 0.76, 1], metallic: 0.8, roughness: 0.2 },
|
|
305
|
+
// Stove + extractor hood with emissive cooktop indicators.
|
|
306
|
+
{ id: "kitchen-stove", geometry: "cube", position: [0.4, 0.45, -2.4], scale: [0.9, 0.9, 0.8], baseColor: [0.2, 0.2, 0.22, 1], metallic: 0.5, roughness: 0.4, emissiveColor: [0.3, 0.08, 0.04], emissiveStrength: 0.25 },
|
|
307
|
+
{ id: "kitchen-hood", geometry: "cube", position: [0.4, 1.9, -2.6], scale: [1.0, 0.4, 0.6], baseColor: [0.55, 0.56, 0.6, 1], metallic: 0.6, roughness: 0.3 },
|
|
308
|
+
// Tall fridge on the right.
|
|
309
|
+
{ id: "kitchen-fridge", geometry: "cube", position: [3.0, 1.05, -2.3], scale: [1.0, 2.1, 0.8], baseColor: [0.78, 0.79, 0.82, 1], metallic: 0.5, roughness: 0.3 },
|
|
310
|
+
// A kitchen island / prep table out front so the floor reads as a kitchen.
|
|
311
|
+
{ id: "kitchen-island", geometry: "cube", position: [0.6, 0.45, -0.6], scale: [1.6, 0.9, 0.9], baseColor: [0.5, 0.45, 0.4, 1], metallic: 0.05, roughness: 0.7 }
|
|
312
|
+
],
|
|
313
|
+
lights: [
|
|
314
|
+
{ id: "key", kind: "point", color: [1, 0.94, 0.84], position: [-2.4, 3.6, 3.0], intensity: 4.2, range: 18 },
|
|
315
|
+
{ id: "fill", kind: "point", color: [0.9, 0.9, 0.92], position: [2.8, 2.4, 1.8], intensity: 2.2, range: 14 },
|
|
316
|
+
{ id: "rim", kind: "point", color: [1, 0.88, 0.72], position: [0.4, 2.4, -2.6], intensity: 2.2, range: 13 }
|
|
317
|
+
]
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// The neutral studio is the fallback (NOT a keyword-matched template, so it never wins by
|
|
321
|
+
// accident); the themed templates below are chosen by prompt keyword overlap.
|
|
322
|
+
// The neutral studio is the generic INTERIOR fallback (a clean cyclorama). The dedicated
|
|
323
|
+
// GARAGE / OFFICE / KITCHEN interiors carry their own keywords so those prompts route to a
|
|
324
|
+
// DISTINCT room instead of all sharing the empty studio cyc; truly generic indoor prompts
|
|
325
|
+
// (room/indoor/interior/studio) still land on the neutral studio.
|
|
326
|
+
const STUDIO_DEFAULT: SetTemplate = {
|
|
327
|
+
id: "studio",
|
|
328
|
+
keywords: ["indoor", "interior", "room", "studio", "lab", "warehouse", "house", "home", "apartment"],
|
|
329
|
+
set: STUDIO,
|
|
330
|
+
walkableBounds: BOUNDS
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
export const SET_TEMPLATES: readonly SetTemplate[] = [
|
|
334
|
+
STUDIO_DEFAULT,
|
|
335
|
+
{ id: "garage", keywords: ["garage", "workshop", "shop", "car", "vehicle", "mechanic", "tools", "repair"], set: GARAGE, walkableBounds: BOUNDS },
|
|
336
|
+
{ id: "office", keywords: ["office", "desk", "workspace", "cubicle", "meeting", "computer", "monitor"], set: OFFICE, walkableBounds: BOUNDS },
|
|
337
|
+
{ id: "kitchen", keywords: ["kitchen", "cook", "cooking", "diner", "cafe", "chef", "fridge", "stove", "counter"], set: KITCHEN, walkableBounds: BOUNDS },
|
|
338
|
+
{ id: "moon-garden", keywords: ["moon", "garden", "night", "bedtime", "lily", "glow"], set: MOON_GARDEN, walkableBounds: BOUNDS, groundProp: { propId: "mushroom", query: "glowing mushroom", scaleRange: [0.06, 0.16], feetOffset: 2.8 } },
|
|
339
|
+
{ id: "space-station", keywords: ["space", "station", "spaceship", "astronaut", "planet", "orbit", "galaxy", "cosmos", "asteroid"], set: SPACE_STATION, walkableBounds: BOUNDS, groundProp: { propId: "crystal", query: "glowing crystal", scaleRange: [0.08, 0.2], feetOffset: 0 } },
|
|
340
|
+
{ id: "meadow", keywords: ["meadow", "field", "day", "sunny", "grass", "park", "forest", "flower"], set: MEADOW, walkableBounds: BOUNDS, groundProp: { propId: "flower", query: "low poly flower", scaleRange: [0.1, 0.25], feetOffset: 0 } }
|
|
341
|
+
];
|
|
342
|
+
|
|
343
|
+
export function getSetTemplate(id: string): SetTemplate | undefined {
|
|
344
|
+
return SET_TEMPLATES.find((t) => t.id === id);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Pick the best set template for a prompt by keyword overlap. When NOTHING matches, the
|
|
349
|
+
* fallback is the NEUTRAL STUDIO set — never the Moon Garden. Moon Garden only wins when
|
|
350
|
+
* the prompt explicitly mentions moon/garden/night/etc.
|
|
351
|
+
*/
|
|
352
|
+
export function pickSetForPrompt(prompt: string): SetTemplate {
|
|
353
|
+
const text = prompt.toLowerCase();
|
|
354
|
+
let best = STUDIO_DEFAULT;
|
|
355
|
+
let bestScore = 0;
|
|
356
|
+
for (const t of SET_TEMPLATES) {
|
|
357
|
+
const score = t.keywords.reduce((n, k) => n + (text.includes(k) ? 1 : 0), 0);
|
|
358
|
+
if (score > bestScore) { bestScore = score; best = t; }
|
|
359
|
+
}
|
|
360
|
+
return best;
|
|
361
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* episode-document-store.ts — the versioned Episode Document.
|
|
3
|
+
*
|
|
4
|
+
* The single editable artifact the Prompt Studio agent mutates (never raw code). Every
|
|
5
|
+
* commit is validated by the coherence gate and recorded as a revision (undo/redo). A
|
|
6
|
+
* commit that fails the gate is REJECTED — there is no path to a broken document.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { validateEpisodeDocument, type EpisodeDocumentValidation, type ValidateOptions } from "../animation-episode-validator";
|
|
10
|
+
import type { EpisodeDocument } from "../episode-document";
|
|
11
|
+
|
|
12
|
+
export interface CommitResult {
|
|
13
|
+
readonly committed: boolean;
|
|
14
|
+
readonly validation: EpisodeDocumentValidation;
|
|
15
|
+
readonly revision: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class EpisodeDocumentStore {
|
|
19
|
+
private revisions: EpisodeDocument[] = [];
|
|
20
|
+
private cursor = -1;
|
|
21
|
+
|
|
22
|
+
constructor(initial: EpisodeDocument, private readonly validateOptions: ValidateOptions = {}) {
|
|
23
|
+
// The initial document is committed even if it only has warnings; hard errors throw.
|
|
24
|
+
const validation = validateEpisodeDocument(initial, this.validateOptions);
|
|
25
|
+
if (!validation.ok) {
|
|
26
|
+
throw new Error(`initial document is invalid:\n - ${validation.errors.join("\n - ")}`);
|
|
27
|
+
}
|
|
28
|
+
this.revisions.push(initial);
|
|
29
|
+
this.cursor = 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
current(): EpisodeDocument {
|
|
33
|
+
return this.revisions[this.cursor]!;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Validate + commit a new revision. Rejected (not committed) if it has hard errors. */
|
|
37
|
+
commit(next: EpisodeDocument): CommitResult {
|
|
38
|
+
const validation = validateEpisodeDocument(next, this.validateOptions);
|
|
39
|
+
if (!validation.ok) {
|
|
40
|
+
return { committed: false, validation, revision: this.cursor };
|
|
41
|
+
}
|
|
42
|
+
// Truncate any redo history, then push.
|
|
43
|
+
this.revisions = this.revisions.slice(0, this.cursor + 1);
|
|
44
|
+
this.revisions.push(next);
|
|
45
|
+
this.cursor = this.revisions.length - 1;
|
|
46
|
+
return { committed: true, validation, revision: this.cursor };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
undo(): boolean {
|
|
50
|
+
if (this.cursor > 0) {
|
|
51
|
+
this.cursor -= 1;
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
redo(): boolean {
|
|
58
|
+
if (this.cursor < this.revisions.length - 1) {
|
|
59
|
+
this.cursor += 1;
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
revisionCount(): number {
|
|
66
|
+
return this.revisions.length;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
currentRevision(): number {
|
|
70
|
+
return this.cursor;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scene-tool-registry.ts — the constrained tool surface the studio agent calls.
|
|
3
|
+
*
|
|
4
|
+
* Every tool mutates the versioned document via the store, which validates + commits (or
|
|
5
|
+
* rejects). The agent has scene-authoring powers, NOT arbitrary-code powers. `toolSchemas`
|
|
6
|
+
* is the declarative surface an LLM is given for tool-calling.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { CameraPresetId } from "@aura3d/engine";
|
|
10
|
+
import type { BlockingWaypoint, EpisodeDocument, PropPlacement, Vec3 } from "../episode-document";
|
|
11
|
+
import { EpisodeDocumentStore, type CommitResult } from "./episode-document-store";
|
|
12
|
+
import { clearProps, placeProp, setBlocking, setCamera, setGesture } from "./scene-tools";
|
|
13
|
+
|
|
14
|
+
export interface ToolSchema {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly description: string;
|
|
17
|
+
readonly params: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class SceneToolRegistry {
|
|
21
|
+
constructor(private readonly store: EpisodeDocumentStore) {}
|
|
22
|
+
|
|
23
|
+
document(): EpisodeDocument {
|
|
24
|
+
return this.store.current();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
block(characterId: string, shotId: string, waypoints: readonly BlockingWaypoint[], clip?: string): CommitResult {
|
|
28
|
+
return this.store.commit(setBlocking(this.store.current(), characterId, shotId, waypoints, clip));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
gesture(characterId: string, shotId: string, clip: string): CommitResult {
|
|
32
|
+
return this.store.commit(setGesture(this.store.current(), characterId, shotId, clip));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
camera(shotId: string, opts: { preset?: CameraPresetId; subject?: Vec3 }): CommitResult {
|
|
36
|
+
return this.store.commit(setCamera(this.store.current(), shotId, opts));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
dress(placement: PropPlacement): CommitResult {
|
|
40
|
+
return this.store.commit(placeProp(this.store.current(), placement));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
clearProps(propId?: string): CommitResult {
|
|
44
|
+
return this.store.commit(clearProps(this.store.current(), propId));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
undo(): boolean {
|
|
48
|
+
return this.store.undo();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
redo(): boolean {
|
|
52
|
+
return this.store.redo();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Declarative tool surface for an LLM tool-calling loop. */
|
|
56
|
+
static toolSchemas(): readonly ToolSchema[] {
|
|
57
|
+
return [
|
|
58
|
+
{
|
|
59
|
+
name: "scene.block",
|
|
60
|
+
description: "Set a character's blocking waypoints (and optional clip) for a shot. Positions must stay on the walkable set.",
|
|
61
|
+
params: { characterId: "string", shotId: "string", waypoints: "[{ time:number, position:[x,y,z], yaw:number }]", clip: "string?" }
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "scene.gesture",
|
|
65
|
+
description: "Schedule a clip/gesture for a character in a shot (keeps existing waypoints).",
|
|
66
|
+
params: { characterId: "string", shotId: "string", clip: "string" }
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "scene.camera",
|
|
70
|
+
description: "Set a shot's camera framing (preset and/or world subject point).",
|
|
71
|
+
params: { shotId: "string", preset: "establishing|two-shot|close-up?", subject: "[x,y,z]?" }
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "scene.dress",
|
|
75
|
+
description: "Place a prop instance on the set (must stay on the walkable set).",
|
|
76
|
+
params: { propId: "string", position: "[x,y,z]", scale: "number", feetOffset: "number" }
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: "scene.clearProps",
|
|
80
|
+
description: "Remove all set dressing, or just one prop type.",
|
|
81
|
+
params: { propId: "string?" }
|
|
82
|
+
},
|
|
83
|
+
{ name: "scene.undo", description: "Undo the last committed edit.", params: {} }
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
}
|