@call-me-sensei/toonlab 0.1.1 → 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 (213) hide show
  1. package/AGENTS.md +254 -0
  2. package/ATTRIBUTION.md +41 -3
  3. package/README.md +300 -27
  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 +66 -6
  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/debrisgen/debrisPresets.js +7 -0
  72. package/src/environment/environmentPresets.js +19 -0
  73. package/src/environment/environmentRigs.js +21 -1
  74. package/src/environment/environmentSettings.js +14 -0
  75. package/src/environment/environmentSunShadowPass.js +8 -0
  76. package/src/environment/environmentTextureResolver.js +3 -0
  77. package/src/environment/index.js +1 -0
  78. package/src/fauna/INTEGRATION.md +174 -0
  79. package/src/fauna/boids.js +861 -0
  80. package/src/fauna/faunaBodies.js +492 -0
  81. package/src/fauna/faunaPresets.js +52 -0
  82. package/src/fauna/faunaSettings.js +525 -0
  83. package/src/fauna/index.js +5 -0
  84. package/src/fauna/stylizedFauna.js +395 -0
  85. package/src/game-feel/gameFeelGenerator.js +402 -0
  86. package/src/game-feel/gameFeelRuntime.js +549 -0
  87. package/src/game-feel/index.js +2 -0
  88. package/src/index.js +22 -4
  89. package/src/lighting/colorIntensity.js +177 -0
  90. package/src/lighting/index.js +161 -0
  91. package/src/lighting/lightDescriptors.js +249 -0
  92. package/src/lighting/lightingCapabilities.js +79 -0
  93. package/src/lighting/lightingDocuments.js +247 -0
  94. package/src/lighting/lightingFixtures.js +446 -0
  95. package/src/lighting/lightingGenerator.js +449 -0
  96. package/src/lighting/lightingPresets.js +319 -0
  97. package/src/lighting/lightingRuntime.js +723 -0
  98. package/src/lighting/lightingStyle.js +386 -0
  99. package/src/lighting/lightingSystem.js +774 -0
  100. package/src/lighting/unrealExport.js +186 -0
  101. package/src/lighting/utils.js +87 -0
  102. package/src/motion/index.js +5 -0
  103. package/src/motion/motionClip.js +441 -0
  104. package/src/motion/motionController.js +628 -0
  105. package/src/motion/motionDocuments.js +225 -0
  106. package/src/motion/motionGraph.js +307 -0
  107. package/src/motion/motionSettings.js +222 -0
  108. package/src/pathgen/index.js +7 -0
  109. package/src/pathgen/pathBridge.js +232 -0
  110. package/src/pathgen/pathPresets.js +35 -0
  111. package/src/pathgen/pathRibbon.js +410 -0
  112. package/src/pathgen/pathRouter.js +380 -0
  113. package/src/pathgen/pathSettings.js +335 -0
  114. package/src/pathgen/pathTextures.js +123 -0
  115. package/src/pathgen/stylizedPaths.js +453 -0
  116. package/src/post/index.js +1 -0
  117. package/src/post/postGenerator.js +177 -0
  118. package/src/post/postProcessing.js +69 -0
  119. package/src/propgen/generatorsWave1.js +379 -0
  120. package/src/propgen/generatorsWave2.js +462 -0
  121. package/src/propgen/index.js +5 -0
  122. package/src/propgen/propAsset.js +323 -0
  123. package/src/propgen/propParts.js +170 -0
  124. package/src/propgen/propPlacement.js +459 -0
  125. package/src/propgen/propPresets.js +82 -0
  126. package/src/propgen/propSettings.js +395 -0
  127. package/src/rockgen/rockgenPresets.js +8 -0
  128. package/src/rockgen/rockgenSettings.js +47 -0
  129. package/src/shaders-tsl/chunks/projected-water-caustics.js +242 -0
  130. package/src/shaders-tsl/chunks/vegetation-style.js +360 -0
  131. package/src/shaders-tsl/chunks/water-shore-state.js +31 -0
  132. package/src/shaders-tsl/chunks/water-waves.js +90 -11
  133. package/src/shaders-tsl/environment.js +75 -1
  134. package/src/shaders-tsl/flower.js +279 -30
  135. package/src/shaders-tsl/grass.js +60 -33
  136. package/src/shaders-tsl/sky.js +125 -31
  137. package/src/shaders-tsl/tree-leaf.js +61 -25
  138. package/src/shaders-tsl/water-breaker.js +7 -4
  139. package/src/shaders-tsl/water-shore-state-simulation.js +523 -0
  140. package/src/shaders-tsl/water.js +488 -45
  141. package/src/shaders-tsl/woody-surface.js +154 -0
  142. package/src/sky/sceneOverrideLayers.js +10 -0
  143. package/src/sky/skyQuality.js +26 -0
  144. package/src/sky/stylizedSky.js +776 -20
  145. package/src/soundscape/index.js +4 -0
  146. package/src/soundscape/soundscapeGenerator.js +179 -0
  147. package/src/soundscape/soundscapeRuntime.js +806 -0
  148. package/src/soundscape/soundscapeSettings.js +292 -0
  149. package/src/styles/index.js +13 -0
  150. package/src/styles/styleBundle.js +325 -0
  151. package/src/stylizedTerrain.js +631 -0
  152. package/src/stylizedWorld.js +903 -0
  153. package/src/texgen/evaluateTexture.js +675 -0
  154. package/src/texgen/index.js +60 -0
  155. package/src/texgen/noise2.js +210 -0
  156. package/src/texgen/textureAi.js +436 -0
  157. package/src/texgen/textureGenerators.js +516 -0
  158. package/src/texgen/texturePresets.js +490 -0
  159. package/src/texgen/textureSettings.js +342 -0
  160. package/src/texgen/textureThree.js +59 -0
  161. package/src/vegetation/flowerSpecies.js +15 -3
  162. package/src/vegetation/grassPalettes.js +153 -0
  163. package/src/vegetation/index.js +8 -0
  164. package/src/vegetation/scatter.js +297 -0
  165. package/src/vegetation/stylizedBush.js +2 -0
  166. package/src/vegetation/stylizedFlower.js +82 -0
  167. package/src/vegetation/stylizedFlowers.js +96 -8
  168. package/src/vegetation/stylizedForest.js +476 -0
  169. package/src/vegetation/stylizedGrass.js +331 -48
  170. package/src/vegetation/stylizedTree.js +109 -4
  171. package/src/vegetation/stylizedTreeFoliage.js +13 -3
  172. package/src/vegetation/vegetationShaders.js +1110 -0
  173. package/src/vfxgen/INTEGRATION.md +145 -0
  174. package/src/vfxgen/core/burstBackbone.js +380 -0
  175. package/src/vfxgen/core/projectileCore.js +92 -0
  176. package/src/vfxgen/core/spriteShapes.js +98 -0
  177. package/src/vfxgen/core/trailRibbon.js +272 -0
  178. package/src/vfxgen/core/vfxRandom.js +29 -0
  179. package/src/vfxgen/effects/emitHelpers.js +37 -0
  180. package/src/vfxgen/effects/magicEffects.js +162 -0
  181. package/src/vfxgen/effects/movementEffects.js +87 -0
  182. package/src/vfxgen/effects/weaponEffects.js +118 -0
  183. package/src/vfxgen/index.js +18 -0
  184. package/src/vfxgen/moves/moveController.js +146 -0
  185. package/src/vfxgen/moves/moveLibrary.js +335 -0
  186. package/src/vfxgen/vfxPresets.js +98 -0
  187. package/src/vfxgen/vfxSettings.js +384 -0
  188. package/src/vfxgen/vfxSystem.js +449 -0
  189. package/src/vfxgen/weapons/stylizedWeapons.js +137 -0
  190. package/src/villagegen/index.js +4 -0
  191. package/src/villagegen/stylizedVillage.js +490 -0
  192. package/src/villagegen/villageArchetypes.js +160 -0
  193. package/src/villagegen/villageNames.js +40 -0
  194. package/src/villagegen/villageSites.js +105 -0
  195. package/src/water/sceneOverrideLayers.js +23 -0
  196. package/src/water/water.js +5 -0
  197. package/src/water/waterBreakerSystem.js +15 -1
  198. package/src/water/waterCurrentField.js +447 -0
  199. package/src/water/waterMaterial.js +12 -0
  200. package/src/water/waterNearshorePhase.js +320 -0
  201. package/src/water/waterScenePasses.js +86 -29
  202. package/src/water/waterSettings.js +335 -13
  203. package/src/water/waterShoreMaterial.js +322 -0
  204. package/src/water/waterShoreStateField.js +605 -0
  205. package/src/water/waterSurface.js +824 -28
  206. package/src/weather/index.js +6 -0
  207. package/src/weather/weatherPrecipitation.js +221 -0
  208. package/src/weather/weatherPresets.js +258 -0
  209. package/src/weather/weatherSettings.js +269 -0
  210. package/src/weather/weatherSystem.js +871 -0
  211. package/src/worldCollision.js +94 -0
  212. package/src/worldMinimap.js +214 -0
  213. package/src/worldPresets.js +125 -0
package/AGENTS.md ADDED
@@ -0,0 +1,254 @@
1
+ # ToonLab — guide for AI coding agents
2
+
3
+ You are helping a developer use `@call-me-sensei/toonlab` (alias: `toonlab`),
4
+ a stylized anime game kit for Three.js (WebGPU-first TSL materials, WebGL2
5
+ fallback; 1 world unit = 1 meter). The look is NOT in any single API — it
6
+ emerges from the assembly: sky feeds water reflections, an aligned sun rig +
7
+ three-layer fog set the palette, cloud shadows tie ground/canopy/water
8
+ together. Skipping the assembly produces gray, flat, "programmer-art" scenes.
9
+
10
+ ## Golden path — a full world in minutes
11
+
12
+ ```js
13
+ import { createStylizedTerrain, createStylizedWorld } from '@call-me-sensei/toonlab';
14
+
15
+ // 1. Terrain: any seed is a valid world. waterCoverage is the "more water"
16
+ // knob; height/depth set mountain amplitude and basin depth; size takes
17
+ // a number or { x, z }; floatingIslands / sinkholes are one option each.
18
+ const terrain = createStylizedTerrain({
19
+ seed: 42,
20
+ size: 1000,
21
+ archetype: 'terracedKarst', // 'lakeland' | 'alpine' | 'rollingPlains' | 'archipelago'
22
+ });
23
+ const terrainRoot = new THREE.Group();
24
+ terrainRoot.add(terrain.root);
25
+ scene.add(terrainRoot);
26
+
27
+ // 2. Everything else: environment shading, aligned sun + shadows, sky,
28
+ // water, LOD forests, follow-window grass, cloud shadows, collision.
29
+ const world = await createStylizedWorld({
30
+ renderer, scene, camera,
31
+ terrain: { heightAt: terrain.heightAt, root: terrainRoot, size: terrain.meshExtent },
32
+ water: { level: terrain.waterLevel },
33
+ followTarget: characterRoot, // splashes, wakes, grass push
34
+ });
35
+ characterRoot.position.copy(terrain.spawn); // probed: walkable, near shore
36
+ // render loop, before rendering:
37
+ world.update(delta);
38
+ ```
39
+
40
+ **Bring your own terrain** (no generator): the ONLY contract is a pure
41
+ `heightAt(x, z) → meters` plus a displaced mesh under `terrain.root` with
42
+ `frustumCulled = false`. Everything else (masks, scatter, collision,
43
+ minimap) derives from `heightAt` and `water.level`. Even
44
+ `heightAt = (x, z) => 12 * Math.sin(x / 90) * Math.cos(z / 90)` gives a
45
+ complete shaded, forested, swimmable world.
46
+
47
+ Character: `applyToonShader(root, { settings: createToonSettings({ preset:
48
+ 'call_me_sensei' }) })` + `createCharacterRenderPasses` (call
49
+ `passes.update()` per frame). Post: `createPostProcessingPipeline` with
50
+ preset `call_me_sensei`; `post.render(delta)` replaces `renderer.render`.
51
+ Minimap: `createWorldMinimap({ heightAt, size, waterLevel, onPick })` —
52
+ call `minimap.setPlayer(x, z, heading)` per frame.
53
+ Every cluster has a `default` and a studio-managed `call_me_sensei` preset.
54
+
55
+ ## Quality rules (validated against reference-class modern anime worlds)
56
+
57
+ - Align the visible sky sun and the light rig, and match the hour: 2 PM sun
58
+ is HIGH (`sunDirection` y ≈ 0.8, warm-white); golden hour is LOW (y ≈ 0.4).
59
+ - Three-layer atmosphere: scene.fog (set by createStylizedWorld) +
60
+ environment heightFog (`heightFogColor` luminous blue [0.63,0.8,0.98],
61
+ density ≈ 0.0012–0.0016, falloff ≈ 400) + post depthCue (~0.3, blue).
62
+ White or absent fog is the #1 giveaway of a bad scene. EVERY custom
63
+ surface must join the height-fog layer or it reads as pasted on —
64
+ `createStylizedWorld` wires water and forest impostors automatically
65
+ (`setDistanceFog`); lower the density per aerial view or flyovers gray out.
66
+ - Cast shadows: terrain `castShadow = true`, near rocks both flags, forests
67
+ `lod: { castShadow: true }` (only live near trees cast — correct).
68
+ - Vividness is palette + saturation, not brightness: environment
69
+ `saturation ≈ 1.24`, `exposure ≈ 1.1`, `shadowTintColor [0.6,0.66,0.82]`,
70
+ saturated cerulean zenith, two-tone cumulus with blue-shaded bottoms,
71
+ green-dominant canopy list with ONE gold accent variant (muddy autumn
72
+ mixes read as confetti), turquoise water ramp.
73
+ - Cluster forests with `createNoisePatchMask`; tree `size` 2.5–4.
74
+ - Cliffs: steep faces need material, not flat paint — set
75
+ `material.userData.envTriplanarMap` (painted stone tile) +
76
+ `triplanarDetail: 1`, `triplanarDetailScale ≈ 14`,
77
+ `triplanarEdgeHighlight` for painted lip highlights; keep per-vertex
78
+ paint LOW-frequency (bands finer than the mesh grid alias into zigzag
79
+ triangles) and gate meadow/gold paint hard by slope.
80
+ - Never let ground hover within ±1.6 m of water level over large areas
81
+ (broken water slivers); end the map in a hazy mountain rim;
82
+ `frustumCulled = false` on world-scale meshes.
83
+ - Budgets: trees ≤ 3,000 via `StylizedForest` (16-vert billboard far LOD —
84
+ pass `renderer` or you get the expensive legacy path), grass ≤ 320k
85
+ blades in a follow window, startup < 10 s. Give every repeated mesh set
86
+ (rocks, cliff decor) a hi/lo distance LOD using TRUE 3D distance so
87
+ aerial cameras demote everything. Exclude above-water dressing from
88
+ water passes: `userData.waterExclude` (all passes) or
89
+ `waterGrabExclude` (refraction only, keeps the reflection).
90
+ - Perf triage: add URL toggles per system and read the FPS meter — the
91
+ water scene passes (grab/depth/reflection) multiply every other cost, so
92
+ measure with and without water first. Scale them via
93
+ `water.settings.passes = { reflectionScale: 0.4, sceneColorScale: 0.6 }`.
94
+
95
+ ## Subpath imports
96
+
97
+ `/toon` `/environment` `/lighting` `/weather` `/water` `/vegetation` (incl. scatter helpers +
98
+ `StylizedForest`) `/vegetation-shaders` `/grass-palettes` `/sky` `/post` `/character` `/loaders` `/rockgen`
99
+ `/debrisgen` `/pathgen` `/debug`; root adds `createStylizedTerrain`,
100
+ `createStylizedWorld`, `createWorldCollision`, `createWorldMinimap`,
101
+ `resolveWorldPreset`, `createStylizedPaths`.
102
+
103
+ Authoring scope: Character/Vegetation/Environment Shader Labs save reusable
104
+ IP-wide material treatments; Tree/Flower/Grass and other Asset Labs save asset
105
+ identity and geometry; Water/Sky Labs save complete runtime-system presets
106
+ with their shader controls embedded, so do not create separate Water Shader or
107
+ Sky Shader documents. Current lighting, weather, camera, and interactions
108
+ remain host-owned.
109
+
110
+ Sky and Water keep authored and current-scene state separate. `.settings` is
111
+ the portable authored baseline; `.renderedSettings` is the composition after
112
+ named transient layers. Lighting, Weather, and other owners must use unique
113
+ ids with `setSceneOverrideLayer`, clear only their own id with
114
+ `clearSceneOverrideLayer`, and never persist the composed result. The manual
115
+ `setSceneOverrides`/`clearSceneOverrides` pair owns only the `scene` layer;
116
+ `clearAllSceneOverrideLayers` is for explicit host teardown.
117
+
118
+ `lighting.attachWorld(world)` owns the world sun adapter plus private
119
+ priority-100 Sky/Water layers. If Weather is present it also installs Lighting
120
+ as Weather's sun/ambient/fog bridge: Lighting remains the sole writer, Weather
121
+ provides modulation and private priority-200 Sky/Water layers, and either
122
+ system removes only its own state on detach/dispose.
123
+ The adapter is `world.setSun({ direction, color, sky })`; it aligns the real
124
+ light/shadows with Grass, Flower, Forest, Ambient FX, Sky, and Water inputs.
125
+ For a standalone custom Weather coordinator, supply paired `getSun`/`setSun`
126
+ and baselines for any write-only cloud/surface adapters so teardown is exact.
127
+
128
+ Sky documents contain exactly 46 portable art fields; constructor-only radius
129
+ and quality are excluded. Sky quality is compile-time (`low`/`medium`/`high`
130
+ = 2/3/4 cloud octaves, or custom `{ cloudOctaves: 1..5 }`), and
131
+ `sky.setQuality()` rebuilds the material while preserving authored/layered
132
+ state. Water quality is selected when constructing `WaterSurface`; a tier
133
+ change requires replacing/rebuilding the surface, never `applySettings()`.
134
+
135
+ Lighting (`/lighting`) owns portable, versioned recipes and looks rather than
136
+ a replacement renderer. Use `createLightingManager({ scene, camera, renderer,
137
+ recipe, quality })`, call `update()` as the focus/camera moves, and save JSON
138
+ with the recipe/look serializers. Luminaire, rig, look, and quality presets
139
+ come from `getLightingPresetOptions(kind)`. Disc/tube area lights, IES, tag
140
+ linking, and Unreal Engine 5.8 MegaLights/Lumen are explicit adapter intent;
141
+ the capability report and diagnostics state each runtime fallback. Author and
142
+ stress-test them in `/lighting-lab/`.
143
+
144
+ `createLightingManager` is the lower-level rig-realization API. For a composed
145
+ world and day cycle, use `createLightingSystem(...)`, then call
146
+ `lighting.attachWorld(world)` for the sun/Sky/Water ownership and Weather
147
+ modulation bridge described above.
148
+
149
+ Paths/roads/bridges: `createStylizedWorld({ paths: { seed, auto: { count: 4,
150
+ styles: ['dirt', 'stone'] } } })` routes seeded trails around slopes, bridges
151
+ water crossings, parts grass/trees around the ribbon, and feeds the flattened
152
+ profile to `world.collision.groundHeight` — use that (not raw `heightAt`) for
153
+ character ground so bridges carry the walk. Minimap: pass `paths: world.paths`
154
+ to `createWorldMinimap` for the network overlay.
155
+
156
+ Catalog (`@call-me-sensei/toonlab/catalog`): the whole procedural library,
157
+ queryable and spawnable —
158
+ `catalog.list({ tags, cluster, text })`, `catalog.get(id)`, and the headline
159
+ `catalog.spawn(id, { seed })` → a PropAsset for any prop / building / tree /
160
+ rock / debris entry (settings presets throw with their copy-paste snippet
161
+ instead). `catalog.register(entry)` adds user assets;
162
+ `catalog.addSource(url, { headers })` mounts remote registries (the pro
163
+ seam). The browser Gallery at `/gallery/` searches third-party open assets;
164
+ the procedural Catalog itself is a runtime API rather than a standalone lab.
165
+
166
+ Villages (`/villagegen`): `createStylizedWorld({ pois: { seed, villages: 2,
167
+ shrines: 1, pierHamlets: 1, size } })` → named settlements (seeded syllable
168
+ names) with streets merged into the world path network, buildings facing
169
+ their street behind picket fences, wells/lanterns/clutter by archetype, POI
170
+ entries auto-connected by roads. `world.pois` feeds
171
+ `createWorldMinimap({ markers })` labels. Fully-shadowed building facades
172
+ need `parameters.sunShadowStrength ≈ 0.7` in worlds that run near-zero
173
+ ambient (full-strength cast shadows crush large vertical masses to black).
174
+
175
+ Fauna (`/fauna`) and ambient VFX (`/ambientfx`) are one option each:
176
+ `createStylizedWorld({ fauna: { species: { birds: 40, fish: 80 } },
177
+ ambientfx: { effects: { petals: true, fireflies: true } } })` — both join
178
+ the world's fog/wind/cloud-shadow automatically.
179
+
180
+ Weather (`/weather`) is a cross-system coordinator, not an environment
181
+ catch-all: `createStylizedWorld({ weather: { preset: 'snow' } })`, then
182
+ `world.setWeather('thunderstorm', { duration: 4 })`. Its 22 shared presets
183
+ drive sky/sun/fog/cloud shadows, wind across vegetation/fauna/ambient FX,
184
+ one-draw GPU rain/snow/sleet/hail/dust, water waves/ripples, lightning and
185
+ thunder events. Surface `{ wetness, snowCover, ice }` values are host-facing
186
+ outputs for custom terrain/prop/character materials. Labs should read
187
+ `getWeatherPresetOptions()` rather than maintaining a private condition list.
188
+
189
+ Gameplay VFX (`/vfxgen`): event-driven combat/movement effects, spawned at
190
+ gameplay moments (not a world option) — `createVfxSystem({ seed, preset,
191
+ heightAt })` then `vfx.spawn('slash' | 'impact' | 'fireball' | 'footstep' |
192
+ 'landing', { at | follow, power, look })` and `vfx.update(delta, camera)`
193
+ per frame. All bursts share TWO draw calls; slash trails / fireball cores
194
+ are pooled meshes. `vfx.setDistanceFog(...)` joins the height-fog layer;
195
+ `vfx.setTimeScale(0)` is hit-stop. Weapons + moves are batteries-included:
196
+ `createStylizedWeapon({ type: 'sword' | 'greatsword' | 'spear' | 'dagger'
197
+ | 'hammer' })` + `createMoveController({ weapon, vfx })` →
198
+ `attack.play('slash' | 'overhead' | 'thrust' | 'spin' | 'plunge')` — authored
199
+ phase-based motions whose event tracks fire the VFX at the right beats
200
+ (plunge = the full crouch→leap→dive→landfall decomposition); weapon weight
201
+ scales timing and hit power. Design interactively in the VFX Lab
202
+ (`/vfx-lab/`): weapon picker + move triggers + schema panels, exports a
203
+ recipe (preset + seed + overrides) that drops straight into `createVfxSystem`.
204
+ The `call_me_sensei` preset targets the reference action-RPG hit language
205
+ (smooth gradient arcs, four-point star + shockwave circle per hit,
206
+ hard-saturated pyro fireball). Demo loop: `examples/vfx-arena/`.
207
+
208
+ Buildings (`/buildinggen`): seeded grammar exteriors —
209
+ `buildingAsset({ type: 'cottage' | 'shed' | 'farmhouse' | 'watchtower' |
210
+ 'shrine', seed })` is a PropAsset (multi-circle footprint, buried foundation
211
+ skirt for slopes ≤ ~20°, hi/lo LOD, `door` anchor with outward normal for
212
+ street-facing placement). `createBuildingFromRecipe(recipe)` rebuilds
213
+ deterministically; ≤ 6 draw calls per building.
214
+
215
+ Props (`/propgen`): every placeable thing is a PropAsset —
216
+ `createPropAsset({ asset: { type: 'lantern', variant: 'stoneToro', seed } })`
217
+ or `propAssetFromObject(importedGlb)` (auto footprint + ground anchor).
218
+ Place with ONE call: `placeAlongSpline({ asset, spline: world.paths.splines[0],
219
+ spacing, offset, mask, heightAt: world.paths.heightAt, collision:
220
+ world.collision, parent })` (fences/walls build continuously; point props
221
+ instance with hi/lo LOD — call the returned `update(delta, camera)` per
222
+ frame), or `scatterProps`/`placeProps`. Props added after `createStylizedWorld`
223
+ need `applyEnvironmentShader(propsRoot, { parameters: { …fog } })` to join the
224
+ look; pass a dry-land `mask` so dressing never marches into water.
225
+
226
+ ## Symptom table — check before debugging blind
227
+
228
+ | Looks like | Cause → fix |
229
+ |---|---|
230
+ | Terrain gray/flat | environment shader never applied → put meshes under `terrain.root` |
231
+ | Distant trees sharp saturated dots on hazed mountains | surface missing the height-fog layer → update ToonLab (impostors/water auto-wired via `setDistanceFog`); custom surfaces must join it |
232
+ | Distant water bright band "cutting into" mountains | same fog-layer mismatch → `waterSurface.setDistanceFog({ color, density })` |
233
+ | Giant white "iceberg" wedges at far shorelines | outdated ToonLab (swash film climbed steep banks) → update |
234
+ | Full-detail trees popping in aerial views | LOD by horizontal distance → update ToonLab (3D distance) |
235
+ | Gold/orange tree with green-shadow or pink-crown leaves | outdated ToonLab (palette derivation broke on warm hues) → update |
236
+ | Billboards upside down / trunk-up | render-target bakes are written top-down → update ToonLab |
237
+ | Trees like confetti from the air | uniform scatter → `createNoisePatchMask`; palette too mixed → green-dominant + one gold |
238
+ | White valley blotches | white height fog → sky-blue `heightFogColor` |
239
+ | Fog has no effect | `heightFogFalloff` too small → ≈ 400 |
240
+ | Everything pale/gray from the air | one fog density for all views → lower `heightFogDensity` for aerial cameras (terrain uniforms + `water/forest.setDistanceFog`) |
241
+ | Cliff walls flat, untextured up close | planar UVs stretch on walls → `envTriplanarMap` + `triplanarDetail` |
242
+ | Zigzag triangles on cliff walls | per-vertex paint finer than the grid, or hue bleeding through stone → low-frequency bands; luminance-only tint is built in |
243
+ | Flat light, no shadows | vertical/misaligned sun, nothing casts → align sun, enable castShadow |
244
+ | Mountains vanish when centered | frustum culling on displaced meshes → `frustumCulled = false` |
245
+ | Minute-long startup | unique tree per placement → `StylizedForest` |
246
+ | ~20 fps in a big world | full-res meshes in every water pass → billboard forests (pass `renderer`), hi/lo rock LOD, `waterExclude`/`waterGrabExclude`, pass scales, `?dpr=1` on retina |
247
+ | Character walks through rocks/trees | blockers unregistered → `world.collision.addCircles([{x,z,radius}])` + `world.collision.resolve(character.position, 0.35)` per frame (trunks are pre-registered) |
248
+ | Character floats over/sinks into water | float on `water.getHeightAt(x, z)` with chest at the waterline; calm swim default, fast stroke on Shift, `action.timeScale = clamp(speed/1.7, 0.75, 1.35)` |
249
+
250
+ Full runbook with budgets and verification workflow:
251
+ `agents/skills/*/outdoor-world/SKILL.md` in the repo; complete reference
252
+ app: `examples/outdoor-world/`. Verify by headless Playwright screenshot
253
+ (`--enable-unsafe-webgpu --enable-gpu`), not by assumption — and LOOK at
254
+ the images.
package/ATTRIBUTION.md CHANGED
@@ -8,9 +8,18 @@ Code is MIT-licensed by Hyperbond Studio PTE. LTD. (see LICENSE). The assets bun
8
8
  |---|---|---|---|
9
9
  | Mannequin character (45 embedded animation clips) | `public/characters/mannequin.glb` | CC0 1.0 | [Quaternius — Universal Animation Library](https://quaternius.com/packs/universalanimationlibrary.html), recolored to neutral grays. See `public/characters/LICENSE.md`. |
10
10
  | Landscape textures (grassy-land, land, mountain, rock, sand, tree-trunk) | `labs/shared/textures/` | CC0 1.0 | First-party (created by the toonlab authors), dedicated to the public domain. |
11
+ | Water Lab sea fern model | `public/water-lab/cc0/quaternius/fern-1.glb` | CC0 1.0 | [Quaternius — Fern 1, Stylized Nature MegaKit](https://quaternius.itch.io/stylized-nature-megakit), selected through the [ToonLab catalog](https://toonlab.io/asset/quaternius:stylized-nature-megakit-fern-1). |
12
+ | Water Lab Coast Sand 01 texture set | `public/water-lab/cc0/polyhaven/` | CC0 1.0 | [Rob Tuytel — Coast Sand 01, Poly Haven](https://polyhaven.com/a/coast_sand_01), selected through the [ToonLab catalog](https://toonlab.io/asset/polyhaven:coast_sand_01). |
13
+ | City street models (buildings, roads, cars, street props — 28 models) | `public/props/cc0/kaykit-city/` | CC0 1.0 | [Kay Lousberg — KayKit City Builder Bits](https://kaylousberg.com). Attribution optional under CC0; credited with thanks. See the bundled `LICENSE.txt`. |
14
+ | Furniture models (tables, chairs, couch, lamps, shelves — 17 models) | `public/props/cc0/kaykit-furniture/` | CC0 1.0 | [Kay Lousberg — KayKit Furniture Bits](https://kaylousberg.com). Attribution optional under CC0; credited with thanks. See the bundled `LICENSE.txt`. |
15
+ | Environment Lab Wood Floor texture | `public/environments/cc0/polyhaven/wood-floor-diff-1k.jpg` | CC0 1.0 | [Dimitrios Savva — Wood Floor, Poly Haven](https://polyhaven.com/a/wood_floor) |
16
+ | Environment Lab Painted Plaster Wall texture | `public/environments/cc0/polyhaven/painted-plaster-wall-diff-1k.jpg` | CC0 1.0 | [Amal Kumar — Painted Plaster Wall, Poly Haven](https://polyhaven.com/a/painted_plaster_wall) |
17
+ | Environment Lab photoscanned furniture (Sofa 02, Modern Arm Chair 01, Round Wooden Table 01, Classic Console 01, Steel Frame Shelves 01, Potted Plant 04) | `public/environments/cc0/polyhaven/models/` | CC0 1.0 | [Poly Haven models](https://polyhaven.com/models) (1k glTF), per-asset credits on each asset page |
18
+ | Environment Lab museum scans (baluster vase F1980.190-.194; Colonoware pot) | `public/environments/cc0/smithsonian/` | CC0 1.0 | [Smithsonian Open Access 3D](https://3d.si.edu), selected through the [ToonLab catalog](https://toonlab.io/gallery?src=smithsonian) |
19
+ | Draco + Basis decoders (model decompression) | `public/draco/`, `public/basis/` | Apache 2.0 | [google/draco](https://github.com/google/draco), [BinomialLLC/basis_universal](https://github.com/BinomialLLC/basis_universal) — bundled three.js decoder builds |
11
20
 
12
21
  Everything else that renders — water, sky, grass, flowers, trees, foliage,
13
- splashes, post-processing — is procedural code, no asset files.
22
+ splashes, post-processing — is procedural code.
14
23
 
15
24
  ## Not bundled (bring your own)
16
25
 
@@ -32,9 +41,38 @@ folder that is **not** part of this repository:
32
41
 
33
42
  ## Recommended free sources
34
43
 
44
+ The machine-readable version of this list (license facts, integration level,
45
+ quality tier, enable/disable flags) is `src/assetlib/sources.js`; the Asset
46
+ Browser lab surfaces it.
47
+
35
48
  - [Quaternius](https://quaternius.com) — CC0 models + the Universal
36
- Animation Library (v1 and v2 free tiers).
49
+ Animation Library (v1 and v2 free tiers); most of the catalog is
50
+ searchable via Poly Pizza.
37
51
  - [Poly Haven](https://polyhaven.com) — CC0 textures/HDRIs/models.
38
- - [Kenney](https://kenney.nl) — CC0 game assets.
52
+ - [Kenney](https://kenney.nl) — CC0 game assets (no file API; manual import).
53
+ - [KayKit](https://kaylousberg.com) — CC0 stylized low-poly packs (city,
54
+ furniture, dungeon, characters …) on GitHub; integrated in the Asset
55
+ Browser (disabled by default — below the platform quality bar).
56
+ Attribution optional; credited with thanks.
57
+ - [Open Source 3D Assets](https://opensource3dassets.com) — keyless CC0
58
+ registry (Polygonal Mind collections); integrated, pending quality review.
59
+ - [Poly Pizza](https://poly.pizza) — low-poly aggregator, CC0/CC-BY
60
+ (BYO free API key).
61
+ - [Smithsonian 3D Open Access](https://3d.si.edu) — museum scans; only
62
+ records explicitly marked CC0.
63
+ - [Sketchfab CC0 search](https://sketchfab.com/search?features=downloadable&licenses=cc0&type=models)
64
+ — download manually (their download API is per-user OAuth with branding
65
+ terms; deliberately not automated).
66
+ - [The Base Mesh](https://thebasemesh.com) — CC0 base meshes (manual import).
67
+ - [3DTextures.me](https://3dtextures.me) — CC0 stylized materials
68
+ (manual import).
69
+ - [TextureCan](https://www.texturecan.com) — CC0 textures (manual import).
70
+ - [ShareTextures](https://www.sharetextures.com) — CC0 files, but their
71
+ platform terms prohibit automated downloads/hotlinking — browse manually
72
+ only, never wire an adapter.
39
73
  - [OpenGameArt](https://opengameart.org) — filter by CC0/CC-BY.
74
+ - [Openverse](https://openverse.org) — openly licensed images/audio
75
+ (per-record license).
76
+ - [Freesound](https://freesound.org) — sounds; free API tier is
77
+ non-commercial.
40
78
  - [VRoid Hub](https://hub.vroid.com) — VRM avatars (check per-model terms).