@headless-three/renderer 0.1.7 → 0.1.9
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/README.md +227 -52
- package/dist/attributes.d.ts +7 -5
- package/dist/attributes.d.ts.map +1 -1
- package/dist/attributes.js +112 -36
- package/dist/attributes.js.map +1 -1
- package/dist/camera.d.ts +1 -0
- package/dist/camera.d.ts.map +1 -1
- package/dist/camera.js +52 -13
- package/dist/camera.js.map +1 -1
- package/dist/clipping.d.ts +6 -0
- package/dist/clipping.d.ts.map +1 -0
- package/dist/clipping.js +84 -0
- package/dist/clipping.js.map +1 -0
- package/dist/color.d.ts +8 -3
- package/dist/color.d.ts.map +1 -1
- package/dist/color.js +136 -11
- package/dist/color.js.map +1 -1
- package/dist/index.d.ts +770 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6213 -46
- package/dist/index.js.map +1 -1
- package/dist/layers.d.ts +3 -0
- package/dist/layers.d.ts.map +1 -0
- package/dist/layers.js +33 -0
- package/dist/layers.js.map +1 -0
- package/dist/lights.d.ts +5 -4
- package/dist/lights.d.ts.map +1 -1
- package/dist/lights.js +473 -77
- package/dist/lights.js.map +1 -1
- package/dist/loaders.d.ts +92 -0
- package/dist/loaders.d.ts.map +1 -0
- package/dist/loaders.js +858 -0
- package/dist/loaders.js.map +1 -0
- package/dist/materials.d.ts +38 -6
- package/dist/materials.d.ts.map +1 -1
- package/dist/materials.js +4236 -162
- package/dist/materials.js.map +1 -1
- package/dist/math.js +2 -2
- package/dist/math.js.map +1 -1
- package/dist/morphs.d.ts.map +1 -1
- package/dist/morphs.js +50 -15
- package/dist/morphs.js.map +1 -1
- package/dist/objects.d.ts +4 -0
- package/dist/objects.d.ts.map +1 -0
- package/dist/objects.js +25 -0
- package/dist/objects.js.map +1 -0
- package/dist/scene.d.ts +279 -2
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +3818 -104
- package/dist/scene.js.map +1 -1
- package/dist/skinning.d.ts.map +1 -1
- package/dist/skinning.js +37 -18
- package/dist/skinning.js.map +1 -1
- package/dist/types.d.ts +1264 -12
- package/dist/types.d.ts.map +1 -1
- package/native.d.ts +767 -3
- package/native.js +54 -52
- package/package.json +20 -9
package/dist/lights.js
CHANGED
|
@@ -3,39 +3,39 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.extractLights = extractLights;
|
|
4
4
|
exports.extractAmbientLight = extractAmbientLight;
|
|
5
5
|
exports.extractAmbientIntensity = extractAmbientIntensity;
|
|
6
|
+
exports.extractLightProbe = extractLightProbe;
|
|
6
7
|
const color_1 = require("./color");
|
|
7
|
-
|
|
8
|
+
const layers_1 = require("./layers");
|
|
9
|
+
const math_1 = require("./math");
|
|
10
|
+
const objects_1 = require("./objects");
|
|
11
|
+
const MAX_NATIVE_LIGHTS = 64;
|
|
12
|
+
const MAX_SHADOW_CASCADES = 4;
|
|
13
|
+
function extractLights(scene, camera) {
|
|
8
14
|
const lights = [];
|
|
9
|
-
visitLights(scene, lights);
|
|
15
|
+
visitLights(scene, camera, lights);
|
|
16
|
+
assertSupportedLightCount(lights);
|
|
10
17
|
return lights.length > 0 ? lights : undefined;
|
|
11
18
|
}
|
|
12
|
-
function visitLights(object, lights) {
|
|
19
|
+
function visitLights(object, camera, lights) {
|
|
13
20
|
if (!object)
|
|
14
21
|
return;
|
|
15
|
-
if (object.visible === false)
|
|
22
|
+
if (optionalBoolean(object.visible, 'object.visible') === false)
|
|
16
23
|
return;
|
|
17
|
-
if (object.isLight === true) {
|
|
24
|
+
if (object.isLight === true && (0, layers_1.objectLayersMatchCamera)(object, camera)) {
|
|
18
25
|
const light = extractLight(object);
|
|
19
26
|
if (light)
|
|
20
27
|
lights.push(light);
|
|
21
28
|
}
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
visitLights(child, lights);
|
|
29
|
+
for (const child of (0, objects_1.objectChildren)(object)) {
|
|
30
|
+
visitLights(child, camera, lights);
|
|
25
31
|
}
|
|
26
32
|
}
|
|
27
33
|
function extractLight(light) {
|
|
28
|
-
const color = (0, color_1.
|
|
29
|
-
const intensity =
|
|
34
|
+
const color = (0, color_1.validatedColorLikeToArray)(light.color, 'light.color') ?? [1, 1, 1, 1];
|
|
35
|
+
const intensity = finiteNumberOrDefault(light.intensity, 'light.intensity', 1);
|
|
30
36
|
if (light.isDirectionalLight === true) {
|
|
31
|
-
const pos = light.matrixWorld
|
|
32
|
-
|
|
33
|
-
: [0, 10, 0];
|
|
34
|
-
let targetPos = [0, 0, 0];
|
|
35
|
-
if (light.target?.matrixWorld) {
|
|
36
|
-
const te = light.target.matrixWorld.elements;
|
|
37
|
-
targetPos = [te[12], te[13], te[14]];
|
|
38
|
-
}
|
|
37
|
+
const pos = positionFromMatrix(light.matrixWorld, 'DirectionalLight.matrixWorld', [0, 10, 0]);
|
|
38
|
+
const targetPos = positionFromMatrix(lightTargetMatrix(light, 'DirectionalLight.target'), 'DirectionalLight.target.matrixWorld', [0, 0, 0]);
|
|
39
39
|
const direction = [
|
|
40
40
|
targetPos[0] - pos[0],
|
|
41
41
|
targetPos[1] - pos[1],
|
|
@@ -54,56 +54,31 @@ function extractLight(light) {
|
|
|
54
54
|
position: pos,
|
|
55
55
|
direction,
|
|
56
56
|
};
|
|
57
|
-
if (light.castShadow === true) {
|
|
58
|
-
const shadow = light.shadow;
|
|
59
|
-
const mapSize = shadow?.mapSize;
|
|
60
|
-
const size = Math.max(32, Math.floor(mapSize?.x ?? mapSize?.width ?? 512));
|
|
61
|
-
const cam = shadow?.camera;
|
|
57
|
+
if (optionalBoolean(light.castShadow, 'light.castShadow') === true) {
|
|
62
58
|
out.castShadow = true;
|
|
63
|
-
out
|
|
64
|
-
if (Number.isFinite(shadow?.bias))
|
|
65
|
-
out.shadowBias = shadow.bias;
|
|
66
|
-
if (Number.isFinite(shadow?.normalBias))
|
|
67
|
-
out.shadowNormalBias = shadow.normalBias;
|
|
68
|
-
if (cam) {
|
|
69
|
-
if (Number.isFinite(cam.left))
|
|
70
|
-
out.shadowCameraLeft = cam.left;
|
|
71
|
-
if (Number.isFinite(cam.right))
|
|
72
|
-
out.shadowCameraRight = cam.right;
|
|
73
|
-
if (Number.isFinite(cam.top))
|
|
74
|
-
out.shadowCameraTop = cam.top;
|
|
75
|
-
if (Number.isFinite(cam.bottom))
|
|
76
|
-
out.shadowCameraBottom = cam.bottom;
|
|
77
|
-
if (Number.isFinite(cam.near))
|
|
78
|
-
out.shadowCameraNear = cam.near;
|
|
79
|
-
if (Number.isFinite(cam.far))
|
|
80
|
-
out.shadowCameraFar = cam.far;
|
|
81
|
-
}
|
|
59
|
+
applyShadowOptions(out, light);
|
|
82
60
|
}
|
|
83
61
|
return out;
|
|
84
62
|
}
|
|
85
63
|
if (light.isPointLight === true) {
|
|
86
|
-
const pos = light.matrixWorld
|
|
87
|
-
|
|
88
|
-
: [0, 0, 0];
|
|
89
|
-
return {
|
|
64
|
+
const pos = positionFromMatrix(light.matrixWorld, 'PointLight.matrixWorld', [0, 0, 0]);
|
|
65
|
+
const out = {
|
|
90
66
|
lightType: 'point',
|
|
91
67
|
color: [color[0], color[1], color[2]],
|
|
92
68
|
intensity,
|
|
93
69
|
position: pos,
|
|
94
|
-
distance:
|
|
95
|
-
decay:
|
|
70
|
+
distance: nonNegativeFiniteNumberOrDefault(light.distance, 'PointLight.distance', 0),
|
|
71
|
+
decay: nonNegativeFiniteNumberOrDefault(light.decay, 'PointLight.decay', 2),
|
|
96
72
|
};
|
|
73
|
+
if (optionalBoolean(light.castShadow, 'light.castShadow') === true) {
|
|
74
|
+
out.castShadow = true;
|
|
75
|
+
applyShadowOptions(out, light);
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
97
78
|
}
|
|
98
79
|
if (light.isSpotLight === true) {
|
|
99
|
-
const pos = light.matrixWorld
|
|
100
|
-
|
|
101
|
-
: [0, 0, 0];
|
|
102
|
-
let targetPos = [0, 0, 0];
|
|
103
|
-
if (light.target?.matrixWorld) {
|
|
104
|
-
const te = light.target.matrixWorld.elements;
|
|
105
|
-
targetPos = [te[12], te[13], te[14]];
|
|
106
|
-
}
|
|
80
|
+
const pos = positionFromMatrix(light.matrixWorld, 'SpotLight.matrixWorld', [0, 0, 0]);
|
|
81
|
+
const targetPos = positionFromMatrix(lightTargetMatrix(light, 'SpotLight.target'), 'SpotLight.target.matrixWorld', [0, 0, 0]);
|
|
107
82
|
const direction = [
|
|
108
83
|
targetPos[0] - pos[0],
|
|
109
84
|
targetPos[1] - pos[1],
|
|
@@ -115,24 +90,52 @@ function extractLight(light) {
|
|
|
115
90
|
direction[1] /= len;
|
|
116
91
|
direction[2] /= len;
|
|
117
92
|
}
|
|
118
|
-
|
|
93
|
+
const out = {
|
|
119
94
|
lightType: 'spot',
|
|
120
95
|
color: [color[0], color[1], color[2]],
|
|
121
96
|
intensity,
|
|
122
97
|
position: pos,
|
|
123
98
|
direction,
|
|
124
|
-
distance:
|
|
125
|
-
decay:
|
|
126
|
-
angle:
|
|
127
|
-
penumbra:
|
|
99
|
+
distance: nonNegativeFiniteNumberOrDefault(light.distance, 'SpotLight.distance', 0),
|
|
100
|
+
decay: nonNegativeFiniteNumberOrDefault(light.decay, 'SpotLight.decay', 2),
|
|
101
|
+
angle: spotAngleOrDefault(light.angle),
|
|
102
|
+
penumbra: normalizedFiniteNumberOrDefault(light.penumbra, 'SpotLight.penumbra', 0),
|
|
103
|
+
};
|
|
104
|
+
if (optionalBoolean(light.castShadow, 'light.castShadow') === true) {
|
|
105
|
+
out.castShadow = true;
|
|
106
|
+
applyShadowOptions(out, light);
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
if (light.isRectAreaLight === true) {
|
|
111
|
+
const matrix = matrixElementsOrUndefined(light.matrixWorld, 'RectAreaLight.matrixWorld');
|
|
112
|
+
const pos = matrix ? [matrix[12], matrix[13], matrix[14]] : [0, 0, 0];
|
|
113
|
+
let direction = [0, 0, -1];
|
|
114
|
+
if (matrix) {
|
|
115
|
+
direction = [-matrix[8], -matrix[9], -matrix[10]];
|
|
116
|
+
}
|
|
117
|
+
const len = Math.sqrt(direction[0] ** 2 + direction[1] ** 2 + direction[2] ** 2);
|
|
118
|
+
if (len > 0) {
|
|
119
|
+
direction[0] /= len;
|
|
120
|
+
direction[1] /= len;
|
|
121
|
+
direction[2] /= len;
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
lightType: 'rectArea',
|
|
125
|
+
color: [color[0], color[1], color[2]],
|
|
126
|
+
intensity,
|
|
127
|
+
position: pos,
|
|
128
|
+
direction,
|
|
129
|
+
width: positiveFiniteNumberOrDefault(light.width, 'RectAreaLight.width', 10),
|
|
130
|
+
height: positiveFiniteNumberOrDefault(light.height, 'RectAreaLight.height', 10),
|
|
128
131
|
};
|
|
129
132
|
}
|
|
130
133
|
if (light.isHemisphereLight === true) {
|
|
131
|
-
const groundColor = (0, color_1.
|
|
134
|
+
const groundColor = (0, color_1.validatedColorLikeToArray)(light.groundColor, 'HemisphereLight.groundColor') ?? [0.04, 0.02, 0.0, 1];
|
|
132
135
|
let direction = [0, 1, 0];
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const ux =
|
|
136
|
+
const matrix = matrixElementsOrUndefined(light.matrixWorld, 'HemisphereLight.matrixWorld');
|
|
137
|
+
if (matrix) {
|
|
138
|
+
const ux = matrix[4], uy = matrix[5], uz = matrix[6];
|
|
136
139
|
const ulen = Math.sqrt(ux * ux + uy * uy + uz * uz);
|
|
137
140
|
if (ulen > 0) {
|
|
138
141
|
direction = [ux / ulen, uy / ulen, uz / ulen];
|
|
@@ -149,10 +152,332 @@ function extractLight(light) {
|
|
|
149
152
|
// AmbientLight is handled separately
|
|
150
153
|
return null;
|
|
151
154
|
}
|
|
152
|
-
function
|
|
155
|
+
function positionFromMatrix(matrix, label, fallback) {
|
|
156
|
+
const elements = matrixElementsOrUndefined(matrix, label);
|
|
157
|
+
return elements ? [elements[12], elements[13], elements[14]] : fallback;
|
|
158
|
+
}
|
|
159
|
+
function matrixElementsOrUndefined(matrix, label) {
|
|
160
|
+
return matrix ? (0, math_1.matrixElements)(matrix, label) : undefined;
|
|
161
|
+
}
|
|
162
|
+
function lightTargetMatrix(light, label) {
|
|
163
|
+
const target = light.target;
|
|
164
|
+
if (target == null)
|
|
165
|
+
return undefined;
|
|
166
|
+
if (typeof target !== 'object' || Array.isArray(target)) {
|
|
167
|
+
throw new TypeError(`${label} must be an object.`);
|
|
168
|
+
}
|
|
169
|
+
return target.matrixWorld;
|
|
170
|
+
}
|
|
171
|
+
function applyShadowOptions(out, light) {
|
|
172
|
+
const shadow = light.shadow;
|
|
173
|
+
assertShadowContainerLike(shadow);
|
|
174
|
+
const mapSize = shadowMapSizeOrDefault(shadow?.mapSize, light);
|
|
175
|
+
out.shadowMapSize = Math.max(mapSize.width, mapSize.height);
|
|
176
|
+
out.shadowMapWidth = mapSize.width;
|
|
177
|
+
out.shadowMapHeight = mapSize.height;
|
|
178
|
+
optionalBoolean(shadow?.autoUpdate, 'light.shadow.autoUpdate');
|
|
179
|
+
optionalBoolean(shadow?.needsUpdate, 'light.shadow.needsUpdate');
|
|
180
|
+
validateShadowObjectState(shadow?.map, 'light.shadow.map');
|
|
181
|
+
validateShadowObjectState(shadow?.mapPass, 'light.shadow.mapPass');
|
|
182
|
+
if (shadow?.matrix != null)
|
|
183
|
+
(0, math_1.matrixElements)(shadow.matrix, 'light.shadow.matrix');
|
|
184
|
+
const bias = optionalFiniteNumber(shadow?.bias, 'light.shadow.bias');
|
|
185
|
+
const normalBias = optionalFiniteNumber(shadow?.normalBias, 'light.shadow.normalBias');
|
|
186
|
+
const radius = optionalNonNegativeFiniteNumber(shadow?.radius, 'light.shadow.radius');
|
|
187
|
+
const intensity = optionalNonNegativeFiniteNumber(shadow?.intensity, 'light.shadow.intensity');
|
|
188
|
+
const blurSamples = optionalNonNegativeFiniteNumber(shadow?.blurSamples, 'light.shadow.blurSamples');
|
|
189
|
+
if (bias !== undefined)
|
|
190
|
+
out.shadowBias = bias;
|
|
191
|
+
if (normalBias !== undefined)
|
|
192
|
+
out.shadowNormalBias = normalBias;
|
|
193
|
+
if (radius !== undefined)
|
|
194
|
+
out.shadowRadius = radius;
|
|
195
|
+
if (intensity !== undefined)
|
|
196
|
+
out.shadowIntensity = intensity;
|
|
197
|
+
if (blurSamples !== undefined)
|
|
198
|
+
out.shadowBlurSamples = blurSamples;
|
|
199
|
+
const cam = shadow?.camera;
|
|
200
|
+
if (cam != null) {
|
|
201
|
+
assertPlainObject(cam, 'light.shadow.camera');
|
|
202
|
+
const bounds = shadowCameraOrthoBounds(cam, out);
|
|
203
|
+
const clipDistances = shadowCameraClipDistances(cam, out);
|
|
204
|
+
if (bounds.shadowCameraLeft !== undefined)
|
|
205
|
+
out.shadowCameraLeft = bounds.shadowCameraLeft;
|
|
206
|
+
if (bounds.shadowCameraRight !== undefined)
|
|
207
|
+
out.shadowCameraRight = bounds.shadowCameraRight;
|
|
208
|
+
if (bounds.shadowCameraTop !== undefined)
|
|
209
|
+
out.shadowCameraTop = bounds.shadowCameraTop;
|
|
210
|
+
if (bounds.shadowCameraBottom !== undefined)
|
|
211
|
+
out.shadowCameraBottom = bounds.shadowCameraBottom;
|
|
212
|
+
if (clipDistances.shadowCameraNear !== undefined)
|
|
213
|
+
out.shadowCameraNear = clipDistances.shadowCameraNear;
|
|
214
|
+
if (clipDistances.shadowCameraFar !== undefined)
|
|
215
|
+
out.shadowCameraFar = clipDistances.shadowCameraFar;
|
|
216
|
+
}
|
|
217
|
+
applyShadowCascadeOptions(out, light);
|
|
218
|
+
}
|
|
219
|
+
function shadowCameraClipDistances(camera, light) {
|
|
220
|
+
const near = optionalFiniteNumber(camera.near, 'light.shadow.camera.near');
|
|
221
|
+
const far = optionalFiniteNumber(camera.far, 'light.shadow.camera.far');
|
|
222
|
+
if (near !== undefined && near < 0) {
|
|
223
|
+
throw new TypeError('light.shadow.camera.near must be non-negative.');
|
|
224
|
+
}
|
|
225
|
+
if (near === 0 && (light.lightType === 'point' || light.lightType === 'spot')) {
|
|
226
|
+
throw new TypeError('light.shadow.camera.near must be positive for point and spot shadows.');
|
|
227
|
+
}
|
|
228
|
+
if (far !== undefined && far <= 0) {
|
|
229
|
+
throw new TypeError('light.shadow.camera.far must be positive.');
|
|
230
|
+
}
|
|
231
|
+
const effectiveNear = near ?? 0.5;
|
|
232
|
+
const effectiveFar = far ?? defaultShadowCameraFar(light);
|
|
233
|
+
if (effectiveFar <= effectiveNear) {
|
|
234
|
+
if (far !== undefined) {
|
|
235
|
+
throw new TypeError('light.shadow.camera.far must be greater than light.shadow.camera.near.');
|
|
236
|
+
}
|
|
237
|
+
throw new TypeError('light.shadow.camera.near must be less than the effective light.shadow.camera.far.');
|
|
238
|
+
}
|
|
239
|
+
return { shadowCameraNear: near, shadowCameraFar: far };
|
|
240
|
+
}
|
|
241
|
+
function defaultShadowCameraFar(light) {
|
|
242
|
+
return light.lightType === 'point' && light.distance !== undefined && light.distance > 0
|
|
243
|
+
? light.distance
|
|
244
|
+
: 500;
|
|
245
|
+
}
|
|
246
|
+
function shadowCameraOrthoBounds(camera, light) {
|
|
247
|
+
const left = optionalFiniteNumber(camera.left, 'light.shadow.camera.left');
|
|
248
|
+
const right = optionalFiniteNumber(camera.right, 'light.shadow.camera.right');
|
|
249
|
+
const top = optionalFiniteNumber(camera.top, 'light.shadow.camera.top');
|
|
250
|
+
const bottom = optionalFiniteNumber(camera.bottom, 'light.shadow.camera.bottom');
|
|
251
|
+
if (light.lightType === 'directional') {
|
|
252
|
+
const effectiveLeft = left ?? -5;
|
|
253
|
+
const effectiveRight = right ?? 5;
|
|
254
|
+
const effectiveTop = top ?? 5;
|
|
255
|
+
const effectiveBottom = bottom ?? -5;
|
|
256
|
+
if (effectiveRight <= effectiveLeft) {
|
|
257
|
+
if (right !== undefined) {
|
|
258
|
+
throw new TypeError('light.shadow.camera.right must be greater than light.shadow.camera.left.');
|
|
259
|
+
}
|
|
260
|
+
throw new TypeError('light.shadow.camera.left must be less than the effective light.shadow.camera.right.');
|
|
261
|
+
}
|
|
262
|
+
if (effectiveTop <= effectiveBottom) {
|
|
263
|
+
if (top !== undefined) {
|
|
264
|
+
throw new TypeError('light.shadow.camera.top must be greater than light.shadow.camera.bottom.');
|
|
265
|
+
}
|
|
266
|
+
throw new TypeError('light.shadow.camera.bottom must be less than the effective light.shadow.camera.top.');
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
shadowCameraLeft: left,
|
|
271
|
+
shadowCameraRight: right,
|
|
272
|
+
shadowCameraTop: top,
|
|
273
|
+
shadowCameraBottom: bottom,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function shadowMapSizeOrDefault(mapSize, light) {
|
|
277
|
+
if (mapSize != null) {
|
|
278
|
+
assertPlainObject(mapSize, 'light.shadow.mapSize');
|
|
279
|
+
}
|
|
280
|
+
const width = shadowMapSizeComponent(mapSize, 'x', 'width');
|
|
281
|
+
const height = shadowMapSizeComponent(mapSize, 'y', 'height');
|
|
282
|
+
const resolvedWidth = Math.max(32, Math.floor(width ?? height ?? 512));
|
|
283
|
+
const resolvedHeight = Math.max(32, Math.floor(height ?? width ?? 512));
|
|
284
|
+
if (light.isPointLight === true && resolvedWidth !== resolvedHeight) {
|
|
285
|
+
throw new Error(`Non-square PointLight shadow map sizes are not supported by @headless-three/renderer yet (${resolvedWidth}x${resolvedHeight}). Use square point-light shadow maps until rectangular cube-face shadows are supported.`);
|
|
286
|
+
}
|
|
287
|
+
return { width: resolvedWidth, height: resolvedHeight };
|
|
288
|
+
}
|
|
289
|
+
function assertShadowContainerLike(shadow) {
|
|
290
|
+
if (shadow == null)
|
|
291
|
+
return;
|
|
292
|
+
assertPlainObject(shadow, 'light.shadow');
|
|
293
|
+
}
|
|
294
|
+
function validateShadowObjectState(value, label) {
|
|
295
|
+
if (value == null)
|
|
296
|
+
return;
|
|
297
|
+
assertPlainObject(value, label);
|
|
298
|
+
}
|
|
299
|
+
function assertPlainObject(value, label) {
|
|
300
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value))
|
|
301
|
+
return;
|
|
302
|
+
throw new TypeError(`${label} must be an object.`);
|
|
303
|
+
}
|
|
304
|
+
function shadowMapSizeComponent(mapSize, vectorKey, dimensionKey) {
|
|
305
|
+
if (!mapSize)
|
|
306
|
+
return undefined;
|
|
307
|
+
const vectorValue = mapSize[vectorKey];
|
|
308
|
+
if (vectorValue != null)
|
|
309
|
+
return optionalPositiveFiniteNumber(vectorValue, `light.shadow.mapSize.${vectorKey}`);
|
|
310
|
+
return optionalPositiveFiniteNumber(mapSize[dimensionKey], `light.shadow.mapSize.${dimensionKey}`);
|
|
311
|
+
}
|
|
312
|
+
function applyShadowCascadeOptions(out, light) {
|
|
313
|
+
const cascadeHints = shadowCascadeHints(light);
|
|
314
|
+
if (!cascadeHints)
|
|
315
|
+
return;
|
|
316
|
+
const { value: cascades, label } = cascadeHints;
|
|
317
|
+
if (!Array.isArray(cascades)) {
|
|
318
|
+
throw new TypeError(`${label} must be an array of shadow cascade hint objects.`);
|
|
319
|
+
}
|
|
320
|
+
if (cascades.length < 2)
|
|
321
|
+
return;
|
|
322
|
+
const splits = [];
|
|
323
|
+
const bounds = [];
|
|
324
|
+
for (let i = 0; i < cascades.length; i += 1) {
|
|
325
|
+
const rawCascade = cascades[i];
|
|
326
|
+
if (!rawCascade || typeof rawCascade !== 'object') {
|
|
327
|
+
throw new TypeError(`shadowCascades[${i}] must be an object with finite left, right, top, bottom, near, and far values.`);
|
|
328
|
+
}
|
|
329
|
+
const cascade = rawCascade;
|
|
330
|
+
const label = `shadowCascades[${i}]`;
|
|
331
|
+
const left = requiredFiniteNumber(cascade.left, `${label}.left`);
|
|
332
|
+
const right = requiredFiniteNumber(cascade.right, `${label}.right`);
|
|
333
|
+
const top = requiredFiniteNumber(cascade.top, `${label}.top`);
|
|
334
|
+
const bottom = requiredFiniteNumber(cascade.bottom, `${label}.bottom`);
|
|
335
|
+
const near = requiredFiniteNumber(cascade.near, `${label}.near`);
|
|
336
|
+
const far = requiredFiniteNumber(cascade.far, `${label}.far`);
|
|
337
|
+
if (bounds.length / 6 >= MAX_SHADOW_CASCADES) {
|
|
338
|
+
throw new Error(`Directional shadow cascade hints support at most ${MAX_SHADOW_CASCADES} valid cascades in @headless-three/renderer. Reduce light.userData.headlessThreeRenderer.shadowCascades or render separate shadow passes.`);
|
|
339
|
+
}
|
|
340
|
+
bounds.push(left, right, top, bottom, near, far);
|
|
341
|
+
const split = shadowCascadeSplit(cascade, label);
|
|
342
|
+
if (split !== undefined)
|
|
343
|
+
splits.push(split);
|
|
344
|
+
}
|
|
345
|
+
const count = bounds.length / 6;
|
|
346
|
+
if (count >= 2) {
|
|
347
|
+
out.shadowCascadeBounds = bounds;
|
|
348
|
+
out.shadowCascadeSplits = splits.slice(0, count - 1);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
function shadowCascadeHints(light) {
|
|
352
|
+
const userData = light.userData;
|
|
353
|
+
if (userData != null && (typeof userData !== 'object' || Array.isArray(userData))) {
|
|
354
|
+
throw new TypeError('light.userData must be an object.');
|
|
355
|
+
}
|
|
356
|
+
const modernHints = userData?.headlessThreeRenderer;
|
|
357
|
+
if (modernHints != null) {
|
|
358
|
+
assertPlainObject(modernHints, 'light.userData.headlessThreeRenderer');
|
|
359
|
+
if (modernHints.shadowCascades != null) {
|
|
360
|
+
return {
|
|
361
|
+
value: modernHints.shadowCascades,
|
|
362
|
+
label: 'light.userData.headlessThreeRenderer.shadowCascades',
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
if (modernHints.cascades != null) {
|
|
366
|
+
return {
|
|
367
|
+
value: modernHints.cascades,
|
|
368
|
+
label: 'light.userData.headlessThreeRenderer.cascades',
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
const legacyHints = userData?.headlessRenderer;
|
|
373
|
+
if (legacyHints != null) {
|
|
374
|
+
assertPlainObject(legacyHints, 'light.userData.headlessRenderer');
|
|
375
|
+
if (legacyHints.shadowCascades != null) {
|
|
376
|
+
return {
|
|
377
|
+
value: legacyHints.shadowCascades,
|
|
378
|
+
label: 'light.userData.headlessRenderer.shadowCascades',
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
if (legacyHints.cascades != null) {
|
|
382
|
+
return {
|
|
383
|
+
value: legacyHints.cascades,
|
|
384
|
+
label: 'light.userData.headlessRenderer.cascades',
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
const shadowCascades = light.shadow?.cascades;
|
|
389
|
+
return shadowCascades != null
|
|
390
|
+
? { value: shadowCascades, label: 'light.shadow.cascades' }
|
|
391
|
+
: null;
|
|
392
|
+
}
|
|
393
|
+
function shadowCascadeSplit(cascade, label) {
|
|
394
|
+
if (cascade.split != null)
|
|
395
|
+
return optionalFiniteNumber(cascade.split, `${label}.split`);
|
|
396
|
+
if (cascade.distance != null)
|
|
397
|
+
return optionalFiniteNumber(cascade.distance, `${label}.distance`);
|
|
398
|
+
return optionalFiniteNumber(cascade.farDistance, `${label}.farDistance`);
|
|
399
|
+
}
|
|
400
|
+
function finiteNumberOrDefault(value, label, fallback) {
|
|
401
|
+
if (value == null)
|
|
402
|
+
return fallback;
|
|
403
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
404
|
+
return value;
|
|
405
|
+
throw new TypeError(`${label} must be a finite number.`);
|
|
406
|
+
}
|
|
407
|
+
function nonNegativeFiniteNumberOrDefault(value, label, fallback) {
|
|
408
|
+
const number = finiteNumberOrDefault(value, label, fallback);
|
|
409
|
+
if (number < 0) {
|
|
410
|
+
throw new TypeError(`${label} must be non-negative.`);
|
|
411
|
+
}
|
|
412
|
+
return number;
|
|
413
|
+
}
|
|
414
|
+
function positiveFiniteNumberOrDefault(value, label, fallback) {
|
|
415
|
+
const number = finiteNumberOrDefault(value, label, fallback);
|
|
416
|
+
if (number <= 0) {
|
|
417
|
+
throw new TypeError(`${label} must be positive.`);
|
|
418
|
+
}
|
|
419
|
+
return number;
|
|
420
|
+
}
|
|
421
|
+
function normalizedFiniteNumberOrDefault(value, label, fallback) {
|
|
422
|
+
const number = finiteNumberOrDefault(value, label, fallback);
|
|
423
|
+
if (number < 0 || number > 1) {
|
|
424
|
+
throw new TypeError(`${label} must be between 0 and 1.`);
|
|
425
|
+
}
|
|
426
|
+
return number;
|
|
427
|
+
}
|
|
428
|
+
function spotAngleOrDefault(value) {
|
|
429
|
+
const angle = finiteNumberOrDefault(value, 'SpotLight.angle', Math.PI / 3);
|
|
430
|
+
if (angle < 0 || angle > Math.PI / 2) {
|
|
431
|
+
throw new TypeError('SpotLight.angle must be between 0 and Math.PI / 2.');
|
|
432
|
+
}
|
|
433
|
+
return angle;
|
|
434
|
+
}
|
|
435
|
+
function optionalFiniteNumber(value, label) {
|
|
436
|
+
if (value == null)
|
|
437
|
+
return undefined;
|
|
438
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
439
|
+
return value;
|
|
440
|
+
throw new TypeError(`${label} must be a finite number.`);
|
|
441
|
+
}
|
|
442
|
+
function optionalNonNegativeFiniteNumber(value, label) {
|
|
443
|
+
const number = optionalFiniteNumber(value, label);
|
|
444
|
+
if (number === undefined)
|
|
445
|
+
return undefined;
|
|
446
|
+
if (number < 0) {
|
|
447
|
+
throw new TypeError(`${label} must be non-negative.`);
|
|
448
|
+
}
|
|
449
|
+
return number;
|
|
450
|
+
}
|
|
451
|
+
function optionalBoolean(value, label) {
|
|
452
|
+
if (value == null)
|
|
453
|
+
return undefined;
|
|
454
|
+
if (typeof value === 'boolean')
|
|
455
|
+
return value;
|
|
456
|
+
throw new TypeError(`${label} must be a boolean.`);
|
|
457
|
+
}
|
|
458
|
+
function optionalPositiveFiniteNumber(value, label) {
|
|
459
|
+
const number = optionalFiniteNumber(value, label);
|
|
460
|
+
if (number === undefined)
|
|
461
|
+
return undefined;
|
|
462
|
+
if (number <= 0) {
|
|
463
|
+
throw new TypeError(`${label} must be positive.`);
|
|
464
|
+
}
|
|
465
|
+
return number;
|
|
466
|
+
}
|
|
467
|
+
function requiredFiniteNumber(value, label) {
|
|
468
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
469
|
+
return value;
|
|
470
|
+
throw new TypeError(`${label} must be a finite number.`);
|
|
471
|
+
}
|
|
472
|
+
function assertSupportedLightCount(lights) {
|
|
473
|
+
if (lights.length > MAX_NATIVE_LIGHTS) {
|
|
474
|
+
throw new Error(`More than ${MAX_NATIVE_LIGHTS} visible non-ambient lights are not supported by @headless-three/renderer yet (${lights.length} found). Keep the closest/brightest ${MAX_NATIVE_LIGHTS} lights, bake lighting, or render separate passes until native light arrays are expanded.`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
function extractAmbientLight(scene, camera) {
|
|
153
478
|
let color = null;
|
|
154
|
-
visitForAmbient(scene, (light) => {
|
|
155
|
-
const c = (0, color_1.
|
|
479
|
+
visitForAmbient(scene, camera, (light) => {
|
|
480
|
+
const c = (0, color_1.validatedColorLikeToArray)(light.color, 'AmbientLight.color') ?? [1, 1, 1, 1];
|
|
156
481
|
if (!color) {
|
|
157
482
|
color = [c[0], c[1], c[2]];
|
|
158
483
|
}
|
|
@@ -164,23 +489,94 @@ function extractAmbientLight(scene) {
|
|
|
164
489
|
});
|
|
165
490
|
return color;
|
|
166
491
|
}
|
|
167
|
-
function extractAmbientIntensity(scene) {
|
|
492
|
+
function extractAmbientIntensity(scene, camera) {
|
|
168
493
|
let intensity = 0;
|
|
169
|
-
visitForAmbient(scene, (light) => {
|
|
170
|
-
intensity +=
|
|
494
|
+
visitForAmbient(scene, camera, (light) => {
|
|
495
|
+
intensity += finiteNumberOrDefault(light.intensity, 'AmbientLight.intensity', 1);
|
|
171
496
|
});
|
|
172
497
|
return intensity > 0 ? intensity : undefined;
|
|
173
498
|
}
|
|
174
|
-
function
|
|
499
|
+
function extractLightProbe(scene, camera) {
|
|
500
|
+
const coefficients = new Array(27).fill(0);
|
|
501
|
+
let found = false;
|
|
502
|
+
visitForLightProbe(scene, camera, (light) => {
|
|
503
|
+
const source = light.sh?.coefficients;
|
|
504
|
+
if (source == null)
|
|
505
|
+
return;
|
|
506
|
+
if (!Array.isArray(source)) {
|
|
507
|
+
throw new TypeError('LightProbe.sh.coefficients must be an array of 9 coefficients.');
|
|
508
|
+
}
|
|
509
|
+
if (source.length < 9) {
|
|
510
|
+
throw new TypeError('LightProbe.sh.coefficients must contain 9 coefficients.');
|
|
511
|
+
}
|
|
512
|
+
const intensity = finiteNumberOrDefault(light.intensity, 'LightProbe.intensity', 1);
|
|
513
|
+
for (let i = 0; i < 9; i += 1) {
|
|
514
|
+
const coefficient = coefficientToRgb(source[i], `LightProbe.sh.coefficients[${i}]`);
|
|
515
|
+
if (!coefficient)
|
|
516
|
+
continue;
|
|
517
|
+
coefficients[i * 3] += coefficient[0] * intensity;
|
|
518
|
+
coefficients[i * 3 + 1] += coefficient[1] * intensity;
|
|
519
|
+
coefficients[i * 3 + 2] += coefficient[2] * intensity;
|
|
520
|
+
}
|
|
521
|
+
found = true;
|
|
522
|
+
});
|
|
523
|
+
return found ? coefficients : null;
|
|
524
|
+
}
|
|
525
|
+
function visitForLightProbe(object, camera, callback) {
|
|
526
|
+
if (!object)
|
|
527
|
+
return;
|
|
528
|
+
if (optionalBoolean(object.visible, 'object.visible') === false)
|
|
529
|
+
return;
|
|
530
|
+
if (object.isLightProbe === true && (0, layers_1.objectLayersMatchCamera)(object, camera))
|
|
531
|
+
callback(object);
|
|
532
|
+
for (const child of (0, objects_1.objectChildren)(object)) {
|
|
533
|
+
visitForLightProbe(child, camera, callback);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
function coefficientToRgb(value, label) {
|
|
537
|
+
if (!value)
|
|
538
|
+
return null;
|
|
539
|
+
const maybeArrayLike = value;
|
|
540
|
+
if (Array.isArray(value) || (ArrayBuffer.isView(value) && typeof maybeArrayLike.length === 'number')) {
|
|
541
|
+
const array = value;
|
|
542
|
+
return [
|
|
543
|
+
requiredFiniteRgbComponent(array[0], `${label}[0]`),
|
|
544
|
+
requiredFiniteRgbComponent(array[1], `${label}[1]`),
|
|
545
|
+
requiredFiniteRgbComponent(array[2], `${label}[2]`),
|
|
546
|
+
];
|
|
547
|
+
}
|
|
548
|
+
const v = value;
|
|
549
|
+
if ('r' in v || 'g' in v || 'b' in v) {
|
|
550
|
+
return [
|
|
551
|
+
requiredFiniteRgbComponent(v.r, `${label}.r`),
|
|
552
|
+
requiredFiniteRgbComponent(v.g, `${label}.g`),
|
|
553
|
+
requiredFiniteRgbComponent(v.b, `${label}.b`),
|
|
554
|
+
];
|
|
555
|
+
}
|
|
556
|
+
if ('x' in v || 'y' in v || 'z' in v) {
|
|
557
|
+
return [
|
|
558
|
+
requiredFiniteRgbComponent(v.x, `${label}.x`),
|
|
559
|
+
requiredFiniteRgbComponent(v.y, `${label}.y`),
|
|
560
|
+
requiredFiniteRgbComponent(v.z, `${label}.z`),
|
|
561
|
+
];
|
|
562
|
+
}
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
function requiredFiniteRgbComponent(value, label) {
|
|
566
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
567
|
+
throw new TypeError(`${label} must be a finite number.`);
|
|
568
|
+
}
|
|
569
|
+
return value;
|
|
570
|
+
}
|
|
571
|
+
function visitForAmbient(object, camera, callback) {
|
|
175
572
|
if (!object)
|
|
176
573
|
return;
|
|
177
|
-
if (object.visible === false)
|
|
574
|
+
if (optionalBoolean(object.visible, 'object.visible') === false)
|
|
178
575
|
return;
|
|
179
|
-
if (object.isAmbientLight === true)
|
|
576
|
+
if (object.isAmbientLight === true && (0, layers_1.objectLayersMatchCamera)(object, camera))
|
|
180
577
|
callback(object);
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
visitForAmbient(child, callback);
|
|
578
|
+
for (const child of (0, objects_1.objectChildren)(object)) {
|
|
579
|
+
visitForAmbient(child, camera, callback);
|
|
184
580
|
}
|
|
185
581
|
}
|
|
186
582
|
//# sourceMappingURL=lights.js.map
|