@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.
- package/ATTRIBUTION.md +40 -0
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/package.json +97 -0
- package/src/character/characterRig.js +353 -0
- package/src/character/freestyleSwimClip.js +541 -0
- package/src/character/index.js +4 -0
- package/src/character/modelLoader.js +433 -0
- package/src/core/materialRoles.js +438 -0
- package/src/core/presetDocuments.js +110 -0
- package/src/core/shaderBackend.js +24 -0
- package/src/debrisgen/debrisFields.js +544 -0
- package/src/debrisgen/debrisGenerator.js +1929 -0
- package/src/debrisgen/debrisPalettes.js +71 -0
- package/src/debrisgen/debrisPhysics.js +198 -0
- package/src/debrisgen/debrisPresets.js +155 -0
- package/src/debrisgen/debrisSettings.js +333 -0
- package/src/debrisgen/debrisTextures.js +380 -0
- package/src/debrisgen/index.js +5 -0
- package/src/debug/fieldValues.js +95 -0
- package/src/debug/index.js +7 -0
- package/src/debug/settingsPanel.js +170 -0
- package/src/environment/environmentAmbientProbe.js +160 -0
- package/src/environment/environmentMaterialAdapter.js +336 -0
- package/src/environment/environmentMaterialClassifier.js +178 -0
- package/src/environment/environmentPlanarReflection.js +201 -0
- package/src/environment/environmentPresets.js +443 -0
- package/src/environment/environmentRigs.js +684 -0
- package/src/environment/environmentSettings.js +504 -0
- package/src/environment/environmentShaderMaterials.js +318 -0
- package/src/environment/environmentSunShadowPass.js +268 -0
- package/src/environment/environmentTextureResolver.js +161 -0
- package/src/environment/environmentTimeOfDay.js +167 -0
- package/src/environment/environmentVertexAo.js +188 -0
- package/src/environment/index.js +8 -0
- package/src/environment/scanAssetStylize.js +103 -0
- package/src/index.js +21 -0
- package/src/loaders/index.js +1 -0
- package/src/post/index.js +2 -0
- package/src/post/postProcessing.js +1087 -0
- package/src/rockgen/export/glbExport.js +177 -0
- package/src/rockgen/heightfield/heightfieldErosion.js +7 -0
- package/src/rockgen/heightfield/heightfieldPatch.js +183 -0
- package/src/rockgen/heightfield/stylizedErosionSim.js +356 -0
- package/src/rockgen/index.js +14 -0
- package/src/rockgen/mesh/meshAttributes.js +374 -0
- package/src/rockgen/mesh/meshDocument.js +158 -0
- package/src/rockgen/mesh/surfaceNets.js +329 -0
- package/src/rockgen/noise/cellularNoise3.js +92 -0
- package/src/rockgen/noise/prng.js +62 -0
- package/src/rockgen/noise/simplexNoise3.js +89 -0
- package/src/rockgen/noise/valueNoise3.js +87 -0
- package/src/rockgen/rockDocument.js +255 -0
- package/src/rockgen/rockHelpers.js +8 -0
- package/src/rockgen/rockgenPresets.js +559 -0
- package/src/rockgen/rockgenSettings.js +756 -0
- package/src/rockgen/sdf/fieldCompiler.js +544 -0
- package/src/rockgen/sdf/sculptEdits.js +66 -0
- package/src/rockgen/sdf/sdfModifiers.js +45 -0
- package/src/rockgen/sdf/sdfOps.js +37 -0
- package/src/rockgen/sdf/sdfPrimitives.js +88 -0
- package/src/shaders-tsl/anime.js +963 -0
- package/src/shaders-tsl/chunks/character-color.js +64 -0
- package/src/shaders-tsl/chunks/character-highlights.js +174 -0
- package/src/shaders-tsl/chunks/character-lighting.js +309 -0
- package/src/shaders-tsl/chunks/character-material-maps.js +146 -0
- package/src/shaders-tsl/chunks/character-roles.js +52 -0
- package/src/shaders-tsl/chunks/character-scene-lights.js +270 -0
- package/src/shaders-tsl/chunks/character-shadow-color.js +61 -0
- package/src/shaders-tsl/chunks/character-skinning.js +140 -0
- package/src/shaders-tsl/chunks/environment-color.js +47 -0
- package/src/shaders-tsl/chunks/environment-debug.js +79 -0
- package/src/shaders-tsl/chunks/environment-lighting.js +260 -0
- package/src/shaders-tsl/chunks/environment-sun-shadow.js +87 -0
- package/src/shaders-tsl/chunks/foliage-fog.js +60 -0
- package/src/shaders-tsl/chunks/pass-depth-color.js +89 -0
- package/src/shaders-tsl/chunks/stylized-cloud-shadow.js +70 -0
- package/src/shaders-tsl/chunks/water-color.js +190 -0
- package/src/shaders-tsl/chunks/water-common.js +124 -0
- package/src/shaders-tsl/chunks/water-foam.js +93 -0
- package/src/shaders-tsl/chunks/water-lighting.js +116 -0
- package/src/shaders-tsl/chunks/water-ripple.js +64 -0
- package/src/shaders-tsl/chunks/water-waves.js +100 -0
- package/src/shaders-tsl/environment-ao-overlay.js +60 -0
- package/src/shaders-tsl/environment.js +680 -0
- package/src/shaders-tsl/flower.js +267 -0
- package/src/shaders-tsl/grass.js +193 -0
- package/src/shaders-tsl/post-composite.js +461 -0
- package/src/shaders-tsl/sky.js +175 -0
- package/src/shaders-tsl/tree-leaf.js +264 -0
- package/src/shaders-tsl/water-breaker.js +423 -0
- package/src/shaders-tsl/water-kelp.js +171 -0
- package/src/shaders-tsl/water-rain.js +107 -0
- package/src/shaders-tsl/water-simulation.js +140 -0
- package/src/shaders-tsl/water-splash.js +298 -0
- package/src/shaders-tsl/water.js +509 -0
- package/src/sky/index.js +2 -0
- package/src/sky/stylizedSky.js +341 -0
- package/src/toon/characterRenderPasses.js +646 -0
- package/src/toon/index.js +4 -0
- package/src/toon/settings/alphaSettings.js +170 -0
- package/src/toon/settings/averageShadowSettings.js +181 -0
- package/src/toon/settings/baseTextureSettings.js +133 -0
- package/src/toon/settings/celShadeSettings.js +96 -0
- package/src/toon/settings/contactShadowSettings.js +114 -0
- package/src/toon/settings/eyeHighlightSettings.js +187 -0
- package/src/toon/settings/faceLightingSettings.js +135 -0
- package/src/toon/settings/furSettings.js +112 -0
- package/src/toon/settings/glitterSettings.js +102 -0
- package/src/toon/settings/hairHighlightSettings.js +325 -0
- package/src/toon/settings/indirectLightSettings.js +244 -0
- package/src/toon/settings/localLightSettings.js +176 -0
- package/src/toon/settings/materialMapSettings.js +299 -0
- package/src/toon/settings/outlineSettings.js +263 -0
- package/src/toon/settings/perspectiveRemovalSettings.js +60 -0
- package/src/toon/settings/rimLightSettings.js +268 -0
- package/src/toon/settings/sceneShadowSettings.js +147 -0
- package/src/toon/settings/selfShadowSettings.js +221 -0
- package/src/toon/settings/shadowColorSettings.js +238 -0
- package/src/toon/settings/skinToneSettings.js +150 -0
- package/src/toon/settings/specularSettings.js +314 -0
- package/src/toon/settings/stickerSettings.js +102 -0
- package/src/toon/toonMaterialAdapter.js +2235 -0
- package/src/toon/toonSettings.js +860 -0
- package/src/vegetation/flowerSpecies.js +260 -0
- package/src/vegetation/index.js +10 -0
- package/src/vegetation/stylizedBush.js +136 -0
- package/src/vegetation/stylizedFlower.js +213 -0
- package/src/vegetation/stylizedFlowers.js +297 -0
- package/src/vegetation/stylizedGrass.js +491 -0
- package/src/vegetation/stylizedTree.js +2449 -0
- package/src/vegetation/stylizedTreeFoliage.js +691 -0
- package/src/vegetation/treeExport.js +300 -0
- package/src/vegetation/treeRecipe.js +647 -0
- package/src/water/index.js +2 -0
- package/src/water/water.js +20 -0
- package/src/water/waterBreakerSystem.js +591 -0
- package/src/water/waterInteraction.js +193 -0
- package/src/water/waterMaterial.js +263 -0
- package/src/water/waterRain.js +136 -0
- package/src/water/waterRippleSimulation.js +227 -0
- package/src/water/waterScenePasses.js +415 -0
- package/src/water/waterSettings.js +1138 -0
- package/src/water/waterSplashSystem.js +251 -0
- package/src/water/waterSurface.js +475 -0
- 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
|
+
}
|