@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,504 @@
1
+ import * as THREE from 'three';
2
+
3
+ export const DEFAULT_ENVIRONMENT_FEATURES = Object.freeze({
4
+ alphaCutout: true,
5
+ alphaMap: true,
6
+ ambientLight: true,
7
+ ambientProbe: true,
8
+ aoMap: true,
9
+ aoOverlay: true,
10
+ directionalLights: true,
11
+ emissive: true,
12
+ emissiveMap: true,
13
+ foliageCutout: true,
14
+ heightFog: true,
15
+ interiorOcclusion: true,
16
+ leftSideShadow: true,
17
+ lightMap: true,
18
+ normalMap: true,
19
+ packedMap: true,
20
+ planarReflection: true,
21
+ pointLights: true,
22
+ shadowMask: true,
23
+ shadowMesh: true,
24
+ skyTint: true,
25
+ specular: true,
26
+ spotLights: true,
27
+ sunBoost: true,
28
+ untexturedGradient: true,
29
+ vertexAo: true,
30
+ windowCutout: true,
31
+ });
32
+
33
+ // Debug view ids must match environmentDebugColor() in
34
+ // src/shaders/chunks/environment-fragment-debug.glsl.
35
+ export const ENVIRONMENT_DEBUG_MODES = Object.freeze({
36
+ off: 0,
37
+ albedo: 1,
38
+ lit: 2,
39
+ ambient: 3,
40
+ direct: 4,
41
+ shadowMask: 5,
42
+ pointLight: 6,
43
+ spotLight: 7,
44
+ occlusion: 8,
45
+ bakedGi: 9,
46
+ normal: 10,
47
+ vertexAo: 11,
48
+ specular: 12,
49
+ emissive: 13,
50
+ windowMask: 14,
51
+ roomOcclusion: 15,
52
+ alpha: 16,
53
+ });
54
+
55
+ export function normalizeEnvironmentDebugMode(mode) {
56
+ if (typeof mode === 'number' && Number.isFinite(mode)) {
57
+ return Object.values(ENVIRONMENT_DEBUG_MODES).includes(mode) ? mode : 0;
58
+ }
59
+ return ENVIRONMENT_DEBUG_MODES[String(mode ?? 'off')] ?? 0;
60
+ }
61
+
62
+ // Uniforms shared by reference across every environment material (scene-wide
63
+ // clock + cloud shadows). They are excluded from the per-material baseline
64
+ // snapshot so re-applying settings on one material cannot reset the shared
65
+ // scene state for all of them.
66
+ export const ENVIRONMENT_SHARED_UNIFORM_NAMES = Object.freeze([
67
+ 'time',
68
+ 'cloudShadowStrength',
69
+ 'cloudShadowCoverage',
70
+ 'cloudShadowScale',
71
+ 'cloudShadowVelocity',
72
+ 'envDebugMode',
73
+ 'environmentOpenings',
74
+ 'environmentOpeningCount',
75
+ 'ambientProbe',
76
+ 'planarReflectionMap',
77
+ 'planarReflectionMatrix',
78
+ ]);
79
+
80
+ export const DEFAULT_ENVIRONMENT_PARAMETERS = Object.freeze({
81
+ ambientProbeBlend: null,
82
+ ambientStrength: null,
83
+ ambientLightInfluence: null,
84
+ aoMapStrength: null,
85
+ aoWarmth: null,
86
+ bakedGlowStrength: null,
87
+ cloudShadowCoverage: null,
88
+ cloudShadowScale: null,
89
+ cloudShadowStrength: null,
90
+ directLightStrength: null,
91
+ emissiveMapStrength: null,
92
+ emissiveStrength: null,
93
+ exposure: null,
94
+ heightFogColor: null,
95
+ heightFogDensity: null,
96
+ heightFogFalloff: null,
97
+ interiorOcclusionColor: null,
98
+ interiorOcclusionStrength: null,
99
+ leftSideShadow: null,
100
+ leftSideShadowColor: null,
101
+ lightMapLift: null,
102
+ lightMapStrength: null,
103
+ lightingInfluence: null,
104
+ normalMapStrength: null,
105
+ packedOcclusionStrength: null,
106
+ planarReflectionFresnel: null,
107
+ planarReflectionStrength: null,
108
+ pointLightStrength: null,
109
+ saturation: null,
110
+ shadeSoftness: null,
111
+ shadeStrength: null,
112
+ shadowLift: null,
113
+ shadowTintColor: null,
114
+ skyGroundTint: null,
115
+ skyTintStrength: null,
116
+ skyTopTint: null,
117
+ specularColor: null,
118
+ specularShininess: null,
119
+ specularSoftness: null,
120
+ specularStrength: null,
121
+ spotLightStrength: null,
122
+ sunBoost: null,
123
+ sunBoostColor: null,
124
+ untexturedGradientStrength: null,
125
+ vertexAoStrength: null,
126
+ });
127
+
128
+ export const ENVIRONMENT_SETTING_GROUPS = Object.freeze([
129
+ Object.freeze({
130
+ description: 'Enables or disables individual environment shader feature paths.',
131
+ id: 'features',
132
+ label: 'Features',
133
+ }),
134
+ Object.freeze({
135
+ description: 'Overrides numeric environment shader uniforms. Auto values preserve material defaults.',
136
+ id: 'parameters',
137
+ label: 'Shader Parameters',
138
+ }),
139
+ ]);
140
+
141
+ const COLOR_PARAMETER_KEYS = Object.freeze(new Set([
142
+ 'heightFogColor',
143
+ 'interiorOcclusionColor',
144
+ 'leftSideShadowColor',
145
+ 'shadowTintColor',
146
+ 'skyGroundTint',
147
+ 'skyTopTint',
148
+ 'specularColor',
149
+ 'sunBoostColor',
150
+ ]));
151
+
152
+ const FIELD_LABEL_OVERRIDES = Object.freeze({
153
+ alphaCutout: 'Alpha Cutout',
154
+ alphaMap: 'Alpha Map',
155
+ ambientLight: 'Ambient Light',
156
+ ambientLightInfluence: 'Ambient Influence',
157
+ ambientProbe: 'Ambient Probe',
158
+ ambientProbeBlend: 'Ambient Probe Blend',
159
+ ambientStrength: 'Ambient Strength',
160
+ aoMap: 'AO Map',
161
+ aoMapStrength: 'AO Map Strength',
162
+ aoOverlay: 'AO Overlay',
163
+ aoWarmth: 'AO Warmth',
164
+ emissiveMap: 'Emissive Map',
165
+ emissiveMapStrength: 'Emissive Map Strength',
166
+ heightFog: 'Height Fog',
167
+ heightFogColor: 'Height Fog Color',
168
+ heightFogDensity: 'Height Fog Density',
169
+ heightFogFalloff: 'Height Fog Falloff',
170
+ interiorOcclusion: 'Interior Occlusion',
171
+ interiorOcclusionColor: 'Interior Occlusion Color',
172
+ interiorOcclusionStrength: 'Interior Occlusion Strength',
173
+ lightMap: 'Lightmap',
174
+ lightMapLift: 'Lightmap Lift',
175
+ lightMapStrength: 'Lightmap Strength',
176
+ normalMap: 'Normal Map',
177
+ normalMapStrength: 'Normal Map Strength',
178
+ planarReflection: 'Floor Reflection',
179
+ planarReflectionFresnel: 'Floor Reflection Fresnel',
180
+ planarReflectionStrength: 'Floor Reflection Strength',
181
+ specular: 'Specular',
182
+ specularColor: 'Specular Color',
183
+ specularShininess: 'Specular Shininess',
184
+ specularSoftness: 'Specular Softness',
185
+ specularStrength: 'Specular Strength',
186
+ untexturedGradient: 'Untextured Gradient',
187
+ untexturedGradientStrength: 'Untextured Gradient Strength',
188
+ vertexAo: 'Vertex AO',
189
+ vertexAoStrength: 'Vertex AO Strength',
190
+ bakedGlowStrength: 'Baked Glow',
191
+ cloudShadowCoverage: 'Cloud Shadow Coverage',
192
+ cloudShadowScale: 'Cloud Shadow Scale',
193
+ cloudShadowStrength: 'Cloud Shadow',
194
+ directLightStrength: 'Direct Light',
195
+ directionalLights: 'Directional Lights',
196
+ emissive: 'Emissive',
197
+ emissiveStrength: 'Emissive Strength',
198
+ exposure: 'Exposure',
199
+ foliageCutout: 'Foliage Cutout',
200
+ leftSideShadow: 'Side Shadow',
201
+ leftSideShadowColor: 'Side Shadow Color',
202
+ lightingInfluence: 'Lighting Influence',
203
+ packedMap: 'Packed Map',
204
+ packedOcclusionStrength: 'Packed Occlusion',
205
+ pointLights: 'Point Lights',
206
+ pointLightStrength: 'Point Light',
207
+ saturation: 'Saturation',
208
+ shadeSoftness: 'Shade Softness',
209
+ shadeStrength: 'Shade Strength',
210
+ shadowLift: 'Shadow Lift',
211
+ shadowMask: 'Shadow Mask',
212
+ shadowMesh: 'Shadow Mesh',
213
+ shadowTintColor: 'Shadow Tint',
214
+ skyGroundTint: 'Sky Ground Tint',
215
+ skyTint: 'Sky Tint',
216
+ skyTintStrength: 'Sky Tint Strength',
217
+ skyTopTint: 'Sky Top Tint',
218
+ spotLights: 'Spot Lights',
219
+ spotLightStrength: 'Spot Light',
220
+ sunBoost: 'Sun Boost',
221
+ sunBoostColor: 'Sun Boost Color',
222
+ windowCutout: 'Window Cutout',
223
+ });
224
+
225
+ function labelFromFieldName(key) {
226
+ if (FIELD_LABEL_OVERRIDES[key]) return FIELD_LABEL_OVERRIDES[key];
227
+ return key
228
+ .replace(/([A-Z])/g, ' $1')
229
+ .replace(/^./, (char) => char.toUpperCase());
230
+ }
231
+
232
+ function descriptionForField(group, key) {
233
+ const label = labelFromFieldName(key).toLowerCase();
234
+ if (group.id === 'features') return `Turns ${label} processing on or off for environment materials.`;
235
+ if (COLOR_PARAMETER_KEYS.has(key)) return `Overrides ${label}; leave unset in code to use the material default.`;
236
+ return `Overrides ${label}; leave unset in code to use the material default.`;
237
+ }
238
+
239
+ function rangeForParameter(key) {
240
+ if (key === 'cloudShadowScale') return { max: 0.1, min: 0, step: 0.0005 };
241
+ if (key === 'specularShininess') return { max: 256, min: 1, step: 1 };
242
+ if (key === 'heightFogDensity') return { max: 0.5, min: 0, step: 0.001 };
243
+ if (key === 'heightFogFalloff') return { max: 30, min: 0.05, step: 0.05 };
244
+ if (key === 'planarReflectionFresnel') return { max: 8, min: 0.1, step: 0.05 };
245
+ if (key === 'exposure' || key === 'saturation') return { max: 2, min: 0, step: 0.01 };
246
+ if (key.includes('Strength') || key.includes('Influence')) return { max: 2, min: 0, step: 0.01 };
247
+ if (key.includes('LightStrength')) return { max: 4, min: 0, step: 0.01 };
248
+ if (key === 'directLightStrength' || key === 'pointLightStrength' || key === 'spotLightStrength') {
249
+ return { max: 4, min: 0, step: 0.01 };
250
+ }
251
+ if (key === 'shadeStrength') return { max: 2, min: 0, step: 0.01 };
252
+ if (key === 'shadeSoftness') return { max: 1, min: 0, step: 0.001 };
253
+ if (key === 'shadowLift') return { max: 1, min: 0, step: 0.01 };
254
+ if (key === 'sunBoost') return { max: 1, min: 0, step: 0.01 };
255
+ return { max: 1, min: 0, step: 0.01 };
256
+ }
257
+
258
+ function createEnvironmentFieldMetadata(group, key, value) {
259
+ const type = group.id === 'features'
260
+ ? 'boolean'
261
+ : COLOR_PARAMETER_KEYS.has(key)
262
+ ? 'color'
263
+ : 'number';
264
+ return Object.freeze({
265
+ defaultValue: value,
266
+ description: descriptionForField(group, key),
267
+ group: group.id,
268
+ id: `${group.id}.${key}`,
269
+ key,
270
+ label: labelFromFieldName(key),
271
+ range: type === 'number' ? rangeForParameter(key) : undefined,
272
+ type,
273
+ });
274
+ }
275
+
276
+ export const ENVIRONMENT_SETTING_FIELD_SCHEMA = Object.freeze({
277
+ features: Object.freeze(Object.fromEntries(
278
+ Object.entries(DEFAULT_ENVIRONMENT_FEATURES)
279
+ .map(([key, value]) => [
280
+ key,
281
+ createEnvironmentFieldMetadata(ENVIRONMENT_SETTING_GROUPS[0], key, value),
282
+ ]),
283
+ )),
284
+ parameters: Object.freeze(Object.fromEntries(
285
+ Object.entries(DEFAULT_ENVIRONMENT_PARAMETERS)
286
+ .map(([key, value]) => [
287
+ key,
288
+ createEnvironmentFieldMetadata(ENVIRONMENT_SETTING_GROUPS[1], key, value),
289
+ ]),
290
+ )),
291
+ });
292
+
293
+ function cleanObject(value) {
294
+ return value && typeof value === 'object' ? value : {};
295
+ }
296
+
297
+ export function createEnvironmentSettings(options = {}) {
298
+ const source = cleanObject(options);
299
+ const featureOverrides = cleanObject(source.features);
300
+ const parameterOverrides = cleanObject(source.parameters);
301
+
302
+ return {
303
+ features: {
304
+ ...DEFAULT_ENVIRONMENT_FEATURES,
305
+ ...featureOverrides,
306
+ },
307
+ parameters: {
308
+ ...DEFAULT_ENVIRONMENT_PARAMETERS,
309
+ ...parameterOverrides,
310
+ },
311
+ };
312
+ }
313
+
314
+ function featureValue(features, name) {
315
+ return features[name] ? 1.0 : 0.0;
316
+ }
317
+
318
+ function setUniform(uniforms, name, value) {
319
+ const uniform = uniforms?.[name];
320
+ if (!uniform) return;
321
+ uniform.value = value;
322
+ }
323
+
324
+ function setNumberUniform(uniforms, name, value) {
325
+ if (!Number.isFinite(value)) return;
326
+ setUniform(uniforms, name, value);
327
+ }
328
+
329
+ function colorFromParameter(value) {
330
+ if (value?.isColor) return value;
331
+ if (Array.isArray(value) && value.length >= 3) {
332
+ const [r, g, b] = value.map(Number);
333
+ return [r, g, b].every(Number.isFinite) ? new THREE.Color(r, g, b) : null;
334
+ }
335
+ if (typeof value === 'number' || typeof value === 'string') {
336
+ try {
337
+ return new THREE.Color(value);
338
+ } catch {
339
+ return null;
340
+ }
341
+ }
342
+ if (value && typeof value === 'object') {
343
+ const r = Number(value.r);
344
+ const g = Number(value.g);
345
+ const b = Number(value.b);
346
+ return [r, g, b].every(Number.isFinite) ? new THREE.Color(r, g, b) : null;
347
+ }
348
+ return null;
349
+ }
350
+
351
+ function setColorUniform(uniforms, name, value) {
352
+ const color = colorFromParameter(value);
353
+ if (!color) return;
354
+
355
+ const uniform = uniforms?.[name];
356
+ if (!uniform) return;
357
+ if (uniform.value?.isColor && uniform.value.copy) {
358
+ uniform.value.copy(color);
359
+ return;
360
+ }
361
+ uniform.value = color.clone();
362
+ }
363
+
364
+ function cloneUniformValue(value) {
365
+ if (value?.isTexture) return value;
366
+ if (value?.clone) return value.clone();
367
+ if (Array.isArray(value)) return value.slice();
368
+ return value;
369
+ }
370
+
371
+ function restoreUniformValue(uniform, value) {
372
+ if (!uniform) return;
373
+ const current = uniform.value;
374
+ if (current?.isColor && value?.isColor && current.copy) {
375
+ current.copy(value);
376
+ return;
377
+ }
378
+ if (current?.isVector2 && value?.isVector2 && current.copy) {
379
+ current.copy(value);
380
+ return;
381
+ }
382
+ if (current?.isVector3 && value?.isVector3 && current.copy) {
383
+ current.copy(value);
384
+ return;
385
+ }
386
+ if (current?.isVector4 && value?.isVector4 && current.copy) {
387
+ current.copy(value);
388
+ return;
389
+ }
390
+ uniform.value = cloneUniformValue(value);
391
+ }
392
+
393
+ function resetEnvironmentUniformsToBase(material) {
394
+ const uniforms = material?.uniforms;
395
+ if (!uniforms) return;
396
+
397
+ material.userData ??= {};
398
+ const existingDefaults = material.userData.environmentBaseUniformValues;
399
+ if (!existingDefaults) {
400
+ material.userData.environmentBaseUniformValues = Object.fromEntries(
401
+ Object.entries(uniforms)
402
+ .filter(([name]) => !ENVIRONMENT_SHARED_UNIFORM_NAMES.includes(name))
403
+ .map(([name, uniform]) => [name, cloneUniformValue(uniform.value)]),
404
+ );
405
+ return;
406
+ }
407
+
408
+ for (const [name, value] of Object.entries(existingDefaults)) {
409
+ restoreUniformValue(uniforms[name], value);
410
+ }
411
+ }
412
+
413
+ export function applyEnvironmentSettingsToMaterial(material, settingsInput = {}) {
414
+ resetEnvironmentUniformsToBase(material);
415
+
416
+ const settings = createEnvironmentSettings(settingsInput);
417
+ const { features, parameters } = settings;
418
+ const uniforms = material?.uniforms;
419
+ if (!uniforms) return material;
420
+
421
+ setUniform(uniforms, 'enableAlphaCutout', featureValue(features, 'alphaCutout'));
422
+ setUniform(uniforms, 'enableAlphaMap', featureValue(features, 'alphaMap'));
423
+ setUniform(uniforms, 'enableAmbientLight', featureValue(features, 'ambientLight'));
424
+ setUniform(uniforms, 'enableAmbientProbe', featureValue(features, 'ambientProbe'));
425
+ setUniform(uniforms, 'enableAoMap', featureValue(features, 'aoMap'));
426
+ setUniform(uniforms, 'enableDirectionalLights', featureValue(features, 'directionalLights'));
427
+ setUniform(uniforms, 'enableEmissive', featureValue(features, 'emissive'));
428
+ setUniform(uniforms, 'enableFoliageCutout', featureValue(features, 'foliageCutout'));
429
+ setUniform(uniforms, 'enableHeightFog', featureValue(features, 'heightFog'));
430
+ setUniform(uniforms, 'enableInteriorOcclusion', featureValue(features, 'interiorOcclusion'));
431
+ setUniform(uniforms, 'enableLeftSideShadow', featureValue(features, 'leftSideShadow'));
432
+ setUniform(uniforms, 'enableLightMap', featureValue(features, 'lightMap'));
433
+ setUniform(uniforms, 'enableNormalMap', featureValue(features, 'normalMap'));
434
+ setUniform(uniforms, 'enablePackedMap', featureValue(features, 'packedMap'));
435
+ setUniform(uniforms, 'enablePlanarReflection', featureValue(features, 'planarReflection'));
436
+ setUniform(uniforms, 'enablePointLights', featureValue(features, 'pointLights'));
437
+ setUniform(uniforms, 'enableShadowMask', featureValue(features, 'shadowMask'));
438
+ setUniform(uniforms, 'enableSkyTint', featureValue(features, 'skyTint'));
439
+ setUniform(uniforms, 'enableSpecular', featureValue(features, 'specular'));
440
+ setUniform(uniforms, 'enableSpotLights', featureValue(features, 'spotLights'));
441
+ setUniform(uniforms, 'enableSunBoost', featureValue(features, 'sunBoost'));
442
+ setUniform(uniforms, 'enableUntexturedGradient', featureValue(features, 'untexturedGradient'));
443
+ setUniform(uniforms, 'enableVertexAo', featureValue(features, 'vertexAo'));
444
+ setUniform(uniforms, 'enableWindowCutout', featureValue(features, 'windowCutout'));
445
+
446
+ setNumberUniform(uniforms, 'ambientProbeBlend', parameters.ambientProbeBlend);
447
+ setNumberUniform(uniforms, 'ambientStrength', parameters.ambientStrength);
448
+ setNumberUniform(uniforms, 'ambientLightInfluence', parameters.ambientLightInfluence);
449
+ setNumberUniform(uniforms, 'aoMapStrength', parameters.aoMapStrength);
450
+ setNumberUniform(uniforms, 'aoWarmth', parameters.aoWarmth);
451
+ setNumberUniform(uniforms, 'bakedGlowStrength', parameters.bakedGlowStrength);
452
+ setNumberUniform(uniforms, 'emissiveMapStrength', parameters.emissiveMapStrength);
453
+ setNumberUniform(uniforms, 'heightFogDensity', parameters.heightFogDensity);
454
+ setNumberUniform(uniforms, 'heightFogFalloff', parameters.heightFogFalloff);
455
+ setNumberUniform(uniforms, 'interiorOcclusionStrength', parameters.interiorOcclusionStrength);
456
+ setNumberUniform(uniforms, 'lightMapLift', parameters.lightMapLift);
457
+ setNumberUniform(uniforms, 'lightMapStrength', parameters.lightMapStrength);
458
+ setNumberUniform(uniforms, 'normalMapStrength', parameters.normalMapStrength);
459
+ setNumberUniform(uniforms, 'planarReflectionFresnel', parameters.planarReflectionFresnel);
460
+ setNumberUniform(uniforms, 'planarReflectionStrength', parameters.planarReflectionStrength);
461
+ setNumberUniform(uniforms, 'specularShininess', parameters.specularShininess);
462
+ setNumberUniform(uniforms, 'specularSoftness', parameters.specularSoftness);
463
+ setNumberUniform(uniforms, 'specularStrength', parameters.specularStrength);
464
+ setNumberUniform(uniforms, 'untexturedGradientStrength', parameters.untexturedGradientStrength);
465
+ setNumberUniform(uniforms, 'vertexAoStrength', parameters.vertexAoStrength);
466
+ setNumberUniform(uniforms, 'cloudShadowCoverage', parameters.cloudShadowCoverage);
467
+ setNumberUniform(uniforms, 'cloudShadowScale', parameters.cloudShadowScale);
468
+ setNumberUniform(uniforms, 'cloudShadowStrength', parameters.cloudShadowStrength);
469
+ setNumberUniform(uniforms, 'directLightStrength', parameters.directLightStrength);
470
+ setNumberUniform(uniforms, 'emissiveStrength', parameters.emissiveStrength);
471
+ setNumberUniform(uniforms, 'exposure', parameters.exposure);
472
+ setNumberUniform(uniforms, 'leftSideShadow', parameters.leftSideShadow);
473
+ setNumberUniform(uniforms, 'lightingInfluence', parameters.lightingInfluence);
474
+ setNumberUniform(uniforms, 'packedOcclusionStrength', parameters.packedOcclusionStrength);
475
+ setNumberUniform(uniforms, 'pointLightStrength', parameters.pointLightStrength);
476
+ setNumberUniform(uniforms, 'saturation', parameters.saturation);
477
+ setNumberUniform(uniforms, 'shadeSoftness', parameters.shadeSoftness);
478
+ setNumberUniform(uniforms, 'shadeStrength', parameters.shadeStrength);
479
+ setNumberUniform(uniforms, 'shadowLift', parameters.shadowLift);
480
+ setNumberUniform(uniforms, 'skyTintStrength', parameters.skyTintStrength);
481
+ setNumberUniform(uniforms, 'spotLightStrength', parameters.spotLightStrength);
482
+ setNumberUniform(uniforms, 'sunBoost', parameters.sunBoost);
483
+ setColorUniform(uniforms, 'heightFogColor', parameters.heightFogColor);
484
+ setColorUniform(uniforms, 'interiorOcclusionColor', parameters.interiorOcclusionColor);
485
+ setColorUniform(uniforms, 'leftSideShadowColor', parameters.leftSideShadowColor);
486
+ setColorUniform(uniforms, 'shadowTintColor', parameters.shadowTintColor);
487
+ setColorUniform(uniforms, 'skyGroundTint', parameters.skyGroundTint);
488
+ setColorUniform(uniforms, 'skyTopTint', parameters.skyTopTint);
489
+ setColorUniform(uniforms, 'specularColor', parameters.specularColor);
490
+ setColorUniform(uniforms, 'sunBoostColor', parameters.sunBoostColor);
491
+
492
+ return material;
493
+ }
494
+
495
+ export function updateEnvironmentBoundsUniforms(material, environmentBox) {
496
+ const uniforms = material?.uniforms;
497
+ if (!uniforms || !environmentBox) return material;
498
+
499
+ const center = environmentBox.getCenter(new THREE.Vector3());
500
+ const size = environmentBox.getSize(new THREE.Vector3());
501
+ uniforms.environmentCenter?.value?.copy?.(center);
502
+ uniforms.environmentSize?.value?.copy?.(size);
503
+ return material;
504
+ }