@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,176 @@
1
+ export const DEFAULT_LOCAL_LIGHT_SETTINGS = Object.freeze({
2
+ defaultIntensity: 0.72,
3
+ defaultMaxContribution: 0.34,
4
+ defaultShadowLift: 0.58,
5
+ enabled: true,
6
+ eyeIntensity: 0.42,
7
+ eyeMaxContribution: 0.18,
8
+ eyeShadowLift: 0.9,
9
+ faceIntensity: 0.56,
10
+ faceMaxContribution: 0.24,
11
+ faceShadowLift: 0.84,
12
+ hairIntensity: 0.72,
13
+ hairMaxContribution: 0.34,
14
+ hairShadowLift: 0.58,
15
+ skinIntensity: 0.64,
16
+ skinMaxContribution: 0.3,
17
+ skinShadowLift: 0.72,
18
+ });
19
+
20
+ function firstDefined(source, keys) {
21
+ for (const key of keys) {
22
+ if (source?.[key] !== undefined) return source[key];
23
+ }
24
+ return undefined;
25
+ }
26
+
27
+ function numberOption(value, fallback, { min = -Infinity, max = Infinity } = {}) {
28
+ const nextValue = Number(value);
29
+ if (!Number.isFinite(nextValue)) return fallback;
30
+ return Math.min(max, Math.max(min, nextValue));
31
+ }
32
+
33
+ function enabledOption(value) {
34
+ if (typeof value === 'string') {
35
+ const normalized = value.trim().toLowerCase();
36
+ return normalized !== 'off' && normalized !== 'none' && normalized !== 'false' && normalized !== '0';
37
+ }
38
+ return value !== false;
39
+ }
40
+
41
+ function normalizeLocalLightOptions(options) {
42
+ if (options === false) return { enabled: false };
43
+ if (options === true) return { enabled: true };
44
+ if (typeof options === 'string') return { enabled: enabledOption(options) };
45
+ return options || {};
46
+ }
47
+
48
+ function roleNumber(source, role, property, fallback, aliases = []) {
49
+ const rolePrefix = role ? `${role}${property[0].toUpperCase()}${property.slice(1)}` : property;
50
+ return numberOption(
51
+ firstDefined(source, [rolePrefix, ...aliases]),
52
+ fallback,
53
+ { min: 0, max: property === 'shadowLift' ? 1 : 8 },
54
+ );
55
+ }
56
+
57
+ export function createLocalLightSettings(options = null) {
58
+ const source = normalizeLocalLightOptions(options);
59
+ const enabled = enabledOption(source.enabled);
60
+
61
+ const defaultIntensity = enabled
62
+ ? roleNumber(
63
+ source,
64
+ 'default',
65
+ 'intensity',
66
+ DEFAULT_LOCAL_LIGHT_SETTINGS.defaultIntensity,
67
+ ['intensity', 'localLightIntensity'],
68
+ )
69
+ : 0;
70
+ const defaultMaxContribution = enabled
71
+ ? roleNumber(
72
+ source,
73
+ 'default',
74
+ 'maxContribution',
75
+ DEFAULT_LOCAL_LIGHT_SETTINGS.defaultMaxContribution,
76
+ ['maxContribution', 'localLightMaxContribution'],
77
+ )
78
+ : 0;
79
+ const defaultShadowLift = enabled
80
+ ? roleNumber(
81
+ source,
82
+ 'default',
83
+ 'shadowLift',
84
+ DEFAULT_LOCAL_LIGHT_SETTINGS.defaultShadowLift,
85
+ ['shadowLift', 'localLightShadowLift'],
86
+ )
87
+ : 0;
88
+
89
+ return {
90
+ defaultIntensity,
91
+ defaultMaxContribution,
92
+ defaultShadowLift,
93
+ enabled,
94
+ eyeIntensity: enabled
95
+ ? roleNumber(source, 'eye', 'intensity', DEFAULT_LOCAL_LIGHT_SETTINGS.eyeIntensity)
96
+ : 0,
97
+ eyeMaxContribution: enabled
98
+ ? roleNumber(source, 'eye', 'maxContribution', DEFAULT_LOCAL_LIGHT_SETTINGS.eyeMaxContribution)
99
+ : 0,
100
+ eyeShadowLift: enabled
101
+ ? roleNumber(source, 'eye', 'shadowLift', DEFAULT_LOCAL_LIGHT_SETTINGS.eyeShadowLift)
102
+ : 0,
103
+ faceIntensity: enabled
104
+ ? roleNumber(source, 'face', 'intensity', DEFAULT_LOCAL_LIGHT_SETTINGS.faceIntensity)
105
+ : 0,
106
+ faceMaxContribution: enabled
107
+ ? roleNumber(source, 'face', 'maxContribution', DEFAULT_LOCAL_LIGHT_SETTINGS.faceMaxContribution)
108
+ : 0,
109
+ faceShadowLift: enabled
110
+ ? roleNumber(source, 'face', 'shadowLift', DEFAULT_LOCAL_LIGHT_SETTINGS.faceShadowLift)
111
+ : 0,
112
+ hairIntensity: enabled
113
+ ? roleNumber(source, 'hair', 'intensity', DEFAULT_LOCAL_LIGHT_SETTINGS.hairIntensity)
114
+ : 0,
115
+ hairMaxContribution: enabled
116
+ ? roleNumber(source, 'hair', 'maxContribution', DEFAULT_LOCAL_LIGHT_SETTINGS.hairMaxContribution)
117
+ : 0,
118
+ hairShadowLift: enabled
119
+ ? roleNumber(source, 'hair', 'shadowLift', DEFAULT_LOCAL_LIGHT_SETTINGS.hairShadowLift)
120
+ : 0,
121
+ skinIntensity: enabled
122
+ ? roleNumber(source, 'skin', 'intensity', DEFAULT_LOCAL_LIGHT_SETTINGS.skinIntensity)
123
+ : 0,
124
+ skinMaxContribution: enabled
125
+ ? roleNumber(source, 'skin', 'maxContribution', DEFAULT_LOCAL_LIGHT_SETTINGS.skinMaxContribution)
126
+ : 0,
127
+ skinShadowLift: enabled
128
+ ? roleNumber(source, 'skin', 'shadowLift', DEFAULT_LOCAL_LIGHT_SETTINGS.skinShadowLift)
129
+ : 0,
130
+ };
131
+ }
132
+
133
+ export function resolveLocalLightForMaterial(settings, {
134
+ isEye = false,
135
+ isFace = false,
136
+ isHair = false,
137
+ isSkin = false,
138
+ } = {}) {
139
+ if (isEye) {
140
+ return {
141
+ intensity: settings.eyeIntensity,
142
+ maxContribution: settings.eyeMaxContribution,
143
+ shadowLift: settings.eyeShadowLift,
144
+ };
145
+ }
146
+
147
+ if (isFace) {
148
+ return {
149
+ intensity: settings.faceIntensity,
150
+ maxContribution: settings.faceMaxContribution,
151
+ shadowLift: settings.faceShadowLift,
152
+ };
153
+ }
154
+
155
+ if (isSkin) {
156
+ return {
157
+ intensity: settings.skinIntensity,
158
+ maxContribution: settings.skinMaxContribution,
159
+ shadowLift: settings.skinShadowLift,
160
+ };
161
+ }
162
+
163
+ if (isHair) {
164
+ return {
165
+ intensity: settings.hairIntensity,
166
+ maxContribution: settings.hairMaxContribution,
167
+ shadowLift: settings.hairShadowLift,
168
+ };
169
+ }
170
+
171
+ return {
172
+ intensity: settings.defaultIntensity,
173
+ maxContribution: settings.defaultMaxContribution,
174
+ shadowLift: settings.defaultShadowLift,
175
+ };
176
+ }
@@ -0,0 +1,299 @@
1
+ import * as THREE from 'three';
2
+
3
+ export const MATERIAL_MAP_SOURCE_MODES = Object.freeze({
4
+ off: 'off',
5
+ source: 'source',
6
+ });
7
+
8
+ export const DEFAULT_MATERIAL_MAP_SETTINGS = Object.freeze({
9
+ aoStrength: 0,
10
+ detailRepeat: [1, 1],
11
+ detailStrength: 0,
12
+ emissiveColor: [1, 1, 1],
13
+ emissiveStrength: 0,
14
+ enabled: true,
15
+ matcapStrength: 0,
16
+ metalnessStrength: 0,
17
+ normalScale: [1, 1],
18
+ normalStrength: 0,
19
+ rampStrength: 0,
20
+ roughnessStrength: 0,
21
+ sourceMode: MATERIAL_MAP_SOURCE_MODES.source,
22
+ specularColorStrength: 0,
23
+ });
24
+
25
+ const COLOR_USER_DATA_TEXTURE_KEYS = Object.freeze([
26
+ 'toonDetailMap',
27
+ 'detailMap',
28
+ 'toonEmissiveMap',
29
+ 'emissiveMap',
30
+ 'toonMatcapMap',
31
+ 'matcapMap',
32
+ 'matcap',
33
+ 'toonRampMap',
34
+ 'rampMap',
35
+ 'shadeRampMap',
36
+ 'gradientMap',
37
+ 'toonSpecularColorMap',
38
+ 'specularColorMap',
39
+ ]);
40
+
41
+ const DATA_USER_DATA_TEXTURE_KEYS = Object.freeze([
42
+ 'toonAoMap',
43
+ 'aoMap',
44
+ 'toonMetalnessMap',
45
+ 'metalnessMap',
46
+ 'toonNormalMap',
47
+ 'normalMap',
48
+ 'toonRoughnessMap',
49
+ 'roughnessMap',
50
+ ]);
51
+
52
+ function firstDefined(source, keys) {
53
+ for (const key of keys) {
54
+ if (source?.[key] !== undefined) return source[key];
55
+ }
56
+ return undefined;
57
+ }
58
+
59
+ function numberOption(value, fallback, { min = -Infinity, max = Infinity } = {}) {
60
+ const nextValue = Number(value);
61
+ if (!Number.isFinite(nextValue)) return fallback;
62
+ return Math.min(max, Math.max(min, nextValue));
63
+ }
64
+
65
+ function enabledOption(value) {
66
+ if (typeof value === 'string') {
67
+ const normalized = value.trim().toLowerCase();
68
+ return normalized !== 'off' && normalized !== 'none' && normalized !== 'false' && normalized !== '0';
69
+ }
70
+ return value !== false;
71
+ }
72
+
73
+ function normalizeSourceMode(value, fallback = DEFAULT_MATERIAL_MAP_SETTINGS.sourceMode) {
74
+ if (value === true) return MATERIAL_MAP_SOURCE_MODES.source;
75
+ if (value === false) return MATERIAL_MAP_SOURCE_MODES.off;
76
+ const normalized = String(value ?? fallback).trim().replace(/[\s_-]+/g, '').toLowerCase();
77
+ if (normalized === 'source' || normalized === 'material' || normalized === 'maps') {
78
+ return MATERIAL_MAP_SOURCE_MODES.source;
79
+ }
80
+ return MATERIAL_MAP_SOURCE_MODES.off;
81
+ }
82
+
83
+ function normalizeMaterialMapOptions(options) {
84
+ if (options === false) return { enabled: false };
85
+ if (options === true) return { enabled: true };
86
+ if (typeof options === 'string') {
87
+ return {
88
+ enabled: enabledOption(options),
89
+ sourceMode: normalizeSourceMode(options),
90
+ };
91
+ }
92
+ return options || {};
93
+ }
94
+
95
+ function colorOption(value, fallback) {
96
+ if (value?.isColor) return value.clone();
97
+
98
+ if (Array.isArray(value) && value.length >= 3) {
99
+ return new THREE.Color(
100
+ numberOption(value[0], fallback[0] ?? fallback.r ?? 1),
101
+ numberOption(value[1], fallback[1] ?? fallback.g ?? 1),
102
+ numberOption(value[2], fallback[2] ?? fallback.b ?? 1),
103
+ );
104
+ }
105
+
106
+ if (typeof value === 'object' && value !== null) {
107
+ return new THREE.Color(
108
+ numberOption(value.r ?? value.x, fallback[0] ?? fallback.r ?? 1),
109
+ numberOption(value.g ?? value.y, fallback[1] ?? fallback.g ?? 1),
110
+ numberOption(value.b ?? value.z, fallback[2] ?? fallback.b ?? 1),
111
+ );
112
+ }
113
+
114
+ if (typeof value === 'string' || typeof value === 'number') {
115
+ try {
116
+ return new THREE.Color(value);
117
+ } catch {
118
+ // Fall through to fallback.
119
+ }
120
+ }
121
+
122
+ return Array.isArray(fallback)
123
+ ? new THREE.Color(fallback[0], fallback[1], fallback[2])
124
+ : fallback.clone();
125
+ }
126
+
127
+ function vector2Option(value, fallback) {
128
+ if (value?.isVector2) return value.clone();
129
+ if (Array.isArray(value) && value.length >= 2) {
130
+ return new THREE.Vector2(
131
+ numberOption(value[0], fallback[0] ?? fallback.x ?? 1),
132
+ numberOption(value[1], fallback[1] ?? fallback.y ?? 1),
133
+ );
134
+ }
135
+ if (typeof value === 'object' && value !== null) {
136
+ return new THREE.Vector2(
137
+ numberOption(value.x ?? value.u ?? value.r, fallback[0] ?? fallback.x ?? 1),
138
+ numberOption(value.y ?? value.v ?? value.g, fallback[1] ?? fallback.y ?? 1),
139
+ );
140
+ }
141
+ return Array.isArray(fallback)
142
+ ? new THREE.Vector2(fallback[0], fallback[1])
143
+ : fallback.clone();
144
+ }
145
+
146
+ function textureOption(value) {
147
+ return value?.isTexture ? value : null;
148
+ }
149
+
150
+ function sourceTexture(mat, materialKeys, userDataKeys) {
151
+ for (const key of materialKeys) {
152
+ const texture = textureOption(mat?.[key]);
153
+ if (texture) return texture;
154
+ }
155
+ for (const key of userDataKeys) {
156
+ const texture = textureOption(mat?.userData?.[key]);
157
+ if (texture) return texture;
158
+ }
159
+ return null;
160
+ }
161
+
162
+ export function createMaterialMapSettings(options = null) {
163
+ const source = normalizeMaterialMapOptions(options);
164
+ const enabled = enabledOption(source.enabled);
165
+
166
+ return {
167
+ aoMap: textureOption(firstDefined(source, ['aoMap', 'occlusionMap'])),
168
+ aoStrength: enabled
169
+ ? numberOption(firstDefined(source, ['aoStrength', 'occlusionStrength']), DEFAULT_MATERIAL_MAP_SETTINGS.aoStrength, { min: 0, max: 1 })
170
+ : 0,
171
+ detailMap: textureOption(firstDefined(source, ['detailMap', 'toonDetailMap'])),
172
+ detailRepeat: vector2Option(firstDefined(source, ['detailRepeat', 'detailScale']), DEFAULT_MATERIAL_MAP_SETTINGS.detailRepeat),
173
+ detailStrength: enabled
174
+ ? numberOption(firstDefined(source, ['detailStrength', 'detailIntensity']), DEFAULT_MATERIAL_MAP_SETTINGS.detailStrength, { min: 0, max: 1 })
175
+ : 0,
176
+ emissiveColor: colorOption(firstDefined(source, ['emissiveColor', 'emissionColor']), DEFAULT_MATERIAL_MAP_SETTINGS.emissiveColor),
177
+ emissiveMap: textureOption(firstDefined(source, ['emissiveMap', 'emissionMap'])),
178
+ emissiveStrength: enabled
179
+ ? numberOption(firstDefined(source, ['emissiveStrength', 'emissionStrength', 'emissiveIntensity']), DEFAULT_MATERIAL_MAP_SETTINGS.emissiveStrength, { min: 0, max: 8 })
180
+ : 0,
181
+ enabled,
182
+ matcapMap: textureOption(firstDefined(source, ['matcapMap', 'matcap'])),
183
+ matcapStrength: enabled
184
+ ? numberOption(firstDefined(source, ['matcapStrength', 'matcapIntensity']), DEFAULT_MATERIAL_MAP_SETTINGS.matcapStrength, { min: 0, max: 1 })
185
+ : 0,
186
+ metalnessMap: textureOption(firstDefined(source, ['metalnessMap', 'metallicMap'])),
187
+ metalnessStrength: enabled
188
+ ? numberOption(firstDefined(source, ['metalnessStrength', 'metallicStrength']), DEFAULT_MATERIAL_MAP_SETTINGS.metalnessStrength, { min: 0, max: 1 })
189
+ : 0,
190
+ normalMap: textureOption(firstDefined(source, ['normalMap', 'toonNormalMap'])),
191
+ normalScale: vector2Option(firstDefined(source, ['normalScale']), DEFAULT_MATERIAL_MAP_SETTINGS.normalScale),
192
+ normalStrength: enabled
193
+ ? numberOption(firstDefined(source, ['normalStrength', 'normalMapStrength']), DEFAULT_MATERIAL_MAP_SETTINGS.normalStrength, { min: 0, max: 1 })
194
+ : 0,
195
+ rampMap: textureOption(firstDefined(source, ['rampMap', 'shadeRampMap', 'gradientMap'])),
196
+ rampStrength: enabled
197
+ ? numberOption(firstDefined(source, ['rampStrength', 'shadeRampStrength']), DEFAULT_MATERIAL_MAP_SETTINGS.rampStrength, { min: 0, max: 1 })
198
+ : 0,
199
+ roughnessMap: textureOption(firstDefined(source, ['roughnessMap'])),
200
+ roughnessStrength: enabled
201
+ ? numberOption(firstDefined(source, ['roughnessStrength', 'roughnessMapStrength']), DEFAULT_MATERIAL_MAP_SETTINGS.roughnessStrength, { min: 0, max: 1 })
202
+ : 0,
203
+ sourceMode: normalizeSourceMode(firstDefined(source, ['sourceMode', 'sourceMaps', 'useSourceMaps'])),
204
+ specularColorMap: textureOption(firstDefined(source, ['specularColorMap', 'specularMap'])),
205
+ specularColorStrength: enabled
206
+ ? numberOption(firstDefined(source, ['specularColorStrength', 'specularMapStrength']), DEFAULT_MATERIAL_MAP_SETTINGS.specularColorStrength, { min: 0, max: 1 })
207
+ : 0,
208
+ };
209
+ }
210
+
211
+ export function collectMaterialMapTextures(mat) {
212
+ const textures = [];
213
+
214
+ for (const key of COLOR_USER_DATA_TEXTURE_KEYS) {
215
+ const texture = textureOption(mat?.userData?.[key]);
216
+ if (texture) textures.push({ texture, colorSpace: THREE.SRGBColorSpace });
217
+ }
218
+
219
+ for (const key of DATA_USER_DATA_TEXTURE_KEYS) {
220
+ const texture = textureOption(mat?.userData?.[key]);
221
+ if (texture) textures.push({ texture, colorSpace: THREE.NoColorSpace });
222
+ }
223
+
224
+ return textures;
225
+ }
226
+
227
+ export function resolveMaterialMapsForMaterial(settings, mat) {
228
+ const sourceEnabled = settings.enabled && settings.sourceMode === MATERIAL_MAP_SOURCE_MODES.source;
229
+
230
+ const normalScale = settings.normalScale.clone();
231
+ if (mat?.normalScale?.isVector2) normalScale.multiply(mat.normalScale);
232
+
233
+ const emissiveColor = settings.emissiveColor.clone();
234
+ if (mat?.emissive?.isColor) emissiveColor.multiply(mat.emissive);
235
+ const sourceEmissiveIntensity = Number.isFinite(mat?.emissiveIntensity) ? mat.emissiveIntensity : 1;
236
+
237
+ const normalMap = settings.normalMap ?? (sourceEnabled
238
+ ? sourceTexture(mat, ['normalMap'], ['toonNormalMap', 'normalMap'])
239
+ : null);
240
+ const aoMap = settings.aoMap ?? (sourceEnabled
241
+ ? sourceTexture(mat, ['aoMap'], ['toonAoMap', 'aoMap', 'occlusionMap'])
242
+ : null);
243
+ const emissiveMap = settings.emissiveMap ?? (sourceEnabled
244
+ ? sourceTexture(mat, ['emissiveMap'], ['toonEmissiveMap', 'emissiveMap', 'emissionMap'])
245
+ : null);
246
+ const matcapMap = settings.matcapMap ?? (sourceEnabled
247
+ ? sourceTexture(mat, ['matcap'], ['toonMatcapMap', 'matcapMap', 'matcap'])
248
+ : null);
249
+ const rampMap = settings.rampMap ?? (sourceEnabled
250
+ ? sourceTexture(mat, ['gradientMap'], ['toonRampMap', 'rampMap', 'shadeRampMap', 'gradientMap'])
251
+ : null);
252
+ const detailMap = settings.detailMap ?? (sourceEnabled
253
+ ? sourceTexture(mat, [], ['toonDetailMap', 'detailMap'])
254
+ : null);
255
+ const roughnessMap = settings.roughnessMap ?? (sourceEnabled
256
+ ? sourceTexture(mat, ['roughnessMap'], ['toonRoughnessMap', 'roughnessMap'])
257
+ : null);
258
+ const metalnessMap = settings.metalnessMap ?? (sourceEnabled
259
+ ? sourceTexture(mat, ['metalnessMap'], ['toonMetalnessMap', 'metalnessMap', 'metallicMap'])
260
+ : null);
261
+ const specularColorMap = settings.specularColorMap ?? (sourceEnabled
262
+ ? sourceTexture(mat, ['specularColorMap', 'specularMap'], ['toonSpecularColorMap', 'specularColorMap', 'specularMap'])
263
+ : null);
264
+
265
+ return {
266
+ aoMap,
267
+ aoStrength: settings.aoStrength,
268
+ detailMap,
269
+ detailRepeat: settings.detailRepeat,
270
+ detailStrength: settings.detailStrength,
271
+ emissiveColor,
272
+ emissiveMap,
273
+ emissiveStrength: settings.emissiveStrength * sourceEmissiveIntensity,
274
+ hasAoMap: Boolean(aoMap),
275
+ hasDetailMap: Boolean(detailMap),
276
+ hasEmissiveMap: Boolean(emissiveMap),
277
+ hasMatcapMap: Boolean(matcapMap),
278
+ hasMetalnessMap: Boolean(metalnessMap),
279
+ hasNormalMap: Boolean(normalMap),
280
+ hasRampMap: Boolean(rampMap),
281
+ hasRoughnessMap: Boolean(roughnessMap),
282
+ hasSpecularColorMap: Boolean(specularColorMap),
283
+ matcapMap,
284
+ matcapStrength: settings.matcapStrength,
285
+ metalness: Number.isFinite(mat?.metalness) ? mat.metalness : 0,
286
+ metalnessMap,
287
+ metalnessStrength: settings.metalnessStrength,
288
+ normalMap,
289
+ normalScale,
290
+ normalStrength: settings.normalStrength,
291
+ rampMap,
292
+ rampStrength: settings.rampStrength,
293
+ roughness: Number.isFinite(mat?.roughness) ? mat.roughness : 0.5,
294
+ roughnessMap,
295
+ roughnessStrength: settings.roughnessStrength,
296
+ specularColorMap,
297
+ specularColorStrength: settings.specularColorStrength,
298
+ };
299
+ }