@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,147 @@
|
|
|
1
|
+
export const DEFAULT_SCENE_SHADOW_SETTINGS = Object.freeze({
|
|
2
|
+
defaultMinLight: 0.24,
|
|
3
|
+
defaultStrength: 0.76,
|
|
4
|
+
enabled: true,
|
|
5
|
+
eyeMinLight: 0.42,
|
|
6
|
+
eyeStrength: 0.05,
|
|
7
|
+
faceMinLight: 0.42,
|
|
8
|
+
faceStrength: 0.46,
|
|
9
|
+
shadowAreaStrength: 0.65,
|
|
10
|
+
skinMinLight: 0.34,
|
|
11
|
+
skinStrength: 0.62,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
function firstDefined(source, keys) {
|
|
15
|
+
for (const key of keys) {
|
|
16
|
+
if (source?.[key] !== undefined) return source[key];
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function numberOption(value, fallback, { min = -Infinity, max = Infinity } = {}) {
|
|
22
|
+
const nextValue = Number(value);
|
|
23
|
+
if (!Number.isFinite(nextValue)) return fallback;
|
|
24
|
+
return Math.min(max, Math.max(min, nextValue));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createSceneShadowSettings(options = null) {
|
|
28
|
+
const source = options || {};
|
|
29
|
+
const enabled = source.enabled !== false;
|
|
30
|
+
|
|
31
|
+
const defaultStrength = enabled
|
|
32
|
+
? numberOption(
|
|
33
|
+
firstDefined(source, [
|
|
34
|
+
'defaultStrength',
|
|
35
|
+
'strength',
|
|
36
|
+
'receiveStrength',
|
|
37
|
+
'receivedShadowStrength',
|
|
38
|
+
]),
|
|
39
|
+
DEFAULT_SCENE_SHADOW_SETTINGS.defaultStrength,
|
|
40
|
+
{ min: 0, max: 1 },
|
|
41
|
+
)
|
|
42
|
+
: 0;
|
|
43
|
+
const defaultMinLight = enabled
|
|
44
|
+
? numberOption(
|
|
45
|
+
firstDefined(source, [
|
|
46
|
+
'defaultMinLight',
|
|
47
|
+
'minLight',
|
|
48
|
+
'receiveMinLight',
|
|
49
|
+
'receivedShadowMinLight',
|
|
50
|
+
]),
|
|
51
|
+
DEFAULT_SCENE_SHADOW_SETTINGS.defaultMinLight,
|
|
52
|
+
{ min: 0, max: 1 },
|
|
53
|
+
)
|
|
54
|
+
: 1;
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
defaultMinLight,
|
|
58
|
+
defaultStrength,
|
|
59
|
+
enabled,
|
|
60
|
+
eyeMinLight: enabled
|
|
61
|
+
? numberOption(
|
|
62
|
+
firstDefined(source, ['eyeMinLight', 'eyeReceivedShadowMinLight']),
|
|
63
|
+
DEFAULT_SCENE_SHADOW_SETTINGS.eyeMinLight,
|
|
64
|
+
{ min: 0, max: 1 },
|
|
65
|
+
)
|
|
66
|
+
: 1,
|
|
67
|
+
eyeStrength: enabled
|
|
68
|
+
? numberOption(
|
|
69
|
+
firstDefined(source, ['eyeStrength', 'eyeReceiveStrength', 'eyeReceivedShadowStrength']),
|
|
70
|
+
DEFAULT_SCENE_SHADOW_SETTINGS.eyeStrength,
|
|
71
|
+
{ min: 0, max: 1 },
|
|
72
|
+
)
|
|
73
|
+
: 0,
|
|
74
|
+
faceMinLight: enabled
|
|
75
|
+
? numberOption(
|
|
76
|
+
firstDefined(source, ['faceMinLight', 'faceReceivedShadowMinLight']),
|
|
77
|
+
DEFAULT_SCENE_SHADOW_SETTINGS.faceMinLight,
|
|
78
|
+
{ min: 0, max: 1 },
|
|
79
|
+
)
|
|
80
|
+
: 1,
|
|
81
|
+
faceStrength: enabled
|
|
82
|
+
? numberOption(
|
|
83
|
+
firstDefined(source, ['faceStrength', 'faceReceiveStrength', 'faceReceivedShadowStrength']),
|
|
84
|
+
DEFAULT_SCENE_SHADOW_SETTINGS.faceStrength,
|
|
85
|
+
{ min: 0, max: 1 },
|
|
86
|
+
)
|
|
87
|
+
: 0,
|
|
88
|
+
shadowAreaStrength: enabled
|
|
89
|
+
? numberOption(
|
|
90
|
+
firstDefined(source, [
|
|
91
|
+
'shadowAreaStrength',
|
|
92
|
+
'directVisibilityStrength',
|
|
93
|
+
'sceneShadowBlend',
|
|
94
|
+
'shadowBlend',
|
|
95
|
+
]),
|
|
96
|
+
DEFAULT_SCENE_SHADOW_SETTINGS.shadowAreaStrength,
|
|
97
|
+
{ min: 0, max: 1 },
|
|
98
|
+
)
|
|
99
|
+
: 0,
|
|
100
|
+
skinMinLight: enabled
|
|
101
|
+
? numberOption(
|
|
102
|
+
firstDefined(source, ['skinMinLight', 'skinReceivedShadowMinLight']),
|
|
103
|
+
DEFAULT_SCENE_SHADOW_SETTINGS.skinMinLight,
|
|
104
|
+
{ min: 0, max: 1 },
|
|
105
|
+
)
|
|
106
|
+
: 1,
|
|
107
|
+
skinStrength: enabled
|
|
108
|
+
? numberOption(
|
|
109
|
+
firstDefined(source, ['skinStrength', 'skinReceiveStrength', 'skinReceivedShadowStrength']),
|
|
110
|
+
DEFAULT_SCENE_SHADOW_SETTINGS.skinStrength,
|
|
111
|
+
{ min: 0, max: 1 },
|
|
112
|
+
)
|
|
113
|
+
: 0,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function resolveSceneShadowForMaterial(settings, {
|
|
118
|
+
isEye = false,
|
|
119
|
+
isFace = false,
|
|
120
|
+
isSkin = false,
|
|
121
|
+
} = {}) {
|
|
122
|
+
if (isEye) {
|
|
123
|
+
return {
|
|
124
|
+
minLight: settings.eyeMinLight,
|
|
125
|
+
strength: settings.eyeStrength,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (isFace) {
|
|
130
|
+
return {
|
|
131
|
+
minLight: settings.faceMinLight,
|
|
132
|
+
strength: settings.faceStrength,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (isSkin) {
|
|
137
|
+
return {
|
|
138
|
+
minLight: settings.skinMinLight,
|
|
139
|
+
strength: settings.skinStrength,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
minLight: settings.defaultMinLight,
|
|
145
|
+
strength: settings.defaultStrength,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
export const SELF_SHADOW_SOURCE_MODES = Object.freeze({
|
|
2
|
+
off: 0,
|
|
3
|
+
sceneProxy: 1,
|
|
4
|
+
// Dedicated character-only orthographic shadow map rendered by
|
|
5
|
+
// characterRenderPasses.js. Materials fall back to "no self shadow" when the
|
|
6
|
+
// runtime pass is not active, so this is safe as a default source.
|
|
7
|
+
characterPass: 2,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const DEFAULT_SELF_SHADOW_SETTINGS = Object.freeze({
|
|
11
|
+
defaultMinLight: 0.62,
|
|
12
|
+
defaultStrength: 0.22,
|
|
13
|
+
enabled: true,
|
|
14
|
+
eyeMinLight: 1,
|
|
15
|
+
eyeStrength: 0,
|
|
16
|
+
faceMinLight: 1,
|
|
17
|
+
faceStrength: 0,
|
|
18
|
+
hairMinLight: 0.58,
|
|
19
|
+
hairStrength: 0.26,
|
|
20
|
+
shadowAreaStrength: 0.5,
|
|
21
|
+
skinMinLight: 0.72,
|
|
22
|
+
skinStrength: 0.16,
|
|
23
|
+
sourceMode: SELF_SHADOW_SOURCE_MODES.characterPass,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
function firstDefined(source, keys) {
|
|
27
|
+
for (const key of keys) {
|
|
28
|
+
if (source?.[key] !== undefined) return source[key];
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function numberOption(value, fallback, { min = -Infinity, max = Infinity } = {}) {
|
|
34
|
+
const nextValue = Number(value);
|
|
35
|
+
if (!Number.isFinite(nextValue)) return fallback;
|
|
36
|
+
return Math.min(max, Math.max(min, nextValue));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeSourceMode(value, enabled) {
|
|
40
|
+
if (value === SELF_SHADOW_SOURCE_MODES.sceneProxy) return SELF_SHADOW_SOURCE_MODES.sceneProxy;
|
|
41
|
+
if (value === SELF_SHADOW_SOURCE_MODES.characterPass) return SELF_SHADOW_SOURCE_MODES.characterPass;
|
|
42
|
+
|
|
43
|
+
const key = String(value ?? (enabled ? 'characterPass' : 'off'))
|
|
44
|
+
.trim()
|
|
45
|
+
.replace(/[\s_-]+/g, '')
|
|
46
|
+
.toLowerCase();
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
key === 'scene' ||
|
|
50
|
+
key === 'sceneshadow' ||
|
|
51
|
+
key === 'sceneproxy' ||
|
|
52
|
+
key === 'shadowmask'
|
|
53
|
+
) {
|
|
54
|
+
return SELF_SHADOW_SOURCE_MODES.sceneProxy;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (
|
|
58
|
+
key === 'characterpass' ||
|
|
59
|
+
key === 'character' ||
|
|
60
|
+
key === 'charpass' ||
|
|
61
|
+
key === 'pass' ||
|
|
62
|
+
key === 'shadowmap' ||
|
|
63
|
+
key === 'selfshadowmap'
|
|
64
|
+
) {
|
|
65
|
+
return SELF_SHADOW_SOURCE_MODES.characterPass;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return SELF_SHADOW_SOURCE_MODES.off;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizeSelfShadowOptions(options) {
|
|
72
|
+
if (options === false) return { enabled: false };
|
|
73
|
+
if (options === true) return { enabled: true };
|
|
74
|
+
if (typeof options === 'string') {
|
|
75
|
+
const key = options.trim().toLowerCase();
|
|
76
|
+
return {
|
|
77
|
+
enabled: key !== 'off' && key !== 'none' && key !== 'false' && key !== '0',
|
|
78
|
+
sourceMode: options,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return options || {};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function createSelfShadowSettings(options = null) {
|
|
85
|
+
const source = normalizeSelfShadowOptions(options);
|
|
86
|
+
const requestedSourceMode = firstDefined(source, ['sourceMode', 'source', 'mode', 'maskSource']);
|
|
87
|
+
const enabled = source.enabled === true ||
|
|
88
|
+
(source.enabled !== false &&
|
|
89
|
+
(requestedSourceMode !== undefined || DEFAULT_SELF_SHADOW_SETTINGS.enabled));
|
|
90
|
+
const sourceMode = enabled
|
|
91
|
+
? normalizeSourceMode(requestedSourceMode, enabled)
|
|
92
|
+
: SELF_SHADOW_SOURCE_MODES.off;
|
|
93
|
+
const active = enabled && sourceMode !== SELF_SHADOW_SOURCE_MODES.off;
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
defaultMinLight: active
|
|
97
|
+
? numberOption(
|
|
98
|
+
firstDefined(source, ['defaultMinLight', 'minLight', 'selfShadowMinLight']),
|
|
99
|
+
DEFAULT_SELF_SHADOW_SETTINGS.defaultMinLight,
|
|
100
|
+
{ min: 0, max: 1 },
|
|
101
|
+
)
|
|
102
|
+
: 1,
|
|
103
|
+
defaultStrength: active
|
|
104
|
+
? numberOption(
|
|
105
|
+
firstDefined(source, ['defaultStrength', 'strength', 'selfShadowStrength']),
|
|
106
|
+
DEFAULT_SELF_SHADOW_SETTINGS.defaultStrength,
|
|
107
|
+
{ min: 0, max: 1 },
|
|
108
|
+
)
|
|
109
|
+
: 0,
|
|
110
|
+
enabled: active,
|
|
111
|
+
eyeMinLight: active
|
|
112
|
+
? numberOption(
|
|
113
|
+
firstDefined(source, ['eyeMinLight', 'eyeSelfShadowMinLight']),
|
|
114
|
+
DEFAULT_SELF_SHADOW_SETTINGS.eyeMinLight,
|
|
115
|
+
{ min: 0, max: 1 },
|
|
116
|
+
)
|
|
117
|
+
: 1,
|
|
118
|
+
eyeStrength: active
|
|
119
|
+
? numberOption(
|
|
120
|
+
firstDefined(source, ['eyeStrength', 'eyeSelfShadowStrength']),
|
|
121
|
+
DEFAULT_SELF_SHADOW_SETTINGS.eyeStrength,
|
|
122
|
+
{ min: 0, max: 1 },
|
|
123
|
+
)
|
|
124
|
+
: 0,
|
|
125
|
+
faceMinLight: active
|
|
126
|
+
? numberOption(
|
|
127
|
+
firstDefined(source, ['faceMinLight', 'faceSelfShadowMinLight']),
|
|
128
|
+
DEFAULT_SELF_SHADOW_SETTINGS.faceMinLight,
|
|
129
|
+
{ min: 0, max: 1 },
|
|
130
|
+
)
|
|
131
|
+
: 1,
|
|
132
|
+
faceStrength: active
|
|
133
|
+
? numberOption(
|
|
134
|
+
firstDefined(source, ['faceStrength', 'faceSelfShadowStrength']),
|
|
135
|
+
DEFAULT_SELF_SHADOW_SETTINGS.faceStrength,
|
|
136
|
+
{ min: 0, max: 1 },
|
|
137
|
+
)
|
|
138
|
+
: 0,
|
|
139
|
+
hairMinLight: active
|
|
140
|
+
? numberOption(
|
|
141
|
+
firstDefined(source, ['hairMinLight', 'hairSelfShadowMinLight']),
|
|
142
|
+
DEFAULT_SELF_SHADOW_SETTINGS.hairMinLight,
|
|
143
|
+
{ min: 0, max: 1 },
|
|
144
|
+
)
|
|
145
|
+
: 1,
|
|
146
|
+
hairStrength: active
|
|
147
|
+
? numberOption(
|
|
148
|
+
firstDefined(source, ['hairStrength', 'hairSelfShadowStrength']),
|
|
149
|
+
DEFAULT_SELF_SHADOW_SETTINGS.hairStrength,
|
|
150
|
+
{ min: 0, max: 1 },
|
|
151
|
+
)
|
|
152
|
+
: 0,
|
|
153
|
+
shadowAreaStrength: active
|
|
154
|
+
? numberOption(
|
|
155
|
+
firstDefined(source, [
|
|
156
|
+
'shadowAreaStrength',
|
|
157
|
+
'selfShadowAreaStrength',
|
|
158
|
+
'areaStrength',
|
|
159
|
+
'blend',
|
|
160
|
+
]),
|
|
161
|
+
DEFAULT_SELF_SHADOW_SETTINGS.shadowAreaStrength,
|
|
162
|
+
{ min: 0, max: 1 },
|
|
163
|
+
)
|
|
164
|
+
: 0,
|
|
165
|
+
skinMinLight: active
|
|
166
|
+
? numberOption(
|
|
167
|
+
firstDefined(source, ['skinMinLight', 'skinSelfShadowMinLight']),
|
|
168
|
+
DEFAULT_SELF_SHADOW_SETTINGS.skinMinLight,
|
|
169
|
+
{ min: 0, max: 1 },
|
|
170
|
+
)
|
|
171
|
+
: 1,
|
|
172
|
+
skinStrength: active
|
|
173
|
+
? numberOption(
|
|
174
|
+
firstDefined(source, ['skinStrength', 'skinSelfShadowStrength']),
|
|
175
|
+
DEFAULT_SELF_SHADOW_SETTINGS.skinStrength,
|
|
176
|
+
{ min: 0, max: 1 },
|
|
177
|
+
)
|
|
178
|
+
: 0,
|
|
179
|
+
sourceMode,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function resolveSelfShadowForMaterial(settings, {
|
|
184
|
+
isEye = false,
|
|
185
|
+
isFace = false,
|
|
186
|
+
isHair = false,
|
|
187
|
+
isSkin = false,
|
|
188
|
+
} = {}) {
|
|
189
|
+
if (isEye) {
|
|
190
|
+
return {
|
|
191
|
+
minLight: settings.eyeMinLight,
|
|
192
|
+
strength: settings.eyeStrength,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (isFace) {
|
|
197
|
+
return {
|
|
198
|
+
minLight: settings.faceMinLight,
|
|
199
|
+
strength: settings.faceStrength,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (isSkin) {
|
|
204
|
+
return {
|
|
205
|
+
minLight: settings.skinMinLight,
|
|
206
|
+
strength: settings.skinStrength,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (isHair) {
|
|
211
|
+
return {
|
|
212
|
+
minLight: settings.hairMinLight,
|
|
213
|
+
strength: settings.hairStrength,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
minLight: settings.defaultMinLight,
|
|
219
|
+
strength: settings.defaultStrength,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_SHADOW_COLOR_SETTINGS = Object.freeze({
|
|
4
|
+
enabled: true,
|
|
5
|
+
lowSaturationFallbackColor: [0.3764706, 0.4141177, 0.5019608, 0],
|
|
6
|
+
selfShadowAlbedoMulStrength: 0,
|
|
7
|
+
selfShadowAreaHSVStrength: 1,
|
|
8
|
+
selfShadowAreaHueOffset: 0,
|
|
9
|
+
selfShadowAreaSaturationBoost: 0.2,
|
|
10
|
+
selfShadowAreaValueMul: 0.68,
|
|
11
|
+
selfShadowTintColor: [1, 1, 1],
|
|
12
|
+
transitionAreaHueOffset: 0.01,
|
|
13
|
+
transitionAreaIntensity: 1,
|
|
14
|
+
transitionAreaSaturationBoost: 0.36,
|
|
15
|
+
transitionAreaTintColor: [1, 1, 1],
|
|
16
|
+
transitionAreaValueMul: 1,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const REFERENCE_SHADOW_COLOR_SETTINGS = Object.freeze({
|
|
20
|
+
...DEFAULT_SHADOW_COLOR_SETTINGS,
|
|
21
|
+
selfShadowAreaValueMul: 0.7,
|
|
22
|
+
transitionAreaSaturationBoost: 0.5,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const SHADOW_COLOR_PRESETS = Object.freeze({
|
|
26
|
+
baseline: DEFAULT_SHADOW_COLOR_SETTINGS,
|
|
27
|
+
reference: REFERENCE_SHADOW_COLOR_SETTINGS,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
function firstDefined(source, keys) {
|
|
31
|
+
for (const key of keys) {
|
|
32
|
+
if (source?.[key] !== undefined) return source[key];
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function numberOption(value, fallback, { min = -Infinity, max = Infinity } = {}) {
|
|
38
|
+
const nextValue = Number(value);
|
|
39
|
+
if (!Number.isFinite(nextValue)) return fallback;
|
|
40
|
+
return THREE.MathUtils.clamp(nextValue, min, max);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function colorOption(value, fallback) {
|
|
44
|
+
if (value?.isColor) return value.clone();
|
|
45
|
+
|
|
46
|
+
if (Array.isArray(value) && value.length >= 3) {
|
|
47
|
+
return new THREE.Color(
|
|
48
|
+
numberOption(value[0], fallback[0] ?? fallback.r ?? 1),
|
|
49
|
+
numberOption(value[1], fallback[1] ?? fallback.g ?? 1),
|
|
50
|
+
numberOption(value[2], fallback[2] ?? fallback.b ?? 1),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (typeof value === 'object' && value !== null) {
|
|
55
|
+
return new THREE.Color(
|
|
56
|
+
numberOption(value.r, fallback[0] ?? fallback.r ?? 1),
|
|
57
|
+
numberOption(value.g, fallback[1] ?? fallback.g ?? 1),
|
|
58
|
+
numberOption(value.b, fallback[2] ?? fallback.b ?? 1),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
63
|
+
try {
|
|
64
|
+
return new THREE.Color(value);
|
|
65
|
+
} catch {
|
|
66
|
+
// Fall through to the normalized fallback below.
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return Array.isArray(fallback)
|
|
71
|
+
? new THREE.Color(fallback[0], fallback[1], fallback[2])
|
|
72
|
+
: fallback.clone();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function vector4Option(value, fallback) {
|
|
76
|
+
if (value?.isVector4) return value.clone();
|
|
77
|
+
|
|
78
|
+
if (Array.isArray(value) && value.length >= 4) {
|
|
79
|
+
return new THREE.Vector4(
|
|
80
|
+
numberOption(value[0], fallback[0] ?? fallback.x ?? 0),
|
|
81
|
+
numberOption(value[1], fallback[1] ?? fallback.y ?? 0),
|
|
82
|
+
numberOption(value[2], fallback[2] ?? fallback.z ?? 0),
|
|
83
|
+
numberOption(value[3], fallback[3] ?? fallback.w ?? 0),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (typeof value === 'object' && value !== null) {
|
|
88
|
+
return new THREE.Vector4(
|
|
89
|
+
numberOption(value.x ?? value.r, fallback[0] ?? fallback.x ?? 0),
|
|
90
|
+
numberOption(value.y ?? value.g, fallback[1] ?? fallback.y ?? 0),
|
|
91
|
+
numberOption(value.z ?? value.b, fallback[2] ?? fallback.z ?? 0),
|
|
92
|
+
numberOption(value.w ?? value.a, fallback[3] ?? fallback.w ?? 0),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return Array.isArray(fallback)
|
|
97
|
+
? new THREE.Vector4(fallback[0], fallback[1], fallback[2], fallback[3])
|
|
98
|
+
: fallback.clone();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function resolvePresetDefaults(source) {
|
|
102
|
+
const presetName = String(firstDefined(source, ['preset', 'profile']) || 'baseline').toLowerCase();
|
|
103
|
+
return SHADOW_COLOR_PRESETS[presetName] || DEFAULT_SHADOW_COLOR_SETTINGS;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function createShadowColorSettings(options = null) {
|
|
107
|
+
const source = typeof options === 'string' ? { preset: options } : options || {};
|
|
108
|
+
const presetDefaults = resolvePresetDefaults(source);
|
|
109
|
+
const enabled = source.enabled !== false;
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
enabled,
|
|
113
|
+
lowSaturationFallbackColor: vector4Option(
|
|
114
|
+
firstDefined(source, [
|
|
115
|
+
'lowSaturationFallbackColor',
|
|
116
|
+
'lowSaturationFallback',
|
|
117
|
+
'fallbackColor',
|
|
118
|
+
]),
|
|
119
|
+
presetDefaults.lowSaturationFallbackColor,
|
|
120
|
+
),
|
|
121
|
+
selfShadowAlbedoMulStrength: enabled
|
|
122
|
+
? numberOption(
|
|
123
|
+
firstDefined(source, [
|
|
124
|
+
'selfShadowAlbedoMulStrength',
|
|
125
|
+
'shadowAlbedoMulStrength',
|
|
126
|
+
'albedoMulStrength',
|
|
127
|
+
]),
|
|
128
|
+
presetDefaults.selfShadowAlbedoMulStrength,
|
|
129
|
+
{ min: 0, max: 1 },
|
|
130
|
+
)
|
|
131
|
+
: presetDefaults.selfShadowAlbedoMulStrength,
|
|
132
|
+
selfShadowAreaHSVStrength: enabled
|
|
133
|
+
? numberOption(
|
|
134
|
+
firstDefined(source, [
|
|
135
|
+
'selfShadowAreaHSVStrength',
|
|
136
|
+
'shadowHSVStrength',
|
|
137
|
+
'hsvStrength',
|
|
138
|
+
]),
|
|
139
|
+
presetDefaults.selfShadowAreaHSVStrength,
|
|
140
|
+
{ min: 0, max: 1 },
|
|
141
|
+
)
|
|
142
|
+
: 0,
|
|
143
|
+
selfShadowAreaHueOffset: enabled
|
|
144
|
+
? numberOption(
|
|
145
|
+
firstDefined(source, [
|
|
146
|
+
'selfShadowAreaHueOffset',
|
|
147
|
+
'shadowHueOffset',
|
|
148
|
+
'hueOffset',
|
|
149
|
+
]),
|
|
150
|
+
presetDefaults.selfShadowAreaHueOffset,
|
|
151
|
+
{ min: -1, max: 1 },
|
|
152
|
+
)
|
|
153
|
+
: presetDefaults.selfShadowAreaHueOffset,
|
|
154
|
+
selfShadowAreaSaturationBoost: enabled
|
|
155
|
+
? numberOption(
|
|
156
|
+
firstDefined(source, [
|
|
157
|
+
'selfShadowAreaSaturationBoost',
|
|
158
|
+
'shadowSaturationBoost',
|
|
159
|
+
'saturationBoost',
|
|
160
|
+
]),
|
|
161
|
+
presetDefaults.selfShadowAreaSaturationBoost,
|
|
162
|
+
{ min: 0, max: 1 },
|
|
163
|
+
)
|
|
164
|
+
: 0,
|
|
165
|
+
selfShadowAreaValueMul: enabled
|
|
166
|
+
? numberOption(
|
|
167
|
+
firstDefined(source, [
|
|
168
|
+
'selfShadowAreaValueMul',
|
|
169
|
+
'shadowValueMul',
|
|
170
|
+
'valueMul',
|
|
171
|
+
]),
|
|
172
|
+
presetDefaults.selfShadowAreaValueMul,
|
|
173
|
+
{ min: 0, max: 1 },
|
|
174
|
+
)
|
|
175
|
+
: 1,
|
|
176
|
+
selfShadowTintColor: colorOption(
|
|
177
|
+
firstDefined(source, [
|
|
178
|
+
'selfShadowTintColor',
|
|
179
|
+
'shadowTintColor',
|
|
180
|
+
'shadowTint',
|
|
181
|
+
]),
|
|
182
|
+
presetDefaults.selfShadowTintColor,
|
|
183
|
+
),
|
|
184
|
+
transitionAreaHueOffset: enabled
|
|
185
|
+
? numberOption(
|
|
186
|
+
firstDefined(source, [
|
|
187
|
+
'transitionAreaHueOffset',
|
|
188
|
+
'litToShadowTransitionAreaHueOffset',
|
|
189
|
+
'transitionHueOffset',
|
|
190
|
+
]),
|
|
191
|
+
presetDefaults.transitionAreaHueOffset,
|
|
192
|
+
{ min: -1, max: 1 },
|
|
193
|
+
)
|
|
194
|
+
: presetDefaults.transitionAreaHueOffset,
|
|
195
|
+
transitionAreaIntensity: enabled
|
|
196
|
+
? numberOption(
|
|
197
|
+
firstDefined(source, [
|
|
198
|
+
'transitionAreaIntensity',
|
|
199
|
+
'litToShadowTransitionAreaIntensity',
|
|
200
|
+
'transitionIntensity',
|
|
201
|
+
]),
|
|
202
|
+
presetDefaults.transitionAreaIntensity,
|
|
203
|
+
{ min: 0, max: 32 },
|
|
204
|
+
)
|
|
205
|
+
: 0,
|
|
206
|
+
transitionAreaSaturationBoost: enabled
|
|
207
|
+
? numberOption(
|
|
208
|
+
firstDefined(source, [
|
|
209
|
+
'transitionAreaSaturationBoost',
|
|
210
|
+
'litToShadowTransitionAreaSaturationBoost',
|
|
211
|
+
'transitionSaturationBoost',
|
|
212
|
+
]),
|
|
213
|
+
presetDefaults.transitionAreaSaturationBoost,
|
|
214
|
+
{ min: 0, max: 1 },
|
|
215
|
+
)
|
|
216
|
+
: 0,
|
|
217
|
+
transitionAreaTintColor: colorOption(
|
|
218
|
+
firstDefined(source, [
|
|
219
|
+
'transitionAreaTintColor',
|
|
220
|
+
'litToShadowTransitionAreaTintColor',
|
|
221
|
+
'transitionTintColor',
|
|
222
|
+
'transitionTint',
|
|
223
|
+
]),
|
|
224
|
+
presetDefaults.transitionAreaTintColor,
|
|
225
|
+
),
|
|
226
|
+
transitionAreaValueMul: enabled
|
|
227
|
+
? numberOption(
|
|
228
|
+
firstDefined(source, [
|
|
229
|
+
'transitionAreaValueMul',
|
|
230
|
+
'litToShadowTransitionAreaValueMul',
|
|
231
|
+
'transitionValueMul',
|
|
232
|
+
]),
|
|
233
|
+
presetDefaults.transitionAreaValueMul,
|
|
234
|
+
{ min: 0, max: 1 },
|
|
235
|
+
)
|
|
236
|
+
: 1,
|
|
237
|
+
};
|
|
238
|
+
}
|