@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.
- package/AGENTS.md +127 -4
- package/ATTRIBUTION.md +41 -3
- package/README.md +234 -34
- package/docs/characters.md +144 -0
- package/docs/debug-panel.md +126 -0
- package/docs/docs.css +589 -0
- package/docs/environment.md +186 -0
- package/docs/getting-started.md +180 -0
- package/docs/index.html +14 -0
- package/docs/lab-architecture.md +100 -0
- package/docs/lighting.md +825 -0
- package/docs/main.jsx +739 -0
- package/docs/mcp.md +97 -0
- package/docs/post-processing.md +98 -0
- package/docs/settings-reference.md +1878 -0
- package/docs/shader-constants.md +77 -0
- package/docs/sky.md +182 -0
- package/docs/style-labs.md +309 -0
- package/docs/texture-lab.md +135 -0
- package/docs/toon-shading.md +183 -0
- package/docs/tsl-conventions.md +167 -0
- package/docs/vegetation-sky.md +275 -0
- package/docs/water.md +430 -0
- package/docs/weather.md +200 -0
- package/docs/world-scale.md +111 -0
- package/mcp/server.mjs +610 -0
- package/mcp/style-lab-tools.mjs +371 -0
- package/mcp/vite-plugin.mjs +174 -0
- package/mcp/workspace.mjs +397 -0
- package/package.json +64 -5
- package/src/ambientfx/INTEGRATION.md +164 -0
- package/src/ambientfx/ambientFxPresets.js +83 -0
- package/src/ambientfx/ambientFxSettings.js +368 -0
- package/src/ambientfx/emitters.js +169 -0
- package/src/ambientfx/index.js +5 -0
- package/src/ambientfx/particleBackbone.js +493 -0
- package/src/ambientfx/stylizedAmbientFx.js +450 -0
- package/src/assetlib/ambientcg.js +117 -0
- package/src/assetlib/assetRef.js +91 -0
- package/src/assetlib/importedEntry.js +40 -0
- package/src/assetlib/index.js +23 -0
- package/src/assetlib/kaykit.js +192 -0
- package/src/assetlib/kaykitStaticIndex.js +700 -0
- package/src/assetlib/loadImported.js +143 -0
- package/src/assetlib/opensource3d.js +113 -0
- package/src/assetlib/polyhaven.js +182 -0
- package/src/assetlib/polypizza.js +115 -0
- package/src/assetlib/smithsonian.js +214 -0
- package/src/assetlib/sources.js +279 -0
- package/src/assetlib/zip.js +58 -0
- package/src/biome/biomeGenerator.js +385 -0
- package/src/biome/biomeRuntime.js +299 -0
- package/src/biome/index.js +2 -0
- package/src/buildinggen/buildingAsset.js +44 -0
- package/src/buildinggen/buildingGrammar.js +311 -0
- package/src/buildinggen/buildingMesh.js +451 -0
- package/src/buildinggen/buildingPresets.js +46 -0
- package/src/buildinggen/buildingRecipe.js +100 -0
- package/src/buildinggen/buildingSettings.js +238 -0
- package/src/buildinggen/index.js +6 -0
- package/src/camera/cameraDirector.js +157 -0
- package/src/camera/cameraGenerator.js +367 -0
- package/src/camera/cameraRig.js +570 -0
- package/src/camera/cameraSettings.js +236 -0
- package/src/camera/index.js +7 -0
- package/src/catalog/builtinEntries.js +245 -0
- package/src/catalog/catalog.js +210 -0
- package/src/catalog/index.js +3 -0
- package/src/catalog/manifest.js +84 -0
- package/src/core/generation.js +529 -0
- package/src/environment/environmentRigs.js +21 -1
- package/src/environment/environmentSettings.js +4 -0
- package/src/environment/environmentSunShadowPass.js +8 -0
- package/src/fauna/INTEGRATION.md +174 -0
- package/src/fauna/boids.js +861 -0
- package/src/fauna/faunaBodies.js +492 -0
- package/src/fauna/faunaPresets.js +52 -0
- package/src/fauna/faunaSettings.js +525 -0
- package/src/fauna/index.js +5 -0
- package/src/fauna/stylizedFauna.js +395 -0
- package/src/game-feel/gameFeelGenerator.js +402 -0
- package/src/game-feel/gameFeelRuntime.js +549 -0
- package/src/game-feel/index.js +2 -0
- package/src/index.js +17 -6
- package/src/lighting/colorIntensity.js +177 -0
- package/src/lighting/index.js +161 -0
- package/src/lighting/lightDescriptors.js +249 -0
- package/src/lighting/lightingCapabilities.js +79 -0
- package/src/lighting/lightingDocuments.js +247 -0
- package/src/lighting/lightingFixtures.js +446 -0
- package/src/lighting/lightingGenerator.js +449 -0
- package/src/lighting/lightingPresets.js +319 -0
- package/src/lighting/lightingRuntime.js +723 -0
- package/src/lighting/lightingStyle.js +386 -0
- package/src/lighting/lightingSystem.js +774 -0
- package/src/lighting/unrealExport.js +186 -0
- package/src/lighting/utils.js +87 -0
- package/src/motion/index.js +5 -0
- package/src/motion/motionClip.js +441 -0
- package/src/motion/motionController.js +628 -0
- package/src/motion/motionDocuments.js +225 -0
- package/src/motion/motionGraph.js +307 -0
- package/src/motion/motionSettings.js +222 -0
- package/src/pathgen/index.js +7 -0
- package/src/pathgen/pathBridge.js +232 -0
- package/src/pathgen/pathPresets.js +35 -0
- package/src/pathgen/pathRibbon.js +410 -0
- package/src/pathgen/pathRouter.js +380 -0
- package/src/pathgen/pathSettings.js +335 -0
- package/src/pathgen/pathTextures.js +123 -0
- package/src/pathgen/stylizedPaths.js +453 -0
- package/src/post/index.js +1 -0
- package/src/post/postGenerator.js +177 -0
- package/src/post/postProcessing.js +41 -0
- package/src/propgen/generatorsWave1.js +379 -0
- package/src/propgen/generatorsWave2.js +462 -0
- package/src/propgen/index.js +5 -0
- package/src/propgen/propAsset.js +323 -0
- package/src/propgen/propParts.js +170 -0
- package/src/propgen/propPlacement.js +459 -0
- package/src/propgen/propPresets.js +82 -0
- package/src/propgen/propSettings.js +395 -0
- package/src/shaders-tsl/chunks/projected-water-caustics.js +242 -0
- package/src/shaders-tsl/chunks/vegetation-style.js +360 -0
- package/src/shaders-tsl/chunks/water-shore-state.js +31 -0
- package/src/shaders-tsl/chunks/water-waves.js +90 -11
- package/src/shaders-tsl/environment.js +15 -1
- package/src/shaders-tsl/flower.js +279 -30
- package/src/shaders-tsl/grass.js +60 -33
- package/src/shaders-tsl/sky.js +125 -31
- package/src/shaders-tsl/tree-leaf.js +61 -25
- package/src/shaders-tsl/water-breaker.js +7 -4
- package/src/shaders-tsl/water-shore-state-simulation.js +523 -0
- package/src/shaders-tsl/water.js +439 -49
- package/src/shaders-tsl/woody-surface.js +154 -0
- package/src/sky/sceneOverrideLayers.js +10 -0
- package/src/sky/skyQuality.js +26 -0
- package/src/sky/stylizedSky.js +753 -45
- package/src/soundscape/index.js +4 -0
- package/src/soundscape/soundscapeGenerator.js +179 -0
- package/src/soundscape/soundscapeRuntime.js +806 -0
- package/src/soundscape/soundscapeSettings.js +292 -0
- package/src/styles/index.js +13 -0
- package/src/styles/styleBundle.js +325 -0
- package/src/stylizedTerrain.js +32 -2
- package/src/stylizedWorld.js +423 -20
- package/src/texgen/evaluateTexture.js +675 -0
- package/src/texgen/index.js +60 -0
- package/src/texgen/noise2.js +210 -0
- package/src/texgen/textureAi.js +436 -0
- package/src/texgen/textureGenerators.js +516 -0
- package/src/texgen/texturePresets.js +490 -0
- package/src/texgen/textureSettings.js +342 -0
- package/src/texgen/textureThree.js +59 -0
- package/src/vegetation/flowerSpecies.js +15 -3
- package/src/vegetation/grassPalettes.js +153 -0
- package/src/vegetation/index.js +6 -0
- package/src/vegetation/stylizedBush.js +2 -0
- package/src/vegetation/stylizedFlower.js +82 -0
- package/src/vegetation/stylizedFlowers.js +48 -7
- package/src/vegetation/stylizedForest.js +29 -1
- package/src/vegetation/stylizedGrass.js +291 -56
- package/src/vegetation/stylizedTree.js +38 -2
- package/src/vegetation/stylizedTreeFoliage.js +2 -1
- package/src/vegetation/vegetationShaders.js +1110 -0
- package/src/vfxgen/INTEGRATION.md +145 -0
- package/src/vfxgen/core/burstBackbone.js +380 -0
- package/src/vfxgen/core/projectileCore.js +92 -0
- package/src/vfxgen/core/spriteShapes.js +98 -0
- package/src/vfxgen/core/trailRibbon.js +272 -0
- package/src/vfxgen/core/vfxRandom.js +29 -0
- package/src/vfxgen/effects/emitHelpers.js +37 -0
- package/src/vfxgen/effects/magicEffects.js +162 -0
- package/src/vfxgen/effects/movementEffects.js +87 -0
- package/src/vfxgen/effects/weaponEffects.js +118 -0
- package/src/vfxgen/index.js +18 -0
- package/src/vfxgen/moves/moveController.js +146 -0
- package/src/vfxgen/moves/moveLibrary.js +335 -0
- package/src/vfxgen/vfxPresets.js +98 -0
- package/src/vfxgen/vfxSettings.js +384 -0
- package/src/vfxgen/vfxSystem.js +449 -0
- package/src/vfxgen/weapons/stylizedWeapons.js +137 -0
- package/src/villagegen/index.js +4 -0
- package/src/villagegen/stylizedVillage.js +490 -0
- package/src/villagegen/villageArchetypes.js +160 -0
- package/src/villagegen/villageNames.js +40 -0
- package/src/villagegen/villageSites.js +105 -0
- package/src/water/sceneOverrideLayers.js +23 -0
- package/src/water/water.js +5 -0
- package/src/water/waterBreakerSystem.js +15 -1
- package/src/water/waterCurrentField.js +447 -0
- package/src/water/waterMaterial.js +12 -0
- package/src/water/waterNearshorePhase.js +320 -0
- package/src/water/waterScenePasses.js +83 -30
- package/src/water/waterSettings.js +325 -13
- package/src/water/waterShoreMaterial.js +322 -0
- package/src/water/waterShoreStateField.js +605 -0
- package/src/water/waterSurface.js +797 -28
- package/src/weather/index.js +6 -0
- package/src/weather/weatherPrecipitation.js +221 -0
- package/src/weather/weatherPresets.js +258 -0
- package/src/weather/weatherSettings.js +269 -0
- package/src/weather/weatherSystem.js +871 -0
- package/src/worldMinimap.js +62 -0
- package/src/worldPresets.js +4 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Characters and animation
|
|
2
|
+
|
|
3
|
+
Bring-your-own-model is a first-class feature: any humanoid character in a
|
|
4
|
+
common format gets loaded, toon-shaded, and animated — without code changes
|
|
5
|
+
in the labs, with rig helpers in `@call-me-sensei/toonlab/character` and optional loader
|
|
6
|
+
helpers in `@call-me-sensei/toonlab/loaders` for your own app.
|
|
7
|
+
|
|
8
|
+
## Loading models
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
import { loadModelAsset, SUPPORTED_MODEL_FORMATS } from '@call-me-sensei/toonlab/loaders';
|
|
12
|
+
|
|
13
|
+
const asset = await loadModelAsset('models/hero.vrm');
|
|
14
|
+
// asset: { root, clips, format, url, resourcePath, vrm? }
|
|
15
|
+
scene.add(asset.root);
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
(Inside this repo the labs import from `../../src/character/...`.)
|
|
19
|
+
|
|
20
|
+
Supported formats (`SUPPORTED_MODEL_FORMATS`): `pmx`, `pmd`, `fbx`, `glb`,
|
|
21
|
+
`gltf`, `vrm` (0 and 1), `obj` (pass `materialUrl` for the MTL), and
|
|
22
|
+
text-based `usdz`. VRM models are detected automatically on glTF load,
|
|
23
|
+
rotated to the +Z convention, and returned with the `vrm` instance.
|
|
24
|
+
|
|
25
|
+
Draco or KTX2-compressed glTF assets need an explicit decoder directory:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
await loadModelAsset('models/compressed.glb', {
|
|
29
|
+
decoderBasePath: '/vendor/three-decoders/',
|
|
30
|
+
renderer,
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
In the labs, use the HUD **Model URL** input or `?model=` — local paths and
|
|
35
|
+
hosted URLs both work (hosted URLs need CORS headers on the server; load
|
|
36
|
+
failures surface in a HUD banner). Private test models go in the gitignored
|
|
37
|
+
`assets-local/models/` drop-in folder: after `npm run assets:local` they
|
|
38
|
+
surface in every model-aware lab's Model select, no code changes. See
|
|
39
|
+
[getting-started.md](getting-started.md#loading-your-own-models) for the
|
|
40
|
+
folder shapes the scanner recognizes.
|
|
41
|
+
|
|
42
|
+
## Bone-role adapters (`characterRig`)
|
|
43
|
+
|
|
44
|
+
Animation is rig-agnostic because bone *names* are resolved to canonical
|
|
45
|
+
humanoid *roles* (VRM humanoid naming, `HUMANOID_ROLES`) once, up front:
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
import { resolveCharacterRig, targetBoneNameForRole } from '@call-me-sensei/toonlab/character';
|
|
49
|
+
|
|
50
|
+
const rig = resolveCharacterRig(skinnedMesh, { vrm: asset.vrm });
|
|
51
|
+
// rig: { type, targetToMixamo, mixamoToTarget, hipCarrierName } — or null
|
|
52
|
+
targetBoneNameForRole(rig, 'head'); // the model's actual head bone name
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`resolveCharacterRig` tries each convention adapter in order:
|
|
56
|
+
|
|
57
|
+
1. **VRM** — the humanoid bone map from the VRM extension (spec'd, exact).
|
|
58
|
+
2. **MMD/PMX** — Japanese bone names (`頭`, `下半身`, ...), including the
|
|
59
|
+
center-bone hip carrier.
|
|
60
|
+
3. **Mixamo-named** — skeletons already using `mixamorig` names (normalized
|
|
61
|
+
for the common prefix variants).
|
|
62
|
+
4. **Rigify** — Blender Rigify `DEF-` deform bones, matched loosely
|
|
63
|
+
(heuristic name matching handles export renaming).
|
|
64
|
+
|
|
65
|
+
If no adapter matches, retargeting is impossible and models fall back to
|
|
66
|
+
their embedded clips. Everything downstream (retargeting, the procedural
|
|
67
|
+
swim clip, head tracking) works through roles, so a new rig convention is
|
|
68
|
+
one name table, not a new pipeline.
|
|
69
|
+
|
|
70
|
+
## Native-clip fast path
|
|
71
|
+
|
|
72
|
+
Models that ship their own locomotion clips skip retargeting entirely. A
|
|
73
|
+
model qualifies when at least idle + walk resolve against these clip-name
|
|
74
|
+
conventions (`NATIVE_LOCOMOTION_CLIP_NAMES` in
|
|
75
|
+
`labs/playground/animationPipeline.js`; first match per role wins):
|
|
76
|
+
|
|
77
|
+
| Role | Accepted clip names |
|
|
78
|
+
|---|---|
|
|
79
|
+
| idle | `Idle_Loop`, `Idle` |
|
|
80
|
+
| walk | `Walk_Loop`, `Walking`, `Walk` |
|
|
81
|
+
| run | `Sprint_Loop`, `Jog_Fwd_Loop`, `Running`, `Run` |
|
|
82
|
+
| jump | `Jump_Start`, `Jump_Loop`, `Jump` |
|
|
83
|
+
| swim | `Swim_Fwd_Loop`, `Swimming`, `Swim` |
|
|
84
|
+
| tread | `Swim_Idle_Loop`, `Treading_Water`, `TreadingWater` |
|
|
85
|
+
|
|
86
|
+
The bundled CC0 mannequin (Quaternius Universal Animation Library, 45
|
|
87
|
+
embedded clips) qualifies out of the box — that is why a fresh clone
|
|
88
|
+
animates with zero downloads. Author your own GLBs to these names and they
|
|
89
|
+
play natively too.
|
|
90
|
+
|
|
91
|
+
## Mixamo retarget pipeline
|
|
92
|
+
|
|
93
|
+
For models without native clips, the Playground retargets Mixamo FBX clips
|
|
94
|
+
onto the resolved rig with a world-space bake: only the role mapping comes
|
|
95
|
+
from `characterRig`; rest-pose differences (T-pose vs. A-pose) and hierarchy
|
|
96
|
+
mismatches are solved numerically. This is how a PMX character, a VRM
|
|
97
|
+
avatar, and a Rigify export all play the same `Walking.fbx`.
|
|
98
|
+
|
|
99
|
+
Adobe's terms do not permit redistributing Mixamo clips, so the repo ships
|
|
100
|
+
none. Download clips with your own Adobe account from
|
|
101
|
+
[mixamo.com](https://www.mixamo.com) and drop them into
|
|
102
|
+
`assets-local/animations/` (`Idle.fbx`, `Walking.fbx`, `Running.fbx`,
|
|
103
|
+
`Jump.fbx`, `Swimming.fbx`, `Treading_Water.fbx`, ...). Override individual
|
|
104
|
+
clips per URL: `?swimAnim=`, `?treadAnim=`, `?diveAnim=`, `?anim=`. See
|
|
105
|
+
[ATTRIBUTION.md](../ATTRIBUTION.md).
|
|
106
|
+
|
|
107
|
+
When a swim/tread retarget source is missing, the pipeline falls back to the
|
|
108
|
+
model's native swim clips if it has them.
|
|
109
|
+
|
|
110
|
+
## Procedural freestyle swim
|
|
111
|
+
|
|
112
|
+
`createFreestyleSwimClip(targetMesh, rig, options)` generates a freestyle
|
|
113
|
+
(front crawl) swim clip with no FBX source: windmilling catch-up arm
|
|
114
|
+
strokes, flutter kick, body roll, and a breath every fourth stroke. Motion
|
|
115
|
+
is authored as world-space rotation deltas against the bind pose, so the
|
|
116
|
+
same code bakes onto any supported skeleton (VRM, MMD, Mixamo-named,
|
|
117
|
+
Rigify).
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
import { createFreestyleSwimClip } from '@call-me-sensei/toonlab/character';
|
|
121
|
+
|
|
122
|
+
// Skeleton must be in bind pose when you call this.
|
|
123
|
+
const clip = createFreestyleSwimClip(targetMesh, rig, {
|
|
124
|
+
clipName: 'FreestyleSwim',
|
|
125
|
+
trackNameStyle: 'skeleton', // 'skeleton' for retarget-style mixers, 'node' for native-clip mixers
|
|
126
|
+
});
|
|
127
|
+
mixer.clipAction(clip).play();
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The Playground uses it as the default swim style (`?freestyleAnim=none`
|
|
131
|
+
disables it).
|
|
132
|
+
|
|
133
|
+
## swimVisualLift
|
|
134
|
+
|
|
135
|
+
Swim physics floats every model at the same capsule depth below the wave
|
|
136
|
+
surface — the whole swim backend (vertical control, clamps, ray geometry,
|
|
137
|
+
wave exposure) is tuned as a package around one value, and giving models
|
|
138
|
+
different physics floats re-exposes tuning bugs. Models that need to *look*
|
|
139
|
+
higher or lower in the water get `swimVisualLift`: a visual-only vertical
|
|
140
|
+
offset applied to the model group while swimming, which the physics never
|
|
141
|
+
sees. The compact mannequin uses `0.18` to read as surface freestyle; a
|
|
142
|
+
character with tall hair volume needs none. Per-model values live in the
|
|
143
|
+
lab's character options (`labs/shared/sceneHub.js`); override for testing
|
|
144
|
+
with `?swimVisualLift=`.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Debug panel
|
|
2
|
+
|
|
3
|
+
`@call-me-sensei/toonlab/debug` is an optional, DOM-dependent tuning GUI: it turns any
|
|
4
|
+
settings module's group/field schema into live controls. One call gives you
|
|
5
|
+
the same panel the labs use — ship it behind a debug flag, or not at all
|
|
6
|
+
(nothing else imports it).
|
|
7
|
+
|
|
8
|
+
The labs now expose one TSL renderer stack: native WebGPU by default and
|
|
9
|
+
`?renderer=webgl` for the WebGL2 fallback. The panel writes the same settings
|
|
10
|
+
surface on both backends.
|
|
11
|
+
|
|
12
|
+
## createSettingsPanel
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import { createSettingsPanel } from '@call-me-sensei/toonlab/debug';
|
|
16
|
+
import { readFieldValueFromSettings } from '@call-me-sensei/toonlab/debug';
|
|
17
|
+
import {
|
|
18
|
+
TOON_SETTING_GROUPS,
|
|
19
|
+
TOON_SETTING_FIELD_SCHEMA,
|
|
20
|
+
applyToonSettingsToMaterial,
|
|
21
|
+
} from '@call-me-sensei/toonlab/toon';
|
|
22
|
+
|
|
23
|
+
const panel = createSettingsPanel({
|
|
24
|
+
container: document.getElementById('toonSettingGroups'),
|
|
25
|
+
groups: TOON_SETTING_GROUPS,
|
|
26
|
+
fieldSchema: TOON_SETTING_FIELD_SCHEMA,
|
|
27
|
+
getValue: (field) => readFieldValueFromSettings(settings, field),
|
|
28
|
+
onChange: (field, value) => {
|
|
29
|
+
settings[field.group][field.key] = value;
|
|
30
|
+
applyToonSettingsToMaterial(characterRoot, settings);
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
panel.refresh(); // re-pull every control value via getValue (e.g. after a preset switch)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
(Inside this repo the labs import from `../../src/debug/...`.)
|
|
38
|
+
|
|
39
|
+
The panel renders one collapsible `<details>` group per schema group, with a
|
|
40
|
+
typed control per field — checkbox for `boolean`, `<select>` for `select`
|
|
41
|
+
(using the schema's `options`/`optionLabels`), color input for `color`,
|
|
42
|
+
range slider for `number` (using the schema's `range`), text input
|
|
43
|
+
otherwise — each with a label, tooltip description, and live `<output>`
|
|
44
|
+
readout.
|
|
45
|
+
|
|
46
|
+
The host owns the settings state: the panel only reads through `getValue`
|
|
47
|
+
and reports edits through `onChange`. It never mutates settings itself,
|
|
48
|
+
which is what makes it schema-generic.
|
|
49
|
+
|
|
50
|
+
### Options
|
|
51
|
+
|
|
52
|
+
Beyond the required five, `createSettingsPanel` accepts:
|
|
53
|
+
|
|
54
|
+
- `fieldFilter(field)` — hide fields (e.g. non-serializable texture slots).
|
|
55
|
+
- `isDisabled(field)` — gray controls out (re-evaluated on `refresh()`).
|
|
56
|
+
- `formatValue(value, field)` — custom `<output>` formatting.
|
|
57
|
+
- `isGroupOpen(group)` — which groups start expanded.
|
|
58
|
+
- `prepend` — DOM nodes to insert before the groups (preset pickers, etc.).
|
|
59
|
+
- `dataAttribute`, `idPrefix`, `rowClassName`, `groupClassName`,
|
|
60
|
+
`fieldsClassName` — styling/data hooks.
|
|
61
|
+
|
|
62
|
+
### It works with every schema
|
|
63
|
+
|
|
64
|
+
Any module following the groups + field-schema convention plugs in — this is
|
|
65
|
+
the full list (see the generated
|
|
66
|
+
[settings reference](settings-reference.md) for their contents):
|
|
67
|
+
|
|
68
|
+
| Module | groups / fieldSchema |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `@call-me-sensei/toonlab/toon` | `TOON_SETTING_GROUPS` / `TOON_SETTING_FIELD_SCHEMA` |
|
|
71
|
+
| `@call-me-sensei/toonlab/environment` | `ENVIRONMENT_SETTING_GROUPS` / `ENVIRONMENT_SETTING_FIELD_SCHEMA` |
|
|
72
|
+
| `@call-me-sensei/toonlab/water` | `WATER_SETTING_GROUPS` / `WATER_SETTING_FIELD_SCHEMA_BY_GROUP` |
|
|
73
|
+
| `@call-me-sensei/toonlab/post` | `POST_PROCESSING_SETTING_GROUPS` / `POST_PROCESSING_SETTING_FIELD_SCHEMA` |
|
|
74
|
+
| `@call-me-sensei/toonlab/vegetation` (grass) | `GRASS_SETTING_GROUPS` / `GRASS_SETTING_FIELD_SCHEMA` |
|
|
75
|
+
| `@call-me-sensei/toonlab/vegetation` (flowers) | `FLOWER_SETTING_GROUPS` / `FLOWER_SETTING_FIELD_SCHEMA` |
|
|
76
|
+
| `@call-me-sensei/toonlab/vegetation` (trees) | `STYLIZED_TREE_SETTING_GROUPS` / `STYLIZED_TREE_SETTING_FIELD_SCHEMA` |
|
|
77
|
+
| `@call-me-sensei/toonlab/sky` | `SKY_SETTING_GROUPS` / `SKY_SETTING_FIELD_SCHEMA` |
|
|
78
|
+
|
|
79
|
+
Note the value-read difference: toon/environment/post/tree settings are
|
|
80
|
+
nested (`settings[field.group][field.key]`), water/grass/flower/sky settings
|
|
81
|
+
are flat (`settings[field.key]`). `readFieldValueFromSettings` handles the
|
|
82
|
+
nested case; for flat settings pass
|
|
83
|
+
`getValue: (field) => settings[field.key]`.
|
|
84
|
+
|
|
85
|
+
## createSelectRow
|
|
86
|
+
|
|
87
|
+
A plain labeled `<select>` row for the controls that live alongside the
|
|
88
|
+
schema-generated groups (preset pickers, debug-view selectors):
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
import { createSelectRow } from '@call-me-sensei/toonlab/debug';
|
|
92
|
+
|
|
93
|
+
const row = createSelectRow({
|
|
94
|
+
id: 'toonPreset',
|
|
95
|
+
label: 'Preset',
|
|
96
|
+
options: getToonPresetOptions().map((p) => ({ value: p.id, label: p.label })),
|
|
97
|
+
value: currentPreset,
|
|
98
|
+
onChange: (value) => setLabParams({ toonPreset: value }),
|
|
99
|
+
});
|
|
100
|
+
panelHeader.append(row);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Field value helpers (`fieldValues.js`)
|
|
104
|
+
|
|
105
|
+
The coercion layer between schema-typed values and DOM control values, used
|
|
106
|
+
by the panel and exported for custom UIs:
|
|
107
|
+
|
|
108
|
+
- `readFieldValueFromSettings(settings, field)` — nested group/key read.
|
|
109
|
+
- `readFieldValueFromControl(control, field)` — checkbox/range/color/select
|
|
110
|
+
→ typed value (colors become `[r, g, b]` arrays, numbers parse, etc.).
|
|
111
|
+
- `writeFieldControlValue(control, output, field, value)` — push a value
|
|
112
|
+
into a control and its `<output>`.
|
|
113
|
+
- `formatFieldValue(value, field)` — display formatting.
|
|
114
|
+
- `colorToHex(value)` / `hexToColorArray(value)` — `[r, g, b]` ↔ `#rrggbb`.
|
|
115
|
+
- `vectorToArray(value)` — vector-ish → plain array.
|
|
116
|
+
|
|
117
|
+
## How the labs use it
|
|
118
|
+
|
|
119
|
+
Character Shader Lab and Environment Shader Lab consume their respective
|
|
120
|
+
schemas independently. Each lab's panel is a thin consumer that wires
|
|
121
|
+
`getValue`/`onChange` to its preset store and to
|
|
122
|
+
`applyToonSettingsToMaterial` or `applyEnvironmentSettingsToMaterial` for
|
|
123
|
+
live uniform-safe edits. Preset switching refreshes the controls from the
|
|
124
|
+
normalized package settings. The same schema-consumer pattern drives Water,
|
|
125
|
+
Sky, Grass, Tree, and Flower Labs; no lab keeps a private copy of its runtime
|
|
126
|
+
parameter contract.
|