@call-me-sensei/toonlab 0.2.0 → 0.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.
Files changed (205) hide show
  1. package/AGENTS.md +127 -4
  2. package/ATTRIBUTION.md +41 -3
  3. package/README.md +234 -34
  4. package/docs/characters.md +144 -0
  5. package/docs/debug-panel.md +126 -0
  6. package/docs/docs.css +589 -0
  7. package/docs/environment.md +186 -0
  8. package/docs/getting-started.md +180 -0
  9. package/docs/index.html +14 -0
  10. package/docs/lab-architecture.md +100 -0
  11. package/docs/lighting.md +825 -0
  12. package/docs/main.jsx +739 -0
  13. package/docs/mcp.md +97 -0
  14. package/docs/post-processing.md +98 -0
  15. package/docs/settings-reference.md +1878 -0
  16. package/docs/shader-constants.md +77 -0
  17. package/docs/sky.md +182 -0
  18. package/docs/style-labs.md +309 -0
  19. package/docs/texture-lab.md +135 -0
  20. package/docs/toon-shading.md +183 -0
  21. package/docs/tsl-conventions.md +167 -0
  22. package/docs/vegetation-sky.md +275 -0
  23. package/docs/water.md +430 -0
  24. package/docs/weather.md +200 -0
  25. package/docs/world-scale.md +111 -0
  26. package/mcp/server.mjs +610 -0
  27. package/mcp/style-lab-tools.mjs +371 -0
  28. package/mcp/vite-plugin.mjs +174 -0
  29. package/mcp/workspace.mjs +397 -0
  30. package/package.json +64 -5
  31. package/src/ambientfx/INTEGRATION.md +164 -0
  32. package/src/ambientfx/ambientFxPresets.js +83 -0
  33. package/src/ambientfx/ambientFxSettings.js +368 -0
  34. package/src/ambientfx/emitters.js +169 -0
  35. package/src/ambientfx/index.js +5 -0
  36. package/src/ambientfx/particleBackbone.js +493 -0
  37. package/src/ambientfx/stylizedAmbientFx.js +450 -0
  38. package/src/assetlib/ambientcg.js +117 -0
  39. package/src/assetlib/assetRef.js +91 -0
  40. package/src/assetlib/importedEntry.js +40 -0
  41. package/src/assetlib/index.js +23 -0
  42. package/src/assetlib/kaykit.js +192 -0
  43. package/src/assetlib/kaykitStaticIndex.js +700 -0
  44. package/src/assetlib/loadImported.js +143 -0
  45. package/src/assetlib/opensource3d.js +113 -0
  46. package/src/assetlib/polyhaven.js +182 -0
  47. package/src/assetlib/polypizza.js +115 -0
  48. package/src/assetlib/smithsonian.js +214 -0
  49. package/src/assetlib/sources.js +279 -0
  50. package/src/assetlib/zip.js +58 -0
  51. package/src/biome/biomeGenerator.js +385 -0
  52. package/src/biome/biomeRuntime.js +299 -0
  53. package/src/biome/index.js +2 -0
  54. package/src/buildinggen/buildingAsset.js +44 -0
  55. package/src/buildinggen/buildingGrammar.js +311 -0
  56. package/src/buildinggen/buildingMesh.js +451 -0
  57. package/src/buildinggen/buildingPresets.js +46 -0
  58. package/src/buildinggen/buildingRecipe.js +100 -0
  59. package/src/buildinggen/buildingSettings.js +238 -0
  60. package/src/buildinggen/index.js +6 -0
  61. package/src/camera/cameraDirector.js +157 -0
  62. package/src/camera/cameraGenerator.js +367 -0
  63. package/src/camera/cameraRig.js +570 -0
  64. package/src/camera/cameraSettings.js +236 -0
  65. package/src/camera/index.js +7 -0
  66. package/src/catalog/builtinEntries.js +245 -0
  67. package/src/catalog/catalog.js +210 -0
  68. package/src/catalog/index.js +3 -0
  69. package/src/catalog/manifest.js +84 -0
  70. package/src/core/generation.js +529 -0
  71. package/src/environment/environmentRigs.js +21 -1
  72. package/src/environment/environmentSettings.js +4 -0
  73. package/src/environment/environmentSunShadowPass.js +8 -0
  74. package/src/fauna/INTEGRATION.md +174 -0
  75. package/src/fauna/boids.js +861 -0
  76. package/src/fauna/faunaBodies.js +492 -0
  77. package/src/fauna/faunaPresets.js +52 -0
  78. package/src/fauna/faunaSettings.js +525 -0
  79. package/src/fauna/index.js +5 -0
  80. package/src/fauna/stylizedFauna.js +395 -0
  81. package/src/game-feel/gameFeelGenerator.js +402 -0
  82. package/src/game-feel/gameFeelRuntime.js +549 -0
  83. package/src/game-feel/index.js +2 -0
  84. package/src/index.js +17 -6
  85. package/src/lighting/colorIntensity.js +177 -0
  86. package/src/lighting/index.js +161 -0
  87. package/src/lighting/lightDescriptors.js +249 -0
  88. package/src/lighting/lightingCapabilities.js +79 -0
  89. package/src/lighting/lightingDocuments.js +247 -0
  90. package/src/lighting/lightingFixtures.js +446 -0
  91. package/src/lighting/lightingGenerator.js +449 -0
  92. package/src/lighting/lightingPresets.js +319 -0
  93. package/src/lighting/lightingRuntime.js +723 -0
  94. package/src/lighting/lightingStyle.js +386 -0
  95. package/src/lighting/lightingSystem.js +774 -0
  96. package/src/lighting/unrealExport.js +186 -0
  97. package/src/lighting/utils.js +87 -0
  98. package/src/motion/index.js +5 -0
  99. package/src/motion/motionClip.js +441 -0
  100. package/src/motion/motionController.js +628 -0
  101. package/src/motion/motionDocuments.js +225 -0
  102. package/src/motion/motionGraph.js +307 -0
  103. package/src/motion/motionSettings.js +222 -0
  104. package/src/pathgen/index.js +7 -0
  105. package/src/pathgen/pathBridge.js +232 -0
  106. package/src/pathgen/pathPresets.js +35 -0
  107. package/src/pathgen/pathRibbon.js +410 -0
  108. package/src/pathgen/pathRouter.js +380 -0
  109. package/src/pathgen/pathSettings.js +335 -0
  110. package/src/pathgen/pathTextures.js +123 -0
  111. package/src/pathgen/stylizedPaths.js +453 -0
  112. package/src/post/index.js +1 -0
  113. package/src/post/postGenerator.js +177 -0
  114. package/src/post/postProcessing.js +41 -0
  115. package/src/propgen/generatorsWave1.js +379 -0
  116. package/src/propgen/generatorsWave2.js +462 -0
  117. package/src/propgen/index.js +5 -0
  118. package/src/propgen/propAsset.js +323 -0
  119. package/src/propgen/propParts.js +170 -0
  120. package/src/propgen/propPlacement.js +459 -0
  121. package/src/propgen/propPresets.js +82 -0
  122. package/src/propgen/propSettings.js +395 -0
  123. package/src/shaders-tsl/chunks/projected-water-caustics.js +242 -0
  124. package/src/shaders-tsl/chunks/vegetation-style.js +360 -0
  125. package/src/shaders-tsl/chunks/water-shore-state.js +31 -0
  126. package/src/shaders-tsl/chunks/water-waves.js +90 -11
  127. package/src/shaders-tsl/environment.js +15 -1
  128. package/src/shaders-tsl/flower.js +279 -30
  129. package/src/shaders-tsl/grass.js +60 -33
  130. package/src/shaders-tsl/sky.js +125 -31
  131. package/src/shaders-tsl/tree-leaf.js +61 -25
  132. package/src/shaders-tsl/water-breaker.js +7 -4
  133. package/src/shaders-tsl/water-shore-state-simulation.js +523 -0
  134. package/src/shaders-tsl/water.js +439 -49
  135. package/src/shaders-tsl/woody-surface.js +154 -0
  136. package/src/sky/sceneOverrideLayers.js +10 -0
  137. package/src/sky/skyQuality.js +26 -0
  138. package/src/sky/stylizedSky.js +753 -45
  139. package/src/soundscape/index.js +4 -0
  140. package/src/soundscape/soundscapeGenerator.js +179 -0
  141. package/src/soundscape/soundscapeRuntime.js +806 -0
  142. package/src/soundscape/soundscapeSettings.js +292 -0
  143. package/src/styles/index.js +13 -0
  144. package/src/styles/styleBundle.js +325 -0
  145. package/src/stylizedTerrain.js +32 -2
  146. package/src/stylizedWorld.js +423 -20
  147. package/src/texgen/evaluateTexture.js +675 -0
  148. package/src/texgen/index.js +60 -0
  149. package/src/texgen/noise2.js +210 -0
  150. package/src/texgen/textureAi.js +436 -0
  151. package/src/texgen/textureGenerators.js +516 -0
  152. package/src/texgen/texturePresets.js +490 -0
  153. package/src/texgen/textureSettings.js +342 -0
  154. package/src/texgen/textureThree.js +59 -0
  155. package/src/vegetation/flowerSpecies.js +15 -3
  156. package/src/vegetation/grassPalettes.js +153 -0
  157. package/src/vegetation/index.js +6 -0
  158. package/src/vegetation/stylizedBush.js +2 -0
  159. package/src/vegetation/stylizedFlower.js +82 -0
  160. package/src/vegetation/stylizedFlowers.js +48 -7
  161. package/src/vegetation/stylizedForest.js +29 -1
  162. package/src/vegetation/stylizedGrass.js +291 -56
  163. package/src/vegetation/stylizedTree.js +38 -2
  164. package/src/vegetation/stylizedTreeFoliage.js +2 -1
  165. package/src/vegetation/vegetationShaders.js +1110 -0
  166. package/src/vfxgen/INTEGRATION.md +145 -0
  167. package/src/vfxgen/core/burstBackbone.js +380 -0
  168. package/src/vfxgen/core/projectileCore.js +92 -0
  169. package/src/vfxgen/core/spriteShapes.js +98 -0
  170. package/src/vfxgen/core/trailRibbon.js +272 -0
  171. package/src/vfxgen/core/vfxRandom.js +29 -0
  172. package/src/vfxgen/effects/emitHelpers.js +37 -0
  173. package/src/vfxgen/effects/magicEffects.js +162 -0
  174. package/src/vfxgen/effects/movementEffects.js +87 -0
  175. package/src/vfxgen/effects/weaponEffects.js +118 -0
  176. package/src/vfxgen/index.js +18 -0
  177. package/src/vfxgen/moves/moveController.js +146 -0
  178. package/src/vfxgen/moves/moveLibrary.js +335 -0
  179. package/src/vfxgen/vfxPresets.js +98 -0
  180. package/src/vfxgen/vfxSettings.js +384 -0
  181. package/src/vfxgen/vfxSystem.js +449 -0
  182. package/src/vfxgen/weapons/stylizedWeapons.js +137 -0
  183. package/src/villagegen/index.js +4 -0
  184. package/src/villagegen/stylizedVillage.js +490 -0
  185. package/src/villagegen/villageArchetypes.js +160 -0
  186. package/src/villagegen/villageNames.js +40 -0
  187. package/src/villagegen/villageSites.js +105 -0
  188. package/src/water/sceneOverrideLayers.js +23 -0
  189. package/src/water/water.js +5 -0
  190. package/src/water/waterBreakerSystem.js +15 -1
  191. package/src/water/waterCurrentField.js +447 -0
  192. package/src/water/waterMaterial.js +12 -0
  193. package/src/water/waterNearshorePhase.js +320 -0
  194. package/src/water/waterScenePasses.js +83 -30
  195. package/src/water/waterSettings.js +325 -13
  196. package/src/water/waterShoreMaterial.js +322 -0
  197. package/src/water/waterShoreStateField.js +605 -0
  198. package/src/water/waterSurface.js +797 -28
  199. package/src/weather/index.js +6 -0
  200. package/src/weather/weatherPrecipitation.js +221 -0
  201. package/src/weather/weatherPresets.js +258 -0
  202. package/src/weather/weatherSettings.js +269 -0
  203. package/src/weather/weatherSystem.js +871 -0
  204. package/src/worldMinimap.js +62 -0
  205. package/src/worldPresets.js +4 -1
@@ -0,0 +1,77 @@
1
+ # Shader constants reference
2
+
3
+ Constants that are deliberately **not** exposed as settings — either because
4
+ they are physics, because a CPU mirror must stay in lockstep with the GPU
5
+ code, or because changing them requires re-deriving neighbors. If you need to
6
+ tune one, edit the TSL shader source or its CPU mirror; this page tells you where and what will
7
+ break.
8
+
9
+ Everything that *is* meant to be tuned lives in the settings schemas
10
+ (`TOON_SETTING_FIELD_SCHEMA`, `WATER_SETTING_FIELD_SCHEMA`,
11
+ `ENVIRONMENT_SETTING_FIELD_SCHEMA`, `POST_PROCESSING_SETTING_FIELD_SCHEMA`,
12
+ `VEGETATION_SHADER_FIELD_SCHEMA`, and the
13
+ `GRASS/FLOWER/STYLIZED_TREE/SKY_SETTING_FIELD_SCHEMA` families) and renders in the
14
+ `@call-me-sensei/toonlab/debug` panel.
15
+
16
+ ## Water
17
+
18
+ | Constant | Where | Why fixed |
19
+ |---|---|---|
20
+ | `WATER_GERSTNER_WAVE_COUNT = 8` | `src/water/waterSettings.js` + wave loops in `src/shaders-tsl/chunks/water-waves.js` | Loop count baked into the shader; `buildGerstnerWaves()` (the CPU mirror used for buoyancy/physics) must produce exactly this many waves. Changing it means editing both in lockstep. |
21
+ | Wavelength falloff `0.68`, min wavelength `0.05` | `buildGerstnerWaves()` in `waterSettings.js` | Wave-spectrum shape: each successive wave is 0.68× the previous wavelength. Part of the tuned spectrum, mirrored on the GPU. |
22
+ | Slope limit `0.4` (`amplitude * waveNumber > 0.4`) | `buildGerstnerWaves()` | Gerstner waves self-intersect (loop over) past steepness ~1; 0.4 keeps the tuned look stable at every `waveIntensity`. |
23
+ | Gravity `9.81` | `buildGerstnerWaves()` | Deep-water dispersion relation (`speed = sqrt(g/k)`). Physics. |
24
+ | Detail-normal distance fade `smoothstep(16.0, 60.0, viewDistance)` and near/far blend `mix(0.2, 1.0, detailFade)` | `src/shaders-tsl/water.js` | Screen-stability tuning: full detail near, 20% far, fading over 16–60 m. Honest candidates for uniforms later; documented here until then. |
25
+ | Detail UV advection factors `0.55` / `0.34`, second-layer rotation `1.9` rad | `src/shaders-tsl/water.js` | Two detail layers must drift at different, incommensurate rates or the surface visibly tiles. The triplet was tuned together. |
26
+ | View-depth blend `0.18` (`effectiveDepth = columnDepth + viewDepthDiff * 0.18`) | `src/shaders-tsl/chunks/water-color.js` | How much grazing-angle view depth deepens the absorption color; tuned against the shoreline film fix. |
27
+ | Absorption divisor `24.0` (`viewDepthDiff / 24.0`) | `src/shaders-tsl/chunks/water-color.js` | Far-field opacity floor so open water never goes glassy at distance. |
28
+
29
+ Quality tiers (`WATER_DETAIL_OCTAVES`, `WATER_FOAM_OCTAVES`) **are** exposed:
30
+ pass `quality: 'low'|'medium'|'high'` or a custom
31
+ `{ detailOctaves, foamOctaves, qualityLevel }` — see
32
+ `resolveWaterQualityDefines()` in `src/water/waterMaterial.js`.
33
+
34
+ ## Character (toon)
35
+
36
+ | Constant | Where | Why fixed |
37
+ |---|---|---|
38
+ | Bayer 4×4 dither matrix | `src/shaders-tsl/chunks/character-color.js` | Screen-door fade pattern (`alpha.ditherOpacity`); the matrix itself is canonical. |
39
+ | Cel edge anti-alias derivative math | `src/shaders-tsl/chunks/character-lighting.js` | Driven by `celShade.edgeAntiAliasStrength` (exposed); the fwidth-based widening formula is not a tunable. |
40
+ | Self-shadow PCF tap offsets (4/9-tap) | `characterRenderPasses.js` + shadow chunks | Kernel shape; strength/bias are exposed via `selfShadow.*`. |
41
+
42
+ ## Environment
43
+
44
+ | Constant | Where | Why fixed |
45
+ |---|---|---|
46
+ | Lightmap painterly remap (warm dark end, lift floor) | `src/shaders-tsl/chunks/environment-lighting.js` | The remap curve IS the art direction; its inputs (strengths, tints) are exposed in `environmentSettings`. |
47
+ | Vertex-AO golden-spiral hemisphere sample set | `src/environment/environmentVertexAo.js` | Deterministic sampling pattern — changing it invalidates every baked result. Budget/strength are exposed. |
48
+
49
+ ## Vegetation / sky
50
+
51
+ Formerly hardcoded uniforms (grass cloud-shadow coverage/scale/velocity,
52
+ backlit strength, tree gnarl frequencies/amplitude, foliage shadow strengths)
53
+ are now settings — see the vegetation/sky schemas. What remains fixed:
54
+
55
+ | Constant | Where | Why fixed |
56
+ |---|---|---|
57
+ | Grass blade segment count / geometry proportions | `stylizedGrass.js` geometry builder | Baked into instanced geometry at construction; density/height ARE settings (construction-time). |
58
+ | Tree trunk ring/segment tessellation | `stylizedTree.js` | Geometry topology; regenerating is the designed path (see the tree recipe system). |
59
+ | Sky far-plane clip epsilon and horizon branch guards | `src/shaders-tsl/sky.js` | Numerical/render-order safeguards, not art direction. Dome radius remains constructor-compatible but is visually invariant because the vertex stage pins the dome to the far plane. |
60
+ | Sky FBM implementation and deployment octave tiers | `src/shaders-tsl/sky.js`, `src/sky/skyQuality.js`, `chunks/water-common.js` | Noise/hash internals define deterministic parity. Shipped quality tiers compile 2/3/4 octaves (`low`/`medium`/`high`), with 1–5 custom; quality stays outside the portable art preset while cloud shape, seed, projection, softness, shading, and motion are exposed. |
61
+ | Sky coverage calibration and projection singularity guards | `src/shaders-tsl/sky.js` | Stable mapping from the authored coverage/altitude controls to safe procedural thresholds. All visually meaningful colors, curve shapes, sun/glow terms, cloud treatment, and star treatment are schema fields. |
62
+
63
+ ## Post-processing
64
+
65
+ All colors, strengths, and thresholds are exposed
66
+ (`POST_PROCESSING_SETTING_FIELD_SCHEMA`, 29 parameters). The bloom pyramid's
67
+ level count is exposed (`bloomLevels`, 2–8); the downsample filter weights are
68
+ not (standard dual-filter kernel).
69
+
70
+ ## TSL texture binding budget
71
+
72
+ The raw GLSL `USE_*` define plumbing was removed with Phase 11, but the
73
+ binding budget did not disappear. The app currently requests default WebGPU
74
+ limits, so native WebGPU and the forced WebGL2 fallback both need the same
75
+ 16-sampled-texture discipline. Optional maps must stay graph-gated in
76
+ `src/shaders-tsl/anime.js`, `src/shaders-tsl/environment.js`, and their chunks:
77
+ only construct/sample a texture node when the corresponding map exists.
package/docs/sky.md ADDED
@@ -0,0 +1,182 @@
1
+ # Sky system
2
+
3
+ `@call-me-sensei/toonlab/sky` provides a procedural stylized sky for Three.js:
4
+ a three-stop vertical dome, sun disc and glow, sun-side horizon scattering,
5
+ two-tone painterly clouds, and a procedural star field. It uses the same TSL
6
+ material on WebGPU and the WebGL2 fallback and requires no texture assets.
7
+
8
+ Sky is a World System in ToonLab. Its appearance shader and procedural motion
9
+ ship as one versioned preset because the cloud, sun, horizon, and star terms
10
+ must be judged together. It is not a fourth IP-wide Shader Lab; see
11
+ [Lab responsibilities](lab-architecture.md).
12
+
13
+ ## Quickstart
14
+
15
+ ```js
16
+ import { StylizedSky } from '@call-me-sensei/toonlab/sky';
17
+
18
+ const sky = new StylizedSky({
19
+ preset: 'call_me_sensei',
20
+ quality: 'high', // deployment policy; not saved in the art preset
21
+ });
22
+ scene.add(sky);
23
+
24
+ renderer.setAnimationLoop(() => {
25
+ const delta = clock.getDelta();
26
+ sky.update(delta, camera); // animates clouds/stars and follows the camera
27
+ renderer.render(scene, camera);
28
+ });
29
+
30
+ // Live runtime retuning does not rebuild the material.
31
+ sky.applySettings({ cloudCoverage: 0.55, starsStrength: 0.2 });
32
+ sky.setPreset('golden_hour'); // replaces the authored look from scratch
33
+ ```
34
+
35
+ `StylizedSky` centers its dome on the camera and pins it to the far plane, so it
36
+ does not intersect scene geometry. Add the same sky to scenes containing a
37
+ `WaterSurface`; water reflection passes then capture the active composed sky rather
38
+ than relying on a separate color approximation.
39
+
40
+ ## Settings and scope
41
+
42
+ `createSkySettings(options)` normalizes flat settings. The public
43
+ `SKY_SETTING_GROUPS` and `SKY_SETTING_FIELD_SCHEMA` drive Sky Lab and custom
44
+ editors. The schema contains 46 portable art fields; the constructor-only dome
45
+ radius is the single non-portable field.
46
+
47
+ | Group | Owns |
48
+ |---|---|
49
+ | Gradient | Zenith, horizon, and below-horizon colors; zenith/ground curve shape; horizon-band width, sun focus, and scattering strength. |
50
+ | Sun | Baseline disc direction and color; size, edge softness, intensity; broad/core glow shape; dense-cloud occlusion. |
51
+ | Clouds | Coverage, scale, deterministic seed, projection, silhouette softness/opacity, direction and speed, two-tone shade controls, light-sample depth, silver lining, and horizon fade. |
52
+ | Stars | Strength, color, deterministic seed, density, pattern scale, glint size, twinkle depth/speed, and horizon fade. |
53
+
54
+ The runtime constructor still accepts `radius` for compatibility. Because the
55
+ dome is centered on the camera and pinned to the far plane, changing that
56
+ geometry radius does not change the rendered look. Sky Lab therefore does not
57
+ surface it, and portable Sky preset documents do not serialize it.
58
+
59
+ Sky settings do not own the current clock, directional-light intensity or
60
+ shadow policy, precipitation, weather transition, fog volume, exposure, or
61
+ camera. Lighting and Weather systems remain authoritative and may modulate the
62
+ active sky at runtime. Independent owners use ordered layers rather than
63
+ writing the same settings object:
64
+
65
+ ```js
66
+ import { SKY_SCENE_OVERRIDE_PRIORITIES } from '@call-me-sensei/toonlab/sky';
67
+
68
+ const lightingLayer = Symbol('lighting');
69
+ const weatherLayer = Symbol('weather');
70
+
71
+ sky.setSceneOverrideLayer(lightingLayer, {
72
+ sunDirection: lightingState.sunDirection,
73
+ zenithColor: lightingState.zenithColor,
74
+ }, { priority: SKY_SCENE_OVERRIDE_PRIORITIES.lighting });
75
+
76
+ // A resolver receives the result of lower-priority layers. Rain therefore
77
+ // darkens the current time-of-day instead of replacing it with a stale color.
78
+ sky.setSceneOverrideLayer(weatherLayer, (base) => ({
79
+ cloudCoverage: weatherState.atmosphere.cloudCoverage,
80
+ zenithColor: base.zenithColor.map((channel) => channel * weatherSkyScale),
81
+ }), { priority: SKY_SCENE_OVERRIDE_PRIORITIES.weather });
82
+
83
+ sky.clearSceneOverrideLayer(weatherLayer); // Lighting remains active
84
+ ```
85
+
86
+ Layers compose in ascending priority: Lighting (100), Weather (200), then the
87
+ manual scene layer (300). `applySettings()` edits the authored baseline and
88
+ recomposes every active resolver; `setPreset(name, overrides)` replaces that
89
+ baseline. `sky.settings` is always authored data, while
90
+ `sky.renderedSettings`, `sky.sceneOverrides`, and `sky.sceneOverrideLayers`
91
+ expose the effective runtime state.
92
+
93
+ `setSceneOverrides()` is the convenience manual layer and
94
+ `clearSceneOverrides()` removes only that layer. Named owners must call
95
+ `clearSceneOverrideLayer(id)` so they cannot erase each other;
96
+ `clearAllSceneOverrideLayers()` is reserved for a host that explicitly owns
97
+ the complete teardown. `LightingSystem.attachWorld(world)` and
98
+ `WeatherSystem` use private layers and release only their own state.
99
+
100
+ The visible dome clouds and the world-projected cloud-shadow field are related
101
+ weather signals, not the same procedural texture: the dome is angular and the
102
+ shadow field is spatial over terrain, water, and vegetation. Weather coordinates
103
+ coverage, wind, and intensity policy; games that require exact cloud-to-shadow
104
+ registration should supply a shared host cloud-field adapter.
105
+
106
+ ## Deployment quality
107
+
108
+ Sky quality is runtime policy, not art direction. `quality: 'low' | 'medium' |
109
+ 'high'` compiles two, three, or four FBM octaves respectively for each cloud
110
+ sample. The graph is compile-time-unrolled, so `sky.setQuality(tier)` rebuilds
111
+ only the Sky material while preserving authored settings, active scene layers,
112
+ and animation time. Custom policies may pass `{ cloudOctaves: 1..5 }`.
113
+
114
+ `SKY_QUALITY_TIERS`, `SKY_QUALITY_OPTIONS`, and `resolveSkyQuality()` are public.
115
+ Quality is a preview control in Sky Lab and is deliberately absent from
116
+ portable Sky preset documents.
117
+
118
+ ## Presets and portable documents
119
+
120
+ Built-in presets include `default`, `call_me_sensei`, `clear_day`,
121
+ `golden_hour`, `overcast`, and `moonlit`. `getSkyPresetOptions()` lists them
122
+ and any project registrations.
123
+
124
+ ```js
125
+ import {
126
+ createSkyPresetDocument,
127
+ createSkySettings,
128
+ parseSkyPresetDocument,
129
+ registerSkyPreset,
130
+ registerSerializedSkyPreset,
131
+ serializeSkyPreset,
132
+ } from '@call-me-sensei/toonlab/sky';
133
+
134
+ registerSkyPreset('violet_twilight', {
135
+ label: 'Violet Twilight',
136
+ settings: {
137
+ zenithColor: [0.12, 0.08, 0.32],
138
+ horizonColor: [0.88, 0.38, 0.5],
139
+ starsStrength: 0.35,
140
+ },
141
+ });
142
+
143
+ const document = createSkyPresetDocument('violet_twilight', {
144
+ label: 'Violet Twilight',
145
+ settings: createSkySettings('violet_twilight'),
146
+ });
147
+ const json = serializeSkyPreset(document);
148
+ const result = parseSkyPresetDocument(json);
149
+ if (result.ok) registerSerializedSkyPreset(json, { overwrite: true });
150
+ ```
151
+
152
+ Documents use `{ type: 'toonlab/sky-preset', version, id, label,
153
+ description, settings }`. Use `validateSkyPresetDocument` when a parsed object
154
+ is already available. Documents contain complete normalized appearance
155
+ settings, so they remain portable if a named base preset changes later.
156
+
157
+ ## Low-level material API
158
+
159
+ Applications that own their dome mesh can use the same production material:
160
+
161
+ ```js
162
+ import {
163
+ applySkySettingsToMaterial,
164
+ createSkyMaterial,
165
+ } from '@call-me-sensei/toonlab/sky';
166
+
167
+ const material = createSkyMaterial({ preset: 'moonlit' });
168
+ applySkySettingsToMaterial(material, { starsStrength: 1.2 });
169
+ applySkySettingsToMaterial(material, { preset: 'clear_day' }); // full reset
170
+ ```
171
+
172
+ `createSkyMaterial` returns the TSL node material used by `StylizedSky`.
173
+ `applySkySettingsToMaterial` updates the shared uniform contract without
174
+ replacing the material.
175
+
176
+ ## Sky Lab
177
+
178
+ Run `/sky-lab/` in the repository or `/labs/sky` on ToonLab Pro. The lab uses
179
+ the npm settings schema directly and supports preset selection, undo/redo,
180
+ local saves, JSON import/export, WebGPU/WebGL comparison, and preview-only
181
+ weather/lighting fixtures. Exported documents contain all 46 reusable
182
+ sky-system art fields and no current scene state or quality tier.
@@ -0,0 +1,309 @@
1
+ # Generative style domains
2
+
3
+ ToonLab's style domains are configuration authoring tools, not small preset
4
+ catalogs. A developer defines a domain, chooses a nonzero seed in the 32-bit seed
5
+ space, locks the decisions that already work, and keeps generating. The same
6
+ recipe can therefore produce thousands or millions of reproducible candidates,
7
+ and applications can register new families, operators, graph nodes, events, and
8
+ layer types without changing ToonLab's built-in lists.
9
+
10
+ ## Status (2026-07 triage)
11
+
12
+ The dedicated browser labs for these domains were removed from the labs grid:
13
+ behavioral domains (camera, game feel, motion) cannot be judged against a demo
14
+ stage, and the visual ones are curated via MCP batches + review instead. The
15
+ domains themselves were triaged by one test — *does this simplify a Three.js
16
+ game developer's life today?*
17
+
18
+ | Domain | npm export | Lab | Notes |
19
+ |---|---|---|---|
20
+ | Post & color | ✅ `./post` | removed (future feature) | Curate presets via MCP; needs 3–5 owner-reviewed presets |
21
+ | Camera | ✅ `./camera` | removed (future feature) | Ships as a code library; needs README quickstart |
22
+ | Game feel | ✅ `./game-feel` | removed (future feature) | Ships as a code library; needs README quickstart |
23
+ | Lighting | ✅ `./lighting` | ✅ `/lighting-lab/` | Styles + fixtures; see [lighting.md](lighting.md) |
24
+ | Motion | ⏸ held (source at `src/motion/`) | removed (future feature) | Returns when a demo drives real GLTF clips end-to-end |
25
+ | Soundscape | ⏸ held (source at `src/soundscape/`) | removed (future feature) | Rethink as adaptive mixing over curated audio assets |
26
+ | Biome | ❌ cut (source at `src/biome/`) | removed (future feature) | Value lives in `stylizedTerrain`/`stylizedWorld`, already exported |
27
+ | UI theme | ❌ removed entirely | removed (future feature) | Out of scope for a Three.js game library; recoverable from history |
28
+
29
+ The product boundary is:
30
+
31
+ ```mermaid
32
+ flowchart LR
33
+ M["Local MCP server"] --> R["Portable generator recipe"]
34
+ R --> G["Deterministic seed resolution"]
35
+ G --> P["Flat runtime preset"]
36
+ P --> N["npm package runtime"]
37
+ N --> F["Game frame loop"]
38
+ ```
39
+
40
+ - MCP provides design-time recipe creation, validation, batch generation, and
41
+ `.toonlab/` persistence to coding agents.
42
+ - The npm package owns shipping runtime behavior. It accepts resolved presets,
43
+ enforces relevant quality budgets, exposes update/configure/regenerate and
44
+ dispose lifecycle methods, and never depends on a lab UI.
45
+
46
+ ## One open generator contract
47
+
48
+ Every generator recipe is a versioned JSON document with the same core fields:
49
+
50
+ ```js
51
+ {
52
+ type: 'toonlab/biome-generator',
53
+ version: 1,
54
+ id: 'painted-valley',
55
+ label: 'Painted Valley',
56
+ seed: 4635,
57
+ basePreset: 'outdoorGameplay',
58
+ configuration: {},
59
+ domains: {
60
+ terrain: {
61
+ morphology: {
62
+ rolling: {
63
+ amp: { $type: 'range', min: 8, max: 42, step: 0.25 },
64
+ },
65
+ },
66
+ },
67
+ },
68
+ locks: ['terrain.palette.meadow'],
69
+ }
70
+ ```
71
+
72
+ Domain leaves support continuous ranges, weighted choices, booleans, colors,
73
+ and constants. A domain can be narrowed, widened, replaced, or extended with
74
+ new paths. Named random streams make a result deterministic and keep unrelated
75
+ settings stable when another branch is added. Locks preserve selected paths
76
+ while the remaining paths continue to vary.
77
+
78
+ The result of generation is a flat, versioned preset. Runtime code does not
79
+ sample a domain every frame. This keeps shipping behavior deterministic,
80
+ serializable, debuggable, and inexpensive.
81
+
82
+ ## The domains
83
+
84
+ ### Post & color
85
+
86
+ **Shipped in npm; lab removed (future feature).** Authors color grade, bloom,
87
+ outline, vignette, depth cue, motion blur, and vertical grade together so the
88
+ result reads as one look.
89
+
90
+ - Open generation: every feature flag and numeric/color parameter is a domain;
91
+ custom post families and ordinary runtime presets are registerable.
92
+ - Runtime: `createPostProcessingPipeline` applies the resolved settings, lazily
93
+ allocates the pyramid bloom chain, skips inactive passes, reports render-target
94
+ statistics, resizes safely, and disposes owned GPU resources.
95
+ - Budgets: mobile removes depth-heavy effects and limits bloom; balanced limits
96
+ simultaneous depth consumers; cinematic preserves the authored intent.
97
+
98
+ Import from `@call-me-sensei/toonlab/post`.
99
+
100
+ ### Camera
101
+
102
+ **Shipped in npm; lab removed (future feature).** Builds a stack of camera
103
+ operators instead of selecting one hard-coded camera type. Follow, framing,
104
+ collision, damping, procedural noise, impulses, and lens behavior can be
105
+ composed or extended.
106
+
107
+ - Open generation: archetypes seed an editable domain; new generator
108
+ archetypes and runtime operator factories can be registered.
109
+ - Runtime: `createCameraRig` evaluates the operator stack without editor state;
110
+ `createCameraDirector` blends between rigs and `addImpulse` handles event
111
+ response without coupling gameplay code to camera math.
112
+ - Scale: one resolved operator stack is evaluated per active camera. Noise and
113
+ impulses are time-based and no candidate generation occurs in the frame loop.
114
+
115
+ Import from `@call-me-sensei/toonlab/camera`.
116
+
117
+ ### Motion (held — future feature)
118
+
119
+ **Held out of the npm exports; lab removed.** The runtime survives at
120
+ `src/motion/` (verified by `verify:motion`) but does not ship until a demo
121
+ drives real GLTF clips on a real character end-to-end. Authors an arbitrary
122
+ animation graph, not a finite animation menu. Clip slots keep game assets
123
+ separate from reusable locomotion logic.
124
+
125
+ - Open generation: recursive 1D/2D/weighted blend nodes, any number of states,
126
+ transitions, parameters, layers, masks, and clip slots; procedural harmonic,
127
+ keyframe, and Three.js clip samplers share one pose contract.
128
+ - Runtime: `createMotionController` evaluates transitions and layered poses,
129
+ supports root-motion policies and stepped/smooth cadence, and applies results
130
+ through a replaceable rig adapter.
131
+ - Scale: graph validation catches missing slots and transition references
132
+ before runtime. Generation changes timing/style parameters while preserving
133
+ arbitrary user graph topology.
134
+
135
+ ### UI Theme Lab (removed — future feature)
136
+
137
+ UI theming (semantic token generation, contrast auditing, scoped CSS export)
138
+ was removed from the package and the labs grid: it serves web UI authoring, not
139
+ Three.js game development, so it sits outside ToonLab's scope. The domain may
140
+ return as a separate product if game-HUD theming becomes a real need. The last
141
+ implementation lived at `src/ui-theme/` + `labs/ui-theme-lab/` (removed
142
+ 2026-07; recoverable from history).
143
+
144
+ ### Biome (cut — future feature)
145
+
146
+ **Export cut; lab removed.** The runtime at `src/biome/` is a lifecycle wrapper
147
+ that hard-requires ToonLab's own renderer/scene, so it is not consumable as a
148
+ standalone package export; the underlying value (`stylizedTerrain`,
149
+ `stylizedWorld`) is already exported from the package root. Treated a biome as
150
+ continuous terrain morphology plus a linked palette, water, atmosphere, and
151
+ vegetation system.
152
+
153
+ - Open generation: terrain height/depth/frequency/terracing/islands, palette
154
+ endpoints, water colors, fog, grass/flower/tree density and size, and wind
155
+ are domain values; new terrain archetypes and biome families are registerable.
156
+ - Runtime: `createBiomeRuntime` constructs the terrain and stylized world,
157
+ exposes race-safe asynchronous regeneration, updates world systems, reports
158
+ scene statistics, and owns disposal.
159
+ - Budgets: mobile, balanced, and cinematic cap terrain segments and vegetation
160
+ radius/density/spacing so authored density never defeats the target device.
161
+
162
+ ### Soundscape (held — future feature)
163
+
164
+ **Held out of the npm exports; lab removed.** The runtime survives at
165
+ `src/soundscape/` (verified by `verify:soundscape`) but purely synthesized
166
+ ambience has a quality ceiling below the asset bar; the likely return path is
167
+ adaptive mixing over curated audio assets. A procedural audio graph that can
168
+ use synthesized layers immediately and resolve project audio through an asset
169
+ resolver later.
170
+
171
+ - Open generation: any number of buses, layers, adaptive parameters,
172
+ modulation mappings, snapshots, and transitions; both generator families and runtime
173
+ layer factories are registerable.
174
+ - Runtime: `createSoundscapeRuntime` creates its `AudioContext` lazily after a
175
+ user gesture, owns Web Audio nodes, transitions without rebuilding every
176
+ frame, exposes adaptive inputs, and disposes or closes owned resources.
177
+ - Budgets: quality tiers cap nodes, layers, and voices. The runtime reports
178
+ skipped layers rather than silently exceeding its budget.
179
+
180
+ ### Game feel
181
+
182
+ **Shipped in npm; lab removed (future feature).** Maps named gameplay events to
183
+ coordinated response graphs. It is the integration layer for impact, not
184
+ another VFX asset catalog.
185
+
186
+ - Open generation: event definitions and effect factories are registries;
187
+ projects can add `parry`, `harvest`, `dialogueBeat`, or any other event and
188
+ compose built-in camera punch, hit-stop/time warp, squash, flash, audio, and
189
+ haptics; custom effect factories can connect project VFX or particles.
190
+ - Runtime: `createGameFeelRuntime` schedules effects on scaled and unscaled
191
+ clocks, enforces cooldown/concurrency rules, returns gameplay delta after
192
+ time effects, and drives replaceable adapters instead of assuming a renderer
193
+ or input library.
194
+ - Safety: haptics and audio are capability-gated; an adapter returning `false`
195
+ explicitly declines so fallback diagnostics remain honest. Unsupported
196
+ effects do not consume supported-effect capacity, errors are isolated,
197
+ trigger/drop statistics are exposed, and disposal restores time/camera/UI
198
+ state. Runtime quality tiers can tighten recipes, resolved presets, or raw
199
+ settings.
200
+
201
+ Import from `@call-me-sensei/toonlab/game-feel`.
202
+
203
+ ### Lighting Lab
204
+
205
+ `/lighting-lab/` authors the game's lighting identity as two generative
206
+ artifacts instead of per-scene light configuration: **lighting styles**
207
+ (`toonlab/lighting-style` — the full day as one curve: sun kelvin/intensity
208
+ per hour, sun path, ambient policy, fog palette, exposure philosophy, shadow
209
+ policy) and **light fixtures** (`toonlab/light-fixture` — reusable practicals
210
+ like street lamps and lanterns with seeded per-placement variation, flicker,
211
+ and day/night schedules).
212
+
213
+ - Open generation: style families (`anime-day`, `call-me-sensei`, `golden`,
214
+ `noir-neon`, `pastel-overcast`) and fixture families (`warm-practical`,
215
+ `cms-practical`, `flame`, `neon`) are registries on the shared domain
216
+ grammar; subtree locks (`sun`, `atmosphere`, `exposure`) survive reseeds.
217
+ - Runtime: `createLightingSystem` applies a style + fixtures to any scene or
218
+ stylized world — `setTimeOfDay(hour)` moves the whole look along the day
219
+ cycle, `place(fixture, position, { seed })` realizes budget-managed lights
220
+ with deterministic variation, `setWeatherModulation` is the single hook for
221
+ weather, and `dispose()` restores captured fog/exposure/sun state. Area
222
+ lights gate on LTC lookup-texture loading instead of crashing node backends.
223
+ - Preview scenes: outdoor, interior, and city-night stages with a time-of-day
224
+ scrubber and weather modulation presets, so a style is judged standing next
225
+ to a lantern at night, not only at editor noon.
226
+
227
+ Import from `@call-me-sensei/toonlab/lighting`.
228
+
229
+ ## MCP authoring
230
+
231
+ The local MCP server exposes four tools shared by the shipped domains (post,
232
+ camera, game feel, lighting styles, light fixtures):
233
+
234
+ - `list_style_labs` returns runtime imports/APIs, extension families,
235
+ and generation capabilities.
236
+ - `create_style_recipe` creates and optionally saves a recipe below
237
+ `.toonlab/creations/`.
238
+ - `generate_style_presets` resolves one to 64 consecutive seeds in one call,
239
+ validates every result, and optionally saves the deterministic batch.
240
+ - `validate_style_document` validates either an editable recipe or flat preset.
241
+
242
+ For example, an agent can create a broad post-look recipe, lock the grade after
243
+ review, generate seeds 2000–2063, and save the batch without inventing a finite
244
+ catalog of look names. See [Local MCP and workspace](mcp.md) for connection
245
+ instructions.
246
+
247
+ ## Runtime example
248
+
249
+ ```js
250
+ import {
251
+ createPostGeneratorRecipe,
252
+ createGeneratedPostPresetDocument,
253
+ createPostProcessingPipeline,
254
+ } from '@call-me-sensei/toonlab/post';
255
+
256
+ // Usually authored and saved via MCP.
257
+ const recipe = createPostGeneratorRecipe('soft-anime', { seed: 4635 });
258
+ const preset = createGeneratedPostPresetDocument(recipe, { quality: 'balanced' });
259
+
260
+ const post = createPostProcessingPipeline({
261
+ camera,
262
+ renderer,
263
+ scene,
264
+ settings: preset.settings,
265
+ });
266
+
267
+ renderer.setAnimationLoop(() => {
268
+ post.render(clock.getDelta());
269
+ });
270
+
271
+ // post.dispose();
272
+ ```
273
+
274
+ Applications may ship recipes when runtime re-rolling is a feature, or resolve
275
+ and check in presets when exact art direction is required. Both paths use the
276
+ same npm implementation.
277
+
278
+ ## Engine practices adopted
279
+
280
+ The architecture follows useful patterns from established engines while
281
+ remaining native to Three.js:
282
+
283
+ - Unreal Engine's Procedural Content Generation framework separates reusable
284
+ graphs, subgraphs, inspection/debugging, and editor/runtime generation. The
285
+ ToonLab equivalent is an extensible recipe domain plus a flat inspected
286
+ result.
287
+ - Unreal Engine MetaSounds treats audio as a procedural runtime graph with
288
+ reusable patches and parameterized inputs. Soundscape uses the same graph and
289
+ registry mindset with explicit Web Audio budgets.
290
+ - Unity Volume profiles separate reusable effect settings from local runtime
291
+ overrides. ToonLab recipes, resolved presets, locks, and quality overrides
292
+ serve the same separation.
293
+ - Unity Cinemachine composes camera behaviors and provides separate impulse and
294
+ noise systems. ToonLab's camera domain uses an operator stack with event-driven impulses
295
+ and deterministic noise.
296
+ - Unity Audio Mixer snapshots support controlled transitions between mixes;
297
+ Soundscape transitions buses and layers rather than hard-switching graphs.
298
+ - Unity's Input System makes haptic support an optional device capability;
299
+ Game Feel uses adapters and capability checks rather than assuming a gamepad.
300
+
301
+ Primary references:
302
+
303
+ - [Unreal Engine Procedural Content Generation Framework](https://dev.epicgames.com/documentation/en-us/unreal-engine/procedural-content-generation-framework-in-unreal-engine)
304
+ - [Unreal Engine MetaSounds](https://dev.epicgames.com/documentation/unreal-engine/metasounds-the-next-generation-sound-sources-in-unreal-engine?lang=en-US)
305
+ - [Unity URP Volumes](https://docs.unity3d.com/ja/Packages/com.unity.render-pipelines.universal%4014.0/manual/Volumes.html)
306
+ - [Unity Cinemachine](https://docs.unity3d.com/ja/current/Manual/com.unity.cinemachine.html)
307
+ - [Unity Cinemachine Impulse](https://docs.unity3d.com/ja/Packages/com.unity.cinemachine%402.6/manual/CinemachineImpulse.html)
308
+ - [Unity Audio Mixer snapshot transitions](https://docs.unity3d.com/jp/current/ScriptReference/Audio.AudioMixer.TransitionToSnapshots.html)
309
+ - [Unity Input System gamepad haptics](https://docs.unity3d.com/ja/Packages/com.unity.inputsystem%401.4/api/UnityEngine.InputSystem.Gamepad.html)