@call-me-sensei/toonlab 0.1.1

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 (146) hide show
  1. package/ATTRIBUTION.md +40 -0
  2. package/LICENSE +21 -0
  3. package/README.md +125 -0
  4. package/package.json +97 -0
  5. package/src/character/characterRig.js +353 -0
  6. package/src/character/freestyleSwimClip.js +541 -0
  7. package/src/character/index.js +4 -0
  8. package/src/character/modelLoader.js +433 -0
  9. package/src/core/materialRoles.js +438 -0
  10. package/src/core/presetDocuments.js +110 -0
  11. package/src/core/shaderBackend.js +24 -0
  12. package/src/debrisgen/debrisFields.js +544 -0
  13. package/src/debrisgen/debrisGenerator.js +1929 -0
  14. package/src/debrisgen/debrisPalettes.js +71 -0
  15. package/src/debrisgen/debrisPhysics.js +198 -0
  16. package/src/debrisgen/debrisPresets.js +155 -0
  17. package/src/debrisgen/debrisSettings.js +333 -0
  18. package/src/debrisgen/debrisTextures.js +380 -0
  19. package/src/debrisgen/index.js +5 -0
  20. package/src/debug/fieldValues.js +95 -0
  21. package/src/debug/index.js +7 -0
  22. package/src/debug/settingsPanel.js +170 -0
  23. package/src/environment/environmentAmbientProbe.js +160 -0
  24. package/src/environment/environmentMaterialAdapter.js +336 -0
  25. package/src/environment/environmentMaterialClassifier.js +178 -0
  26. package/src/environment/environmentPlanarReflection.js +201 -0
  27. package/src/environment/environmentPresets.js +443 -0
  28. package/src/environment/environmentRigs.js +684 -0
  29. package/src/environment/environmentSettings.js +504 -0
  30. package/src/environment/environmentShaderMaterials.js +318 -0
  31. package/src/environment/environmentSunShadowPass.js +268 -0
  32. package/src/environment/environmentTextureResolver.js +161 -0
  33. package/src/environment/environmentTimeOfDay.js +167 -0
  34. package/src/environment/environmentVertexAo.js +188 -0
  35. package/src/environment/index.js +8 -0
  36. package/src/environment/scanAssetStylize.js +103 -0
  37. package/src/index.js +21 -0
  38. package/src/loaders/index.js +1 -0
  39. package/src/post/index.js +2 -0
  40. package/src/post/postProcessing.js +1087 -0
  41. package/src/rockgen/export/glbExport.js +177 -0
  42. package/src/rockgen/heightfield/heightfieldErosion.js +7 -0
  43. package/src/rockgen/heightfield/heightfieldPatch.js +183 -0
  44. package/src/rockgen/heightfield/stylizedErosionSim.js +356 -0
  45. package/src/rockgen/index.js +14 -0
  46. package/src/rockgen/mesh/meshAttributes.js +374 -0
  47. package/src/rockgen/mesh/meshDocument.js +158 -0
  48. package/src/rockgen/mesh/surfaceNets.js +329 -0
  49. package/src/rockgen/noise/cellularNoise3.js +92 -0
  50. package/src/rockgen/noise/prng.js +62 -0
  51. package/src/rockgen/noise/simplexNoise3.js +89 -0
  52. package/src/rockgen/noise/valueNoise3.js +87 -0
  53. package/src/rockgen/rockDocument.js +255 -0
  54. package/src/rockgen/rockHelpers.js +8 -0
  55. package/src/rockgen/rockgenPresets.js +559 -0
  56. package/src/rockgen/rockgenSettings.js +756 -0
  57. package/src/rockgen/sdf/fieldCompiler.js +544 -0
  58. package/src/rockgen/sdf/sculptEdits.js +66 -0
  59. package/src/rockgen/sdf/sdfModifiers.js +45 -0
  60. package/src/rockgen/sdf/sdfOps.js +37 -0
  61. package/src/rockgen/sdf/sdfPrimitives.js +88 -0
  62. package/src/shaders-tsl/anime.js +963 -0
  63. package/src/shaders-tsl/chunks/character-color.js +64 -0
  64. package/src/shaders-tsl/chunks/character-highlights.js +174 -0
  65. package/src/shaders-tsl/chunks/character-lighting.js +309 -0
  66. package/src/shaders-tsl/chunks/character-material-maps.js +146 -0
  67. package/src/shaders-tsl/chunks/character-roles.js +52 -0
  68. package/src/shaders-tsl/chunks/character-scene-lights.js +270 -0
  69. package/src/shaders-tsl/chunks/character-shadow-color.js +61 -0
  70. package/src/shaders-tsl/chunks/character-skinning.js +140 -0
  71. package/src/shaders-tsl/chunks/environment-color.js +47 -0
  72. package/src/shaders-tsl/chunks/environment-debug.js +79 -0
  73. package/src/shaders-tsl/chunks/environment-lighting.js +260 -0
  74. package/src/shaders-tsl/chunks/environment-sun-shadow.js +87 -0
  75. package/src/shaders-tsl/chunks/foliage-fog.js +60 -0
  76. package/src/shaders-tsl/chunks/pass-depth-color.js +89 -0
  77. package/src/shaders-tsl/chunks/stylized-cloud-shadow.js +70 -0
  78. package/src/shaders-tsl/chunks/water-color.js +190 -0
  79. package/src/shaders-tsl/chunks/water-common.js +124 -0
  80. package/src/shaders-tsl/chunks/water-foam.js +93 -0
  81. package/src/shaders-tsl/chunks/water-lighting.js +116 -0
  82. package/src/shaders-tsl/chunks/water-ripple.js +64 -0
  83. package/src/shaders-tsl/chunks/water-waves.js +100 -0
  84. package/src/shaders-tsl/environment-ao-overlay.js +60 -0
  85. package/src/shaders-tsl/environment.js +680 -0
  86. package/src/shaders-tsl/flower.js +267 -0
  87. package/src/shaders-tsl/grass.js +193 -0
  88. package/src/shaders-tsl/post-composite.js +461 -0
  89. package/src/shaders-tsl/sky.js +175 -0
  90. package/src/shaders-tsl/tree-leaf.js +264 -0
  91. package/src/shaders-tsl/water-breaker.js +423 -0
  92. package/src/shaders-tsl/water-kelp.js +171 -0
  93. package/src/shaders-tsl/water-rain.js +107 -0
  94. package/src/shaders-tsl/water-simulation.js +140 -0
  95. package/src/shaders-tsl/water-splash.js +298 -0
  96. package/src/shaders-tsl/water.js +509 -0
  97. package/src/sky/index.js +2 -0
  98. package/src/sky/stylizedSky.js +341 -0
  99. package/src/toon/characterRenderPasses.js +646 -0
  100. package/src/toon/index.js +4 -0
  101. package/src/toon/settings/alphaSettings.js +170 -0
  102. package/src/toon/settings/averageShadowSettings.js +181 -0
  103. package/src/toon/settings/baseTextureSettings.js +133 -0
  104. package/src/toon/settings/celShadeSettings.js +96 -0
  105. package/src/toon/settings/contactShadowSettings.js +114 -0
  106. package/src/toon/settings/eyeHighlightSettings.js +187 -0
  107. package/src/toon/settings/faceLightingSettings.js +135 -0
  108. package/src/toon/settings/furSettings.js +112 -0
  109. package/src/toon/settings/glitterSettings.js +102 -0
  110. package/src/toon/settings/hairHighlightSettings.js +325 -0
  111. package/src/toon/settings/indirectLightSettings.js +244 -0
  112. package/src/toon/settings/localLightSettings.js +176 -0
  113. package/src/toon/settings/materialMapSettings.js +299 -0
  114. package/src/toon/settings/outlineSettings.js +263 -0
  115. package/src/toon/settings/perspectiveRemovalSettings.js +60 -0
  116. package/src/toon/settings/rimLightSettings.js +268 -0
  117. package/src/toon/settings/sceneShadowSettings.js +147 -0
  118. package/src/toon/settings/selfShadowSettings.js +221 -0
  119. package/src/toon/settings/shadowColorSettings.js +238 -0
  120. package/src/toon/settings/skinToneSettings.js +150 -0
  121. package/src/toon/settings/specularSettings.js +314 -0
  122. package/src/toon/settings/stickerSettings.js +102 -0
  123. package/src/toon/toonMaterialAdapter.js +2235 -0
  124. package/src/toon/toonSettings.js +860 -0
  125. package/src/vegetation/flowerSpecies.js +260 -0
  126. package/src/vegetation/index.js +10 -0
  127. package/src/vegetation/stylizedBush.js +136 -0
  128. package/src/vegetation/stylizedFlower.js +213 -0
  129. package/src/vegetation/stylizedFlowers.js +297 -0
  130. package/src/vegetation/stylizedGrass.js +491 -0
  131. package/src/vegetation/stylizedTree.js +2449 -0
  132. package/src/vegetation/stylizedTreeFoliage.js +691 -0
  133. package/src/vegetation/treeExport.js +300 -0
  134. package/src/vegetation/treeRecipe.js +647 -0
  135. package/src/water/index.js +2 -0
  136. package/src/water/water.js +20 -0
  137. package/src/water/waterBreakerSystem.js +591 -0
  138. package/src/water/waterInteraction.js +193 -0
  139. package/src/water/waterMaterial.js +263 -0
  140. package/src/water/waterRain.js +136 -0
  141. package/src/water/waterRippleSimulation.js +227 -0
  142. package/src/water/waterScenePasses.js +415 -0
  143. package/src/water/waterSettings.js +1138 -0
  144. package/src/water/waterSplashSystem.js +251 -0
  145. package/src/water/waterSurface.js +475 -0
  146. package/src/water/waterVegetation.js +83 -0
@@ -0,0 +1,297 @@
1
+ import * as THREE from 'three';
2
+
3
+ import { createFlowerNodeMaterial } from '../shaders-tsl/flower.js';
4
+
5
+ function setSrgbColor(color, rgb) {
6
+ color.setRGB(rgb[0], rgb[1], rgb[2], THREE.SRGBColorSpace);
7
+ }
8
+
9
+ function cleanObject(value) {
10
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
11
+ }
12
+
13
+ function finiteNumber(value, fallback, { min = -Infinity, max = Infinity } = {}) {
14
+ const number = Number(value);
15
+ if (!Number.isFinite(number)) return fallback;
16
+ return Math.min(max, Math.max(min, number));
17
+ }
18
+
19
+ function colorArray(value, fallback) {
20
+ if (value?.isColor) return [value.r, value.g, value.b];
21
+ if (Array.isArray(value) && value.length >= 3) {
22
+ const next = value.slice(0, 3).map(Number);
23
+ return next.every(Number.isFinite) ? next : fallback.slice();
24
+ }
25
+ if (typeof value === 'number' || typeof value === 'string') {
26
+ try {
27
+ const color = new THREE.Color(value);
28
+ return [color.r, color.g, color.b];
29
+ } catch {
30
+ return fallback.slice();
31
+ }
32
+ }
33
+ return fallback.slice();
34
+ }
35
+
36
+ function vectorArray(value, fallback, size) {
37
+ const keys = ['x', 'y', 'z', 'w'];
38
+ const read = (index) => {
39
+ if (Array.isArray(value)) return Number(value[index]);
40
+ if (value && typeof value === 'object') return Number(value[keys[index]]);
41
+ return NaN;
42
+ };
43
+ const next = Array.from({ length: size }, (_, index) => read(index));
44
+ return next.every(Number.isFinite) ? next : fallback.slice(0, size);
45
+ }
46
+
47
+ /**
48
+ * Default flower-field settings. Every value equals the field's historical
49
+ * hardcoded/constructor default, so `new StylizedFlowerField({ placements })`
50
+ * renders identically to previous releases.
51
+ */
52
+ export const DEFAULT_FLOWER_SETTINGS = Object.freeze({
53
+ centerColor: Object.freeze([0.98, 0.8, 0.34]),
54
+ petalColor: Object.freeze([1.0, 0.98, 0.92]),
55
+ shadowStrength: 0.85,
56
+ sizeRange: Object.freeze([0.045, 0.08]),
57
+ windDirection: Object.freeze([1, 0.3]),
58
+ windSpeed: 1.0,
59
+ windStrength: 0.16,
60
+ });
61
+
62
+ /**
63
+ * Validates, clamps, and merges partial flower options over
64
+ * {@link DEFAULT_FLOWER_SETTINGS}. Unknown keys are ignored; malformed values
65
+ * fall back to their defaults. `createFlowerSettings()` deep-equals the
66
+ * defaults object.
67
+ *
68
+ * @param {Object} [options] Partial settings (legacy constructor options are
69
+ * the same flat shape, so they work unchanged).
70
+ * @returns {Object} A complete, plain flower settings object.
71
+ */
72
+ export function createFlowerSettings(options = {}) {
73
+ const source = cleanObject(options);
74
+ const base = DEFAULT_FLOWER_SETTINGS;
75
+ return {
76
+ centerColor: colorArray(source.centerColor, base.centerColor),
77
+ petalColor: colorArray(source.petalColor, base.petalColor),
78
+ shadowStrength: finiteNumber(source.shadowStrength, base.shadowStrength, { min: 0, max: 1 }),
79
+ sizeRange: vectorArray(source.sizeRange, base.sizeRange, 2),
80
+ windDirection: vectorArray(source.windDirection, base.windDirection, 2),
81
+ windSpeed: finiteNumber(source.windSpeed, base.windSpeed),
82
+ windStrength: finiteNumber(source.windStrength, base.windStrength, { min: 0 }),
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Panel group metadata for the flower settings, in display order. Settings
88
+ * themselves stay flat; each group lists which flat keys it owns via
89
+ * {@link FLOWER_SETTING_FIELD_SCHEMA}.
90
+ */
91
+ export const FLOWER_SETTING_GROUPS = Object.freeze([
92
+ Object.freeze({
93
+ description: 'Random head sizes baked into the instance attributes when the field is built. Construction-only.',
94
+ id: 'heads',
95
+ label: 'Heads',
96
+ }),
97
+ Object.freeze({
98
+ description: 'Wind sway shared with the surrounding grass so heads and blades move together.',
99
+ id: 'wind',
100
+ label: 'Wind',
101
+ }),
102
+ Object.freeze({
103
+ description: 'Petal/center palette and scene-shadow darkening.',
104
+ id: 'appearance',
105
+ label: 'Appearance',
106
+ }),
107
+ ]);
108
+
109
+ const FLOWER_FIELD_DEFINITIONS = Object.freeze({
110
+ heads: {
111
+ sizeRange: {
112
+ description: 'Min/max head size in meters for placements without an explicit size. Construction-only: baked into instance attributes.',
113
+ label: 'Size Range',
114
+ type: 'vector2',
115
+ },
116
+ },
117
+ wind: {
118
+ windDirection: {
119
+ description: 'Horizontal (XZ) heading the wind blows toward. Magnitude does not matter; use wind strength for amplitude.',
120
+ label: 'Wind Direction',
121
+ type: 'vector2',
122
+ },
123
+ windSpeed: {
124
+ description: 'How fast the head sway oscillates.',
125
+ label: 'Wind Speed',
126
+ range: { max: 4, min: 0, step: 0.01 },
127
+ type: 'number',
128
+ },
129
+ windStrength: {
130
+ description: 'How far flower heads bob with the wind.',
131
+ label: 'Wind Strength',
132
+ range: { max: 1, min: 0, step: 0.005 },
133
+ type: 'number',
134
+ },
135
+ },
136
+ appearance: {
137
+ petalColor: {
138
+ description: 'Petal color of the procedural daisies.',
139
+ label: 'Petal Color',
140
+ type: 'color',
141
+ },
142
+ centerColor: {
143
+ description: 'Center-disc color of the procedural daisies.',
144
+ label: 'Center Color',
145
+ type: 'color',
146
+ },
147
+ shadowStrength: {
148
+ description: 'How strongly renderer shadow maps darken flower heads.',
149
+ label: 'Shadow Strength',
150
+ range: { max: 1, min: 0, step: 0.01 },
151
+ type: 'number',
152
+ },
153
+ },
154
+ });
155
+
156
+ function createFlowerFieldMetadata(group, key, field) {
157
+ const defaultValue = DEFAULT_FLOWER_SETTINGS[key];
158
+ return Object.freeze({
159
+ defaultValue: Array.isArray(defaultValue) ? [...defaultValue] : defaultValue,
160
+ description: field.description,
161
+ group: group.id,
162
+ id: `${group.id}.${key}`,
163
+ key,
164
+ label: field.label,
165
+ optionLabels: field.optionLabels ?? null,
166
+ options: field.options ?? null,
167
+ range: field.range ?? null,
168
+ serializable: field.serializable ?? true,
169
+ type: field.type,
170
+ });
171
+ }
172
+
173
+ /**
174
+ * Field metadata (id/group/key/label/description/type/range/defaultValue/
175
+ * serializable) per settings group, in the shape consumed by
176
+ * `createSettingsPanel`. Keys are the flat {@link DEFAULT_FLOWER_SETTINGS}
177
+ * keys.
178
+ */
179
+ export const FLOWER_SETTING_FIELD_SCHEMA = Object.freeze(
180
+ Object.fromEntries(
181
+ FLOWER_SETTING_GROUPS.map((group) => [
182
+ group.id,
183
+ Object.freeze(
184
+ Object.fromEntries(
185
+ Object.entries(FLOWER_FIELD_DEFINITIONS[group.id] ?? {})
186
+ .map(([key, field]) => [key, createFlowerFieldMetadata(group, key, field)]),
187
+ ),
188
+ ),
189
+ ]),
190
+ ),
191
+ );
192
+
193
+ // Procedural daisies scattered through a grass canopy: instanced billboard
194
+ // quads whose petals are drawn in the fragment shader. One draw call, no
195
+ // texture assets. Stems are implied — heads float at canopy height and the
196
+ // dense grass below sells the attachment.
197
+ //
198
+ // const flowers = new StylizedFlowerField({
199
+ // placements: spots.map((p) => ({ x: p.x, y: ground(p), z: p.z, headHeight: 0.4 })),
200
+ // });
201
+ // scene.add(flowers);
202
+ // flowers.update(delta); // each frame
203
+ // flowers.applySettings({ petalColor: [1, 0.9, 0.95] });
204
+ //
205
+ // Options are a flat settings object (see DEFAULT_FLOWER_SETTINGS) plus
206
+ // `placements`; legacy individual constructor options are the same keys, so
207
+ // existing callers keep working unchanged.
208
+ export class StylizedFlowerField extends THREE.Mesh {
209
+ constructor(options = {}) {
210
+ const { placements = [] } = cleanObject(options);
211
+ const settings = createFlowerSettings(options);
212
+ const { sizeRange } = settings;
213
+
214
+ const quad = new THREE.PlaneGeometry(1, 1);
215
+
216
+ const geometry = new THREE.InstancedBufferGeometry();
217
+ geometry.index = quad.index;
218
+ geometry.setAttribute('position', quad.attributes.position);
219
+ geometry.setAttribute('uv', quad.attributes.uv);
220
+
221
+ const count = Math.max(placements.length, 1);
222
+ const origins = new Float32Array(count * 3);
223
+ const infos = new Float32Array(count * 4);
224
+ placements.forEach((placement, i) => {
225
+ origins[i * 3] = placement.x ?? 0;
226
+ origins[i * 3 + 1] = placement.y ?? 0;
227
+ origins[i * 3 + 2] = placement.z ?? 0;
228
+ infos[i * 4] = placement.size ??
229
+ THREE.MathUtils.lerp(sizeRange[0], sizeRange[1], Math.random());
230
+ infos[i * 4 + 1] = Math.random();
231
+ infos[i * 4 + 2] = placement.headHeight ?? 0.3;
232
+ infos[i * 4 + 3] = 0;
233
+ });
234
+ geometry.setAttribute('iOrigin', new THREE.InstancedBufferAttribute(origins, 3));
235
+ geometry.setAttribute('iInfo', new THREE.InstancedBufferAttribute(infos, 4));
236
+ geometry.instanceCount = placements.length;
237
+ geometry.boundingSphere = new THREE.Sphere(new THREE.Vector3(), 1e5);
238
+
239
+ const material = createFlowerNodeMaterial(settings);
240
+ setSrgbColor(material.uniforms.uPetalColor.value, settings.petalColor);
241
+ setSrgbColor(material.uniforms.uCenterColor.value, settings.centerColor);
242
+
243
+ super(geometry, material);
244
+ this.name = 'StylizedFlowerField';
245
+ this.frustumCulled = false;
246
+ this.receiveShadow = true;
247
+ this.settings = settings;
248
+ }
249
+
250
+ /**
251
+ * Runtime re-tune: merges `options` into the current settings and pushes
252
+ * every material-driven value (wind, palette, shadow strength) into the
253
+ * uniforms. `sizeRange` is baked into the instance attributes at
254
+ * construction and is construction-only; a new value is stored but does not
255
+ * resize existing heads.
256
+ *
257
+ * @param {Object} [options] Partial flat settings, same keys as
258
+ * {@link DEFAULT_FLOWER_SETTINGS}.
259
+ * @returns {Object} The updated settings object.
260
+ */
261
+ applySettings(options = {}) {
262
+ const merged = { ...this.settings };
263
+ for (const [key, value] of Object.entries(cleanObject(options))) {
264
+ if (value !== undefined) merged[key] = value;
265
+ }
266
+ const settings = createFlowerSettings(merged);
267
+ this.settings = settings;
268
+
269
+ const uniforms = this.material.uniforms;
270
+ uniforms.uShadowStrength.value = settings.shadowStrength;
271
+ uniforms.uWindDirection.value.set(settings.windDirection[0], settings.windDirection[1]);
272
+ uniforms.uWindSpeed.value = settings.windSpeed;
273
+ uniforms.uWindStrength.value = settings.windStrength;
274
+ setSrgbColor(uniforms.uPetalColor.value, settings.petalColor);
275
+ setSrgbColor(uniforms.uCenterColor.value, settings.centerColor);
276
+ return this.settings;
277
+ }
278
+
279
+ setWind({ direction, speed, strength } = {}) {
280
+ this.applySettings({
281
+ windDirection: direction,
282
+ windSpeed: speed,
283
+ windStrength: strength,
284
+ });
285
+ return this;
286
+ }
287
+
288
+ update(delta) {
289
+ this.material.uniforms.uTime.value += Math.min(Math.max(delta ?? 0.016, 0), 0.1);
290
+ return this;
291
+ }
292
+
293
+ dispose() {
294
+ this.geometry.dispose();
295
+ this.material.dispose();
296
+ }
297
+ }