@headless-three/renderer 0.1.7 → 0.1.8

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 (58) hide show
  1. package/README.md +227 -52
  2. package/dist/attributes.d.ts +7 -5
  3. package/dist/attributes.d.ts.map +1 -1
  4. package/dist/attributes.js +112 -36
  5. package/dist/attributes.js.map +1 -1
  6. package/dist/camera.d.ts +1 -0
  7. package/dist/camera.d.ts.map +1 -1
  8. package/dist/camera.js +52 -13
  9. package/dist/camera.js.map +1 -1
  10. package/dist/clipping.d.ts +6 -0
  11. package/dist/clipping.d.ts.map +1 -0
  12. package/dist/clipping.js +84 -0
  13. package/dist/clipping.js.map +1 -0
  14. package/dist/color.d.ts +8 -3
  15. package/dist/color.d.ts.map +1 -1
  16. package/dist/color.js +136 -11
  17. package/dist/color.js.map +1 -1
  18. package/dist/index.d.ts +770 -5
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +6208 -46
  21. package/dist/index.js.map +1 -1
  22. package/dist/layers.d.ts +3 -0
  23. package/dist/layers.d.ts.map +1 -0
  24. package/dist/layers.js +33 -0
  25. package/dist/layers.js.map +1 -0
  26. package/dist/lights.d.ts +5 -4
  27. package/dist/lights.d.ts.map +1 -1
  28. package/dist/lights.js +473 -77
  29. package/dist/lights.js.map +1 -1
  30. package/dist/loaders.d.ts +92 -0
  31. package/dist/loaders.d.ts.map +1 -0
  32. package/dist/loaders.js +858 -0
  33. package/dist/loaders.js.map +1 -0
  34. package/dist/materials.d.ts +38 -6
  35. package/dist/materials.d.ts.map +1 -1
  36. package/dist/materials.js +4236 -162
  37. package/dist/materials.js.map +1 -1
  38. package/dist/math.js +2 -2
  39. package/dist/math.js.map +1 -1
  40. package/dist/morphs.d.ts.map +1 -1
  41. package/dist/morphs.js +50 -15
  42. package/dist/morphs.js.map +1 -1
  43. package/dist/objects.d.ts +4 -0
  44. package/dist/objects.d.ts.map +1 -0
  45. package/dist/objects.js +25 -0
  46. package/dist/objects.js.map +1 -0
  47. package/dist/scene.d.ts +251 -2
  48. package/dist/scene.d.ts.map +1 -1
  49. package/dist/scene.js +3679 -105
  50. package/dist/scene.js.map +1 -1
  51. package/dist/skinning.d.ts.map +1 -1
  52. package/dist/skinning.js +37 -18
  53. package/dist/skinning.js.map +1 -1
  54. package/dist/types.d.ts +1261 -12
  55. package/dist/types.d.ts.map +1 -1
  56. package/native.d.ts +761 -3
  57. package/native.js +54 -52
  58. package/package.json +20 -9
package/dist/scene.js CHANGED
@@ -1,46 +1,135 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSceneExtractionCache = createSceneExtractionCache;
3
4
  exports.flattenScene = flattenScene;
4
5
  const math_1 = require("./math");
5
6
  const attributes_1 = require("./attributes");
6
7
  const materials_1 = require("./materials");
7
8
  const skinning_1 = require("./skinning");
8
9
  const morphs_1 = require("./morphs");
9
- function flattenScene(scene) {
10
+ const layers_1 = require("./layers");
11
+ const objects_1 = require("./objects");
12
+ const clipping_1 = require("./clipping");
13
+ const SupportedInstancedBufferGeometryAttributes = new Set([
14
+ 'color',
15
+ 'normal',
16
+ 'uv',
17
+ 'uv1',
18
+ 'uv2',
19
+ 'uv3',
20
+ 'instanceOffset',
21
+ 'instancePosition',
22
+ 'instanceScale',
23
+ 'offset',
24
+ 'scale',
25
+ 'translate',
26
+ 'translation',
27
+ ]);
28
+ const NativeRenderOrderLimit = 1000000000;
29
+ const MAX_POINT_SPRITE_SIZE = 64;
30
+ function createSceneExtractionCache() {
31
+ return {
32
+ meshGeometry: new WeakMap(),
33
+ instancedMeshes: new WeakMap(),
34
+ instancedPositionExpansions: new WeakMap(),
35
+ instancedNormalExpansions: new WeakMap(),
36
+ instancedUvExpansions: new WeakMap(),
37
+ instancedColorExpansions: new WeakMap(),
38
+ batchedGeometryViews: new WeakMap(),
39
+ dashedLines: new WeakMap(),
40
+ instancedDashedLines: new WeakMap(),
41
+ texturePayloads: new WeakMap(),
42
+ materialColors: new WeakMap(),
43
+ textureStates: new WeakMap(),
44
+ materialRenderStates: new WeakMap(),
45
+ materialScalarFeatures: new WeakMap(),
46
+ pointBillboards: new WeakMap(),
47
+ spriteBillboards: new WeakMap(),
48
+ };
49
+ }
50
+ function flattenScene(scene, camera, viewportHeight = 512, globalClippingPlanes = [], localClippingEnabled = true, shadowMaterialMode, materialContext = {}, sortOptions = {}, overrideMaterial, cache, callbackContext) {
10
51
  const meshes = [];
11
- visitObject(scene, true, meshes);
12
- return meshes;
52
+ const clippingContext = {
53
+ unionPlanes: globalClippingPlanes,
54
+ intersectionPlanes: [],
55
+ clipShadows: false,
56
+ };
57
+ visitObject(scene, camera, meshes, 0, viewportHeight, clippingContext, localClippingEnabled, shadowMaterialMode, materialContext, overrideMaterial, cache, callbackContext);
58
+ return nativeMeshesFromSortedFlattened(sortFlattenedMeshes(meshes, sortOptions));
13
59
  }
14
- function visitObject(object, parentVisible, meshes) {
15
- if (!object || !parentVisible || object.visible === false)
60
+ function visitObject(object, camera, meshes, groupOrder, viewportHeight, clippingContext, localClippingEnabled, shadowMaterialMode, materialContext, overrideMaterial, cache, callbackContext) {
61
+ if (!object)
16
62
  return;
17
- if (object.isMesh === true && object.geometry) {
18
- appendMesh(object, meshes);
19
- }
20
- else if ((object.isLineSegments === true || object.isLineLoop === true || object.isLine === true) && object.geometry) {
21
- appendLineOrPoints(object, meshes, 'lines');
63
+ if (optionalObjectBoolean(object.visible, 'object.visible') === false)
64
+ return;
65
+ const nextGroupOrder = object.isGroup === true
66
+ ? renderOrderNumber(object.renderOrder, 'object.renderOrder', 0)
67
+ : groupOrder;
68
+ const visibleToCamera = (0, layers_1.objectLayersMatchCamera)(object, camera);
69
+ const nextClippingContext = visibleToCamera
70
+ ? clippingContextForObject(clippingContext, object)
71
+ : clippingContext;
72
+ if (visibleToCamera) {
73
+ updateLodObject(object, camera);
74
+ if (object.isBatchedMesh === true && object.geometry) {
75
+ if (!renderableObjectOutsideFrustum(object, camera)) {
76
+ appendBatchedMesh(object, camera, meshes, nextGroupOrder, nextClippingContext, localClippingEnabled, shadowMaterialMode, materialContext, overrideMaterial, cache, callbackContext);
77
+ }
78
+ }
79
+ else if (object.isMesh === true && object.geometry) {
80
+ if (!renderableObjectOutsideFrustum(object, camera)) {
81
+ appendMesh(object, camera, meshes, nextGroupOrder, nextClippingContext, localClippingEnabled, shadowMaterialMode, materialContext, overrideMaterial, cache, undefined, undefined, undefined, callbackContext);
82
+ }
83
+ }
84
+ else if ((object.isLineSegments === true || object.isLineLoop === true || object.isLine === true) && object.geometry) {
85
+ if (!renderableObjectOutsideFrustum(object, camera)) {
86
+ appendLineOrPoints(object, camera, meshes, 'lines', nextGroupOrder, viewportHeight, nextClippingContext, localClippingEnabled, materialContext, overrideMaterial, cache, callbackContext);
87
+ }
88
+ }
89
+ else if (object.isPoints === true && object.geometry) {
90
+ if (!renderableObjectOutsideFrustum(object, camera, viewportHeight, overrideMaterial)) {
91
+ appendPoints(object, camera, meshes, nextGroupOrder, viewportHeight, nextClippingContext, localClippingEnabled, shadowMaterialMode, materialContext, overrideMaterial, cache, callbackContext);
92
+ }
93
+ }
94
+ else if (object.isSprite === true) {
95
+ appendSprite(object, camera, meshes, nextGroupOrder, nextClippingContext, localClippingEnabled, shadowMaterialMode, materialContext, overrideMaterial, cache, callbackContext);
96
+ }
22
97
  }
23
- else if (object.isPoints === true && object.geometry) {
24
- appendLineOrPoints(object, meshes, 'points');
98
+ for (const child of (0, objects_1.objectChildren)(object)) {
99
+ visitObject(child, camera, meshes, nextGroupOrder, viewportHeight, nextClippingContext, localClippingEnabled, shadowMaterialMode, materialContext, overrideMaterial, cache, callbackContext);
25
100
  }
26
- const children = Array.isArray(object.children) ? object.children : [];
27
- for (const child of children) {
28
- visitObject(child, true, meshes);
101
+ }
102
+ function appendBatchedMesh(object, camera, meshes, groupOrder, clippingContext, localClippingEnabled, shadowMaterialMode, materialContext, overrideMaterial, cache, callbackContext) {
103
+ const geometry = object.geometry;
104
+ const draws = batchedMeshDraws(object, camera, geometry);
105
+ if (draws.length === 0)
106
+ return;
107
+ const objectSortZ = camera ? projectedObjectZ(object, camera) : 0;
108
+ const sortIndexBase = unsignedSortKey(object.id, meshes.length);
109
+ for (let drawOrder = 0; drawOrder < draws.length; drawOrder += 1) {
110
+ const draw = draws[drawOrder];
111
+ const geometryView = batchedGeometryView(geometry, draw.range, cache);
112
+ const objectView = Object.create(object);
113
+ objectView.geometry = geometryView;
114
+ objectView.isInstancedMesh = false;
115
+ objectView.instanceMatrix = undefined;
116
+ objectView.instanceColor = undefined;
117
+ objectView.count = undefined;
118
+ const sortKeyOverride = {
119
+ sortZ: objectSortZ,
120
+ sortIndex: batchedDrawSortIndex(sortIndexBase, drawOrder),
121
+ };
122
+ appendMesh(objectView, camera, meshes, groupOrder, clippingContext, localClippingEnabled, shadowMaterialMode, materialContext, overrideMaterial, cache, [draw.instance], sortKeyOverride, object, callbackContext, draw.z);
29
123
  }
30
124
  }
31
- function appendMesh(object, meshes) {
125
+ function appendMesh(object, camera, meshes, groupOrder, clippingContext, localClippingEnabled, shadowMaterialMode, materialContext, overrideMaterial, cache, instanceOverride, sortKeyOverride, sortItemObject, callbackContext, sortItemZOverride) {
32
126
  const geometry = object.geometry;
33
- const position = (0, attributes_1.getAttribute)(geometry, 'position');
34
- if (!position)
127
+ const geometryExtraction = meshGeometryExtraction(geometry, cache);
128
+ if (!geometryExtraction)
35
129
  return;
36
- let positions = (0, attributes_1.readVec3Attribute)(position);
37
- const uvAttribute = (0, attributes_1.getAttribute)(geometry, 'uv');
38
- const uvs = uvAttribute ? (0, attributes_1.readVec2Attribute)(uvAttribute) : null;
39
- const normalAttribute = (0, attributes_1.getAttribute)(geometry, 'normal');
40
- let normals = normalAttribute ? (0, attributes_1.readVec3Attribute)(normalAttribute) : null;
41
- const vertexColors = (0, attributes_1.getAttribute)(geometry, 'color');
42
- const index = geometry.index ? (0, attributes_1.readIndexAttribute)(geometry.index) : null;
43
- const groups = effectiveGroups(geometry, index, position.count);
130
+ const { position, uvChannels, uvs, normalAttribute, vertexColors, index, groups, instancedGeometryCount, instancedPositionOffset, instancedPositionScale, } = geometryExtraction;
131
+ let positions = geometryExtraction.positions;
132
+ let normals = geometryExtraction.normals;
44
133
  // CPU-side morph targets (blend shapes / shape keys / VRM blendshapes)
45
134
  if (object.morphTargetInfluences && object.morphTargetInfluences.length > 0) {
46
135
  const morphed = (0, morphs_1.applyMorphTargets)(object, positions, normals);
@@ -55,140 +144,3625 @@ function appendMesh(object, meshes) {
55
144
  }
56
145
  // For skinned meshes, positions are already in world space after CPU skinning.
57
146
  const isSkinned = object.isSkinnedMesh === true && object.skeleton;
58
- const meshTransform = isSkinned
147
+ const meshTransform = instanceOverride
59
148
  ? math_1.IDENTITY_4X4.slice()
60
- : (0, math_1.matrixElements)(object.matrixWorld, 'mesh.matrixWorld');
149
+ : isSkinned
150
+ ? math_1.IDENTITY_4X4.slice()
151
+ : (0, math_1.matrixElements)(object.matrixWorld, 'mesh.matrixWorld');
152
+ const instances = instanceOverride ?? meshInstances(object, meshTransform, cache);
153
+ if (instances.length === 0)
154
+ return;
155
+ const objectCastsShadow = optionalObjectBoolean(object.castShadow, 'object.castShadow') === true;
156
+ const objectReceivesShadow = optionalObjectBoolean(object.receiveShadow, 'object.receiveShadow') === true;
157
+ const callbackObject = sortItemObject ?? object;
61
158
  for (const group of groups) {
62
- const material = (0, materials_1.materialForGroup)(object.material, group.materialIndex);
159
+ const material = materialForObjectGroup(object, group.materialIndex, overrideMaterial);
63
160
  if (material?.visible === false)
64
161
  continue;
65
- const color = (0, materials_1.materialColor)(material);
162
+ invokeObjectRenderCallback(callbackObject.onBeforeRender, 'onBeforeRender', callbackContext, callbackObject, camera, geometry, material, group);
163
+ const customShadowMaterial = customShadowMaterialForMode(object, shadowMaterialMode);
164
+ const usesCustomShadowMaterial = objectCastsShadow && customShadowMaterial != null;
165
+ const baseColor = (0, materials_1.materialColor)(material, materialContext);
66
166
  const useVertexColors = vertexColors && material?.vertexColors !== false;
67
- const pbrProps = (0, materials_1.extractPbrProperties)(material);
68
- const textureInfo = (0, materials_1.extractTextureData)(material);
69
- const castShadow = object.castShadow === true ? true : undefined;
70
- const receiveShadow = object.receiveShadow === true ? true : undefined;
167
+ const pbrProps = (0, materials_1.extractPbrProperties)(material, materialContext);
168
+ assertSupportedCustomFragmentInstancedAttributes(geometry, pbrProps);
169
+ const uvStreams = textureUvStreamsForMeshMaterial(uvChannels, material);
170
+ if (uvStreams.alphaMapUsesUv2 !== undefined) {
171
+ pbrProps.alphaMapUsesUv2 = uvStreams.alphaMapUsesUv2;
172
+ }
173
+ applyPbrUvStreamFlags(pbrProps, uvStreams);
174
+ const textureInfo = (0, materials_1.extractTextureData)(material, materialContext);
175
+ const castShadow = objectCastsShadow && !usesCustomShadowMaterial ? true : undefined;
176
+ const receiveShadow = objectReceivesShadow ? true : undefined;
177
+ const clipping = clippingState(clippingContext, material, localClippingEnabled);
178
+ const wireframe = isMeshWireframeMaterial(material);
71
179
  if (index) {
72
180
  const indices = index.slice(group.start, group.start + group.count);
73
181
  if (indices.length % 3 !== 0) {
74
182
  throw new Error(`THREE.Mesh "${object.name || object.uuid || '<unnamed>'}" has a non-triangle index range`);
75
183
  }
76
- meshes.push({
77
- positions,
78
- indices,
79
- normals: normals ?? undefined,
80
- color,
81
- colors: useVertexColors ? (0, attributes_1.readColorAttribute)(vertexColors, color) : undefined,
82
- uvs: uvs ?? undefined,
83
- texture: textureInfo?.data,
84
- textureWidth: textureInfo?.width ?? undefined,
85
- textureHeight: textureInfo?.height ?? undefined,
86
- textureWrapS: textureInfo?.wrapS,
87
- textureWrapT: textureInfo?.wrapT,
88
- transform: meshTransform,
89
- castShadow,
90
- receiveShadow,
91
- ...pbrProps,
92
- });
184
+ const renderIndices = wireframe ? wireframeIndicesForTriangles(indices) : indices;
185
+ const expandedIndices = expandIndicesForInstances(renderIndices, position.count, instancedGeometryCount);
186
+ const expandedPositions = expandVec3ValuesForInstancesWithCache(cache, geometry, position, positions, 0, position.count, instancedGeometryCount, instancedPositionOffset, instancedPositionScale);
187
+ const expandedNormals = normalAttribute && normals
188
+ ? expandNormalValuesForInstancesWithCache(cache, geometry, normalAttribute, normals, 0, position.count, instancedGeometryCount)
189
+ : undefined;
190
+ const expandedUvs = uvStreams.uvs
191
+ ? expandUvChannelForInstancesWithCache(cache, geometry, uvStreams.uvs, 0, position.count, instancedGeometryCount)
192
+ : undefined;
193
+ const expandedSecondaryUvs = uvStreams.uvs2
194
+ ? expandUvChannelForInstancesWithCache(cache, geometry, uvStreams.uvs2, 0, position.count, instancedGeometryCount)
195
+ : undefined;
196
+ for (const instance of instances) {
197
+ const color = instanceColor(baseColor, instance);
198
+ const sortInfo = sortInfoForObject(object, material, camera, meshes.length, groupOrder, instance.transform, geometry, group, sortItemObject, sortItemZOverride);
199
+ const sortKeys = mergeSortKeys(sortInfo.keys, sortKeyOverride);
200
+ pushMesh(meshes, {
201
+ positions: expandedPositions,
202
+ indices: expandedIndices,
203
+ normals: expandedNormals,
204
+ color,
205
+ colors: useVertexColors
206
+ ? expandColorAttributeForInstancesWithCache(cache, geometry, vertexColors, color, 0, position.count, instancedGeometryCount)
207
+ : undefined,
208
+ uvs: expandedUvs,
209
+ uvs2: expandedSecondaryUvs,
210
+ texture: textureInfo?.data,
211
+ textureWidth: textureInfo?.width ?? undefined,
212
+ textureHeight: textureInfo?.height ?? undefined,
213
+ textureWrapS: textureInfo?.wrapS,
214
+ textureWrapT: textureInfo?.wrapT,
215
+ textureMagFilter: textureInfo?.magFilter,
216
+ textureMinFilter: textureInfo?.minFilter,
217
+ textureAnisotropy: textureInfo?.anisotropy,
218
+ textureTransform: textureInfo?.transform,
219
+ textureColorSpace: textureInfo?.colorSpace,
220
+ textureUsesUv2: uvStreams.textureUsesUv2 ?? textureInfo?.usesUv2,
221
+ transform: instance.transform,
222
+ topology: wireframe ? 'lines' : undefined,
223
+ castShadow,
224
+ receiveShadow,
225
+ clipShadows: clipShadowsForMaterial(material, clippingContext),
226
+ ...clipping,
227
+ ...sortKeys,
228
+ ...pbrProps,
229
+ }, sortInfo.item, needsTransparentDoubleSidePass(material, pbrProps, wireframe));
230
+ }
93
231
  }
94
232
  else {
95
233
  if (group.count % 3 !== 0) {
96
234
  throw new Error(`THREE.Mesh "${object.name || object.uuid || '<unnamed>'}" has a non-triangle vertex range`);
97
235
  }
98
- meshes.push({
99
- positions: positions.slice(group.start * 3, (group.start + group.count) * 3),
100
- normals: normals ? normals.slice(group.start * 3, (group.start + group.count) * 3) : undefined,
236
+ const expandedGroupPositions = expandVec3ValuesForInstancesWithCache(cache, geometry, position, positions, group.start, group.count, instancedGeometryCount, instancedPositionOffset, instancedPositionScale);
237
+ const expandedGroupNormals = normalAttribute && normals
238
+ ? expandNormalValuesForInstancesWithCache(cache, geometry, normalAttribute, normals, group.start, group.count, instancedGeometryCount)
239
+ : undefined;
240
+ const expandedGroupUvs = uvStreams.uvs
241
+ ? expandUvChannelForInstancesWithCache(cache, geometry, uvStreams.uvs, group.start, group.count, instancedGeometryCount)
242
+ : undefined;
243
+ const expandedGroupSecondaryUvs = uvStreams.uvs2
244
+ ? expandUvChannelForInstancesWithCache(cache, geometry, uvStreams.uvs2, group.start, group.count, instancedGeometryCount)
245
+ : undefined;
246
+ const expandedGroupIndices = wireframe
247
+ ? expandIndicesForInstances(wireframeIndicesForUnindexedTriangles(group.count), group.count, instancedGeometryCount)
248
+ : undefined;
249
+ for (const instance of instances) {
250
+ const color = instanceColor(baseColor, instance);
251
+ const sortInfo = sortInfoForObject(object, material, camera, meshes.length, groupOrder, instance.transform, geometry, group, sortItemObject, sortItemZOverride);
252
+ const sortKeys = mergeSortKeys(sortInfo.keys, sortKeyOverride);
253
+ pushMesh(meshes, {
254
+ positions: expandedGroupPositions,
255
+ indices: expandedGroupIndices,
256
+ normals: expandedGroupNormals,
257
+ color,
258
+ colors: useVertexColors
259
+ ? expandColorAttributeForInstancesWithCache(cache, geometry, vertexColors, color, group.start, group.count, instancedGeometryCount)
260
+ : undefined,
261
+ uvs: expandedGroupUvs,
262
+ uvs2: expandedGroupSecondaryUvs,
263
+ texture: textureInfo?.data,
264
+ textureWidth: textureInfo?.width ?? undefined,
265
+ textureHeight: textureInfo?.height ?? undefined,
266
+ textureWrapS: textureInfo?.wrapS,
267
+ textureWrapT: textureInfo?.wrapT,
268
+ textureMagFilter: textureInfo?.magFilter,
269
+ textureMinFilter: textureInfo?.minFilter,
270
+ textureAnisotropy: textureInfo?.anisotropy,
271
+ textureTransform: textureInfo?.transform,
272
+ textureColorSpace: textureInfo?.colorSpace,
273
+ textureUsesUv2: uvStreams.textureUsesUv2 ?? textureInfo?.usesUv2,
274
+ transform: instance.transform,
275
+ topology: wireframe ? 'lines' : undefined,
276
+ castShadow,
277
+ receiveShadow,
278
+ clipShadows: clipShadowsForMaterial(material, clippingContext),
279
+ ...clipping,
280
+ ...sortKeys,
281
+ ...pbrProps,
282
+ }, sortInfo.item, needsTransparentDoubleSidePass(material, pbrProps, wireframe));
283
+ }
284
+ }
285
+ if (usesCustomShadowMaterial && customShadowMaterial?.visible !== false) {
286
+ appendShadowOnlyMeshGroup(object, camera, meshes, group, groupOrder, clippingContext, localClippingEnabled, customShadowMaterial, material, materialContext, positions, normals, normalAttribute, uvs, uvChannels, vertexColors, position.count, index, instancedGeometryCount, instancedPositionOffset, instancedPositionScale, instances, cache);
287
+ }
288
+ invokeObjectRenderCallback(callbackObject.onAfterRender, 'onAfterRender', callbackContext, callbackObject, camera, geometry, material, group);
289
+ }
290
+ }
291
+ function meshGeometryExtraction(geometry, cache) {
292
+ const position = (0, attributes_1.getAttribute)(geometry, 'position');
293
+ if (!position)
294
+ return null;
295
+ const signature = meshGeometrySignature(geometry, position);
296
+ if (cache && signature.cacheable) {
297
+ const cached = cache.meshGeometry.get(geometry);
298
+ if (cached && sameMeshGeometrySignature(cached.signature, signature)) {
299
+ return cached.extraction;
300
+ }
301
+ }
302
+ const extraction = readMeshGeometryExtraction(geometry, position);
303
+ if (cache && signature.cacheable) {
304
+ cache.meshGeometry.set(geometry, { signature, extraction });
305
+ }
306
+ return extraction;
307
+ }
308
+ function readMeshGeometryExtraction(geometry, position) {
309
+ const positions = (0, attributes_1.readVec3Attribute)(position, 'geometry.attributes.position');
310
+ const uvChannels = readUvChannels(geometry);
311
+ const normalAttribute = (0, attributes_1.getAttribute)(geometry, 'normal');
312
+ const normals = normalAttribute ? (0, attributes_1.readVec3Attribute)(normalAttribute, 'geometry.attributes.normal') : null;
313
+ const vertexColors = (0, attributes_1.getAttribute)(geometry, 'color');
314
+ const index = geometry.index ? (0, attributes_1.readIndexAttribute)(geometry.index, 'geometry.index', position.count) : null;
315
+ const sourceIndex = index ?? rangeIndices(position.count);
316
+ const groups = effectiveGroups(geometry, index, position.count);
317
+ const instancedGeometryCount = instancedBufferGeometryCount(geometry);
318
+ const instancedPositionOffset = instancedOffsetAttribute(geometry);
319
+ const instancedPositionScale = instancedScaleAttribute(geometry);
320
+ return {
321
+ position,
322
+ positions,
323
+ uvChannels,
324
+ uvs: uvChannels[0]?.values ?? null,
325
+ normalAttribute,
326
+ normals,
327
+ vertexColors,
328
+ index,
329
+ sourceIndex,
330
+ groups,
331
+ instancedGeometryCount,
332
+ instancedPositionOffset,
333
+ instancedPositionScale,
334
+ };
335
+ }
336
+ function meshGeometrySignature(geometry, position) {
337
+ const attributes = (0, attributes_1.geometryAttributes)(geometry);
338
+ const instancedAttributes = Object.entries(attributes)
339
+ .filter((entry) => isInstancedAttribute(entry[1]))
340
+ .map(([name, attribute]) => ({ name, signature: attributeSignature(attribute) }));
341
+ const instancedPositionOffset = namedInstancedOffsetAttribute(geometry);
342
+ const instancedPositionScale = namedInstancedScaleAttribute(geometry);
343
+ const signature = {
344
+ cacheable: true,
345
+ geometryVersion: geometry.version,
346
+ isInstancedBufferGeometry: geometry.isInstancedBufferGeometry,
347
+ instanceCount: geometry.instanceCount,
348
+ drawRange: geometryDrawRangeSignature(geometry.drawRange),
349
+ drawRangeStart: geometry.drawRange?.start,
350
+ drawRangeCount: geometry.drawRange?.count,
351
+ groups: geometryGroupsSignature(geometry.groups),
352
+ position: attributeSignature(position),
353
+ normal: attributeSignature((0, attributes_1.getAttribute)(geometry, 'normal')),
354
+ color: attributeSignature((0, attributes_1.getAttribute)(geometry, 'color')),
355
+ index: attributeSignature(geometry.index),
356
+ uv: attributeSignature((0, attributes_1.getAttribute)(geometry, 'uv')),
357
+ uv1: attributeSignature((0, attributes_1.getAttribute)(geometry, 'uv1')),
358
+ uv2: attributeSignature((0, attributes_1.getAttribute)(geometry, 'uv2')),
359
+ uv3: attributeSignature((0, attributes_1.getAttribute)(geometry, 'uv3')),
360
+ instancedPositionOffsetName: instancedPositionOffset?.name,
361
+ instancedPositionOffset: attributeSignature(instancedPositionOffset?.attribute),
362
+ instancedPositionScaleName: instancedPositionScale?.name,
363
+ instancedPositionScale: attributeSignature(instancedPositionScale?.attribute),
364
+ instancedAttributes,
365
+ };
366
+ signature.cacheable = meshGeometrySignatureCacheable(signature);
367
+ return signature;
368
+ }
369
+ function meshGeometrySignatureCacheable(signature) {
370
+ return [
371
+ signature.position,
372
+ signature.normal,
373
+ signature.color,
374
+ signature.index,
375
+ signature.uv,
376
+ signature.uv1,
377
+ signature.uv2,
378
+ signature.uv3,
379
+ signature.instancedPositionOffset,
380
+ signature.instancedPositionScale,
381
+ ...signature.instancedAttributes.map(({ signature }) => signature),
382
+ ].every(attributeSignatureCacheable);
383
+ }
384
+ function attributeSignature(attribute) {
385
+ if (!attribute)
386
+ return {};
387
+ const data = attribute.data && typeof attribute.data === 'object' && !Array.isArray(attribute.data)
388
+ ? attribute.data
389
+ : undefined;
390
+ return {
391
+ ref: attribute,
392
+ version: attribute.version,
393
+ count: attribute.count,
394
+ itemSize: attribute.itemSize,
395
+ normalized: attribute.normalized,
396
+ array: attribute.array,
397
+ dataArray: data?.array,
398
+ dataStride: data?.stride,
399
+ offset: attribute.offset,
400
+ isInstancedBufferAttribute: attribute.isInstancedBufferAttribute,
401
+ meshPerAttribute: attribute.meshPerAttribute,
402
+ };
403
+ }
404
+ function attributeSignatureCacheable(signature) {
405
+ return signature.ref == null || typeof signature.version === 'number';
406
+ }
407
+ function namedInstancedOffsetAttribute(geometry) {
408
+ const names = ['instanceOffset', 'instancePosition', 'offset', 'translate', 'translation'];
409
+ for (const name of names) {
410
+ const attribute = (0, attributes_1.getAttribute)(geometry, name);
411
+ if (isInstancedAttribute(attribute))
412
+ return { name, attribute };
413
+ }
414
+ return null;
415
+ }
416
+ function namedInstancedScaleAttribute(geometry) {
417
+ const names = ['instanceScale', 'scale'];
418
+ for (const name of names) {
419
+ const attribute = (0, attributes_1.getAttribute)(geometry, name);
420
+ if (isInstancedAttribute(attribute))
421
+ return { name, attribute };
422
+ }
423
+ return null;
424
+ }
425
+ function geometryGroupsSignature(groups) {
426
+ if (groups == null)
427
+ return 'none';
428
+ if (!Array.isArray(groups))
429
+ return `invalid:${typeof groups}`;
430
+ return groups.map((group, index) => {
431
+ if (!group || typeof group !== 'object' || Array.isArray(group)) {
432
+ return `${index}:invalid:${typeof group}`;
433
+ }
434
+ return [
435
+ index,
436
+ typeof group.start,
437
+ String(group.start),
438
+ typeof group.count,
439
+ String(group.count),
440
+ typeof group.materialIndex,
441
+ String(group.materialIndex),
442
+ ].join(':');
443
+ }).join('|');
444
+ }
445
+ function geometryDrawRangeSignature(drawRange) {
446
+ if (drawRange == null)
447
+ return 'none';
448
+ if (typeof drawRange !== 'object' || Array.isArray(drawRange))
449
+ return `invalid:${typeof drawRange}`;
450
+ return [
451
+ typeof drawRange.start,
452
+ String(drawRange.start),
453
+ typeof drawRange.count,
454
+ String(drawRange.count),
455
+ ].join(':');
456
+ }
457
+ function sameMeshGeometrySignature(a, b) {
458
+ return a.cacheable === b.cacheable
459
+ && a.geometryVersion === b.geometryVersion
460
+ && a.isInstancedBufferGeometry === b.isInstancedBufferGeometry
461
+ && a.instanceCount === b.instanceCount
462
+ && a.drawRange === b.drawRange
463
+ && Object.is(a.drawRangeStart, b.drawRangeStart)
464
+ && Object.is(a.drawRangeCount, b.drawRangeCount)
465
+ && a.groups === b.groups
466
+ && a.instancedPositionOffsetName === b.instancedPositionOffsetName
467
+ && a.instancedPositionScaleName === b.instancedPositionScaleName
468
+ && sameAttributeSignature(a.position, b.position)
469
+ && sameAttributeSignature(a.normal, b.normal)
470
+ && sameAttributeSignature(a.color, b.color)
471
+ && sameAttributeSignature(a.index, b.index)
472
+ && sameAttributeSignature(a.uv, b.uv)
473
+ && sameAttributeSignature(a.uv1, b.uv1)
474
+ && sameAttributeSignature(a.uv2, b.uv2)
475
+ && sameAttributeSignature(a.uv3, b.uv3)
476
+ && sameAttributeSignature(a.instancedPositionOffset, b.instancedPositionOffset)
477
+ && sameAttributeSignature(a.instancedPositionScale, b.instancedPositionScale)
478
+ && sameInstancedAttributeSignatures(a.instancedAttributes, b.instancedAttributes);
479
+ }
480
+ function sameInstancedAttributeSignatures(a, b) {
481
+ if (a.length !== b.length)
482
+ return false;
483
+ for (let i = 0; i < a.length; i += 1) {
484
+ if (a[i].name !== b[i].name)
485
+ return false;
486
+ if (!sameAttributeSignature(a[i].signature, b[i].signature))
487
+ return false;
488
+ }
489
+ return true;
490
+ }
491
+ function sameAttributeSignature(a, b) {
492
+ return a.ref === b.ref
493
+ && a.version === b.version
494
+ && a.count === b.count
495
+ && a.itemSize === b.itemSize
496
+ && a.normalized === b.normalized
497
+ && a.array === b.array
498
+ && a.dataArray === b.dataArray
499
+ && a.dataStride === b.dataStride
500
+ && a.offset === b.offset
501
+ && a.isInstancedBufferAttribute === b.isInstancedBufferAttribute
502
+ && a.meshPerAttribute === b.meshPerAttribute;
503
+ }
504
+ function appendShadowOnlyMeshGroup(object, camera, meshes, group, groupOrder, clippingContext, localClippingEnabled, material, sourceMaterial, materialContext, positions, normals, normalAttribute, uvs, uvChannels, vertexColors, vertexCount, index, instancedGeometryCount, instancedPositionOffset, instancedPositionScale, instances, cache) {
505
+ const shadowMaterial = shadowMaterialWithSourceShadowState(material, sourceMaterial);
506
+ const baseColor = (0, materials_1.materialColor)(shadowMaterial, materialContext);
507
+ const useVertexColors = vertexColors && material.vertexColors !== false;
508
+ const pbrProps = shadowPbrProperties(shadowMaterial, sourceMaterial, materialContext);
509
+ assertSupportedCustomFragmentInstancedAttributes(object.geometry, pbrProps);
510
+ const uvStreams = textureUvStreamsForMeshMaterial(uvChannels, shadowMaterial);
511
+ if (uvStreams.alphaMapUsesUv2 !== undefined) {
512
+ pbrProps.alphaMapUsesUv2 = uvStreams.alphaMapUsesUv2;
513
+ }
514
+ applyPbrUvStreamFlags(pbrProps, uvStreams);
515
+ const textureInfo = (0, materials_1.extractTextureData)(shadowMaterial, materialContext);
516
+ const clipping = clippingState(clippingContext, shadowMaterial, localClippingEnabled);
517
+ const wireframe = isDepthDistanceWireframeMaterial(shadowMaterial);
518
+ const hiddenMainPass = shadowOnlyMainPassState();
519
+ if (index) {
520
+ const indices = index.slice(group.start, group.start + group.count);
521
+ if (indices.length % 3 !== 0) {
522
+ throw new Error(`THREE.Mesh "${object.name || object.uuid || '<unnamed>'}" has a non-triangle index range`);
523
+ }
524
+ const renderIndices = wireframe ? wireframeIndicesForTriangles(indices) : indices;
525
+ const expandedIndices = expandIndicesForInstances(renderIndices, vertexCount, instancedGeometryCount);
526
+ const geometry = object.geometry;
527
+ const expandedPositions = expandVec3ValuesForInstancesWithCache(cache, geometry, (0, attributes_1.getAttribute)(geometry, 'position'), positions, 0, vertexCount, instancedGeometryCount, instancedPositionOffset, instancedPositionScale);
528
+ const expandedNormals = normalAttribute && normals
529
+ ? expandNormalValuesForInstancesWithCache(cache, geometry, normalAttribute, normals, 0, vertexCount, instancedGeometryCount)
530
+ : undefined;
531
+ const expandedUvs = uvStreams.uvs
532
+ ? expandUvChannelForInstancesWithCache(cache, geometry, uvStreams.uvs, 0, vertexCount, instancedGeometryCount)
533
+ : undefined;
534
+ const expandedSecondaryUvs = uvStreams.uvs2
535
+ ? expandUvChannelForInstancesWithCache(cache, geometry, uvStreams.uvs2, 0, vertexCount, instancedGeometryCount)
536
+ : undefined;
537
+ for (const instance of instances) {
538
+ const color = instanceColor(baseColor, instance);
539
+ const sortInfo = sortInfoForObject(object, material, camera, meshes.length, groupOrder, instance.transform, object.geometry, group);
540
+ pushMesh(meshes, {
541
+ positions: expandedPositions,
542
+ indices: expandedIndices,
543
+ normals: expandedNormals,
101
544
  color,
102
545
  colors: useVertexColors
103
- ? (0, attributes_1.readColorAttribute)(vertexColors, color).slice(group.start * 4, (group.start + group.count) * 4)
546
+ ? expandColorAttributeForInstancesWithCache(cache, geometry, vertexColors, color, 0, vertexCount, instancedGeometryCount)
104
547
  : undefined,
105
- uvs: uvs ? uvs.slice(group.start * 2, (group.start + group.count) * 2) : undefined,
548
+ uvs: expandedUvs,
549
+ uvs2: expandedSecondaryUvs,
106
550
  texture: textureInfo?.data,
107
551
  textureWidth: textureInfo?.width ?? undefined,
108
552
  textureHeight: textureInfo?.height ?? undefined,
109
553
  textureWrapS: textureInfo?.wrapS,
110
554
  textureWrapT: textureInfo?.wrapT,
111
- transform: meshTransform,
112
- castShadow,
113
- receiveShadow,
555
+ textureMagFilter: textureInfo?.magFilter,
556
+ textureMinFilter: textureInfo?.minFilter,
557
+ textureAnisotropy: textureInfo?.anisotropy,
558
+ textureTransform: textureInfo?.transform,
559
+ textureColorSpace: textureInfo?.colorSpace,
560
+ textureUsesUv2: uvStreams.textureUsesUv2 ?? textureInfo?.usesUv2,
561
+ transform: instance.transform,
562
+ topology: wireframe ? 'lines' : undefined,
563
+ castShadow: true,
564
+ receiveShadow: false,
565
+ clipShadows: clipShadowsForMaterial(shadowMaterial, clippingContext),
566
+ ...clipping,
567
+ ...sortInfo.keys,
114
568
  ...pbrProps,
115
- });
569
+ ...hiddenMainPass,
570
+ }, sortInfo.item);
571
+ }
572
+ return;
573
+ }
574
+ if (group.count % 3 !== 0) {
575
+ throw new Error(`THREE.Mesh "${object.name || object.uuid || '<unnamed>'}" has a non-triangle vertex range`);
576
+ }
577
+ const geometry = object.geometry;
578
+ const expandedGroupPositions = expandVec3ValuesForInstancesWithCache(cache, geometry, (0, attributes_1.getAttribute)(geometry, 'position'), positions, group.start, group.count, instancedGeometryCount, instancedPositionOffset, instancedPositionScale);
579
+ const expandedGroupNormals = normalAttribute && normals
580
+ ? expandNormalValuesForInstancesWithCache(cache, geometry, normalAttribute, normals, group.start, group.count, instancedGeometryCount)
581
+ : undefined;
582
+ const expandedGroupUvs = uvStreams.uvs
583
+ ? expandUvChannelForInstancesWithCache(cache, geometry, uvStreams.uvs, group.start, group.count, instancedGeometryCount)
584
+ : undefined;
585
+ const expandedGroupSecondaryUvs = uvStreams.uvs2
586
+ ? expandUvChannelForInstancesWithCache(cache, geometry, uvStreams.uvs2, group.start, group.count, instancedGeometryCount)
587
+ : undefined;
588
+ const expandedGroupIndices = wireframe
589
+ ? expandIndicesForInstances(wireframeIndicesForUnindexedTriangles(group.count), group.count, instancedGeometryCount)
590
+ : undefined;
591
+ for (const instance of instances) {
592
+ const color = instanceColor(baseColor, instance);
593
+ const sortInfo = sortInfoForObject(object, material, camera, meshes.length, groupOrder, instance.transform, object.geometry, group);
594
+ pushMesh(meshes, {
595
+ positions: expandedGroupPositions,
596
+ indices: expandedGroupIndices,
597
+ normals: expandedGroupNormals,
598
+ color,
599
+ colors: useVertexColors
600
+ ? expandColorAttributeForInstancesWithCache(cache, geometry, vertexColors, color, group.start, group.count, instancedGeometryCount)
601
+ : undefined,
602
+ uvs: expandedGroupUvs,
603
+ uvs2: expandedGroupSecondaryUvs,
604
+ texture: textureInfo?.data,
605
+ textureWidth: textureInfo?.width ?? undefined,
606
+ textureHeight: textureInfo?.height ?? undefined,
607
+ textureWrapS: textureInfo?.wrapS,
608
+ textureWrapT: textureInfo?.wrapT,
609
+ textureMagFilter: textureInfo?.magFilter,
610
+ textureMinFilter: textureInfo?.minFilter,
611
+ textureAnisotropy: textureInfo?.anisotropy,
612
+ textureTransform: textureInfo?.transform,
613
+ textureColorSpace: textureInfo?.colorSpace,
614
+ textureUsesUv2: uvStreams.textureUsesUv2 ?? textureInfo?.usesUv2,
615
+ transform: instance.transform,
616
+ topology: wireframe ? 'lines' : undefined,
617
+ castShadow: true,
618
+ receiveShadow: false,
619
+ clipShadows: clipShadowsForMaterial(shadowMaterial, clippingContext),
620
+ ...clipping,
621
+ ...sortInfo.keys,
622
+ ...pbrProps,
623
+ ...hiddenMainPass,
624
+ }, sortInfo.item);
625
+ }
626
+ }
627
+ function customShadowMaterialForMode(object, mode) {
628
+ if (mode === 'depth') {
629
+ (0, materials_1.assertMaterialLike)(object.customDepthMaterial, 'Object3D.customDepthMaterial');
630
+ optionalSceneBoolean(object.customDepthMaterial?.visible, 'Object3D.customDepthMaterial.visible');
631
+ return object.customDepthMaterial;
632
+ }
633
+ if (mode === 'distance') {
634
+ (0, materials_1.assertMaterialLike)(object.customDistanceMaterial, 'Object3D.customDistanceMaterial');
635
+ optionalSceneBoolean(object.customDistanceMaterial?.visible, 'Object3D.customDistanceMaterial.visible');
636
+ return object.customDistanceMaterial;
637
+ }
638
+ return undefined;
639
+ }
640
+ function materialForObjectGroup(object, materialIndex, overrideMaterial) {
641
+ if (overrideMaterial !== undefined) {
642
+ (0, materials_1.assertMaterialLike)(overrideMaterial, 'scene.overrideMaterial');
643
+ return overrideMaterial;
644
+ }
645
+ return (0, materials_1.materialForGroup)(object.material, materialIndex);
646
+ }
647
+ function invokeObjectRenderCallback(callback, name, context, object, camera, geometry, material, group) {
648
+ if (callback == null)
649
+ return;
650
+ if (typeof callback !== 'function') {
651
+ throw new TypeError(`THREE.Object3D.${name} must be a function when provided.`);
652
+ }
653
+ if (isInternalBatchedMeshRenderCallback(object, callback, name))
654
+ return;
655
+ if (!context)
656
+ return;
657
+ callback.call(object, context.renderer, context.scene, camera, geometry, material, group);
658
+ }
659
+ function isInternalBatchedMeshRenderCallback(object, callback, name) {
660
+ if (name !== 'onBeforeRender' || object.isBatchedMesh !== true)
661
+ return false;
662
+ if (Object.prototype.hasOwnProperty.call(object, name))
663
+ return false;
664
+ let prototype = Object.getPrototypeOf(object);
665
+ while (prototype && prototype !== Object.prototype) {
666
+ if (prototype.constructor?.name === 'BatchedMesh' && typeof prototype[name] === 'function') {
667
+ return callback === prototype[name];
668
+ }
669
+ prototype = Object.getPrototypeOf(prototype);
670
+ }
671
+ return false;
672
+ }
673
+ function shadowOnlyMainPassState() {
674
+ return {
675
+ blending: 'none',
676
+ colorWrite: false,
677
+ depthTest: false,
678
+ depthWrite: false,
679
+ stencilWrite: false,
680
+ transparent: false,
681
+ };
682
+ }
683
+ function appendSprite(object, camera, meshes, groupOrder, clippingContext, localClippingEnabled, shadowMaterialMode, materialContext, overrideMaterial, cache, callbackContext) {
684
+ const objectCastsShadow = optionalObjectBoolean(object.castShadow, 'object.castShadow') === true;
685
+ optionalObjectBoolean(object.receiveShadow, 'object.receiveShadow');
686
+ const material = materialForObjectGroup(object, 0, overrideMaterial);
687
+ if (material?.visible === false)
688
+ return;
689
+ validateSpriteScale(object);
690
+ const matrix = (0, math_1.matrixElements)(object.matrixWorld, 'sprite.matrixWorld');
691
+ const center = [
692
+ finiteMaterialOrObjectNumber(object.center?.x, 'Sprite.center.x', 0.5),
693
+ finiteMaterialOrObjectNumber(object.center?.y, 'Sprite.center.y', 0.5),
694
+ ];
695
+ if (spriteOutsideFrustum(object, camera, matrix, center))
696
+ return;
697
+ invokeObjectRenderCallback(object.onBeforeRender, 'onBeforeRender', callbackContext, object, camera, object.geometry, material);
698
+ const worldPosition = [matrix[12], matrix[13], matrix[14]];
699
+ let scaleX = columnLength3(matrix, 0);
700
+ let scaleY = columnLength3(matrix, 4);
701
+ const sizeAttenuation = optionalSceneBoolean(material?.sizeAttenuation, 'material.sizeAttenuation');
702
+ if (sizeAttenuation === false && camera?.isPerspectiveCamera === true) {
703
+ const viewZ = viewSpaceZ(worldPosition, camera);
704
+ if (Number.isFinite(viewZ)) {
705
+ scaleX *= -viewZ;
706
+ scaleY *= -viewZ;
707
+ }
708
+ }
709
+ if (scaleX <= 0 || scaleY <= 0)
710
+ return;
711
+ const axes = cameraBillboardAxes(camera);
712
+ const rotation = finiteMaterialOrObjectNumber(material?.rotation, 'material.rotation', 0);
713
+ const billboard = spriteBillboardExpansion(object, matrix, worldPosition, center, scaleX, scaleY, rotation, sizeAttenuation, axes, camera, cache);
714
+ const positions = billboard.positions;
715
+ const indices = billboard.indices;
716
+ const uvs = billboard.uvs;
717
+ const textureInfo = (0, materials_1.extractTextureData)(material, materialContext);
718
+ const sortInfo = sortInfoForObject(object, material, camera, meshes.length, groupOrder);
719
+ const pbrProps = (0, materials_1.extractPbrProperties)(material, materialContext);
720
+ pbrProps.alphaMapUsesUv2 = false;
721
+ const clipping = clippingState(clippingContext, material, localClippingEnabled);
722
+ const customShadowMaterial = customShadowMaterialForMode(object, shadowMaterialMode);
723
+ const usesCustomShadowMaterial = objectCastsShadow && customShadowMaterial != null;
724
+ pushMesh(meshes, {
725
+ positions,
726
+ indices,
727
+ uvs,
728
+ color: (0, materials_1.materialColor)(material, materialContext),
729
+ texture: textureInfo?.data,
730
+ textureWidth: textureInfo?.width ?? undefined,
731
+ textureHeight: textureInfo?.height ?? undefined,
732
+ textureWrapS: textureInfo?.wrapS,
733
+ textureWrapT: textureInfo?.wrapT,
734
+ textureMagFilter: textureInfo?.magFilter,
735
+ textureMinFilter: textureInfo?.minFilter,
736
+ textureAnisotropy: textureInfo?.anisotropy,
737
+ textureTransform: textureInfo?.transform,
738
+ textureColorSpace: textureInfo?.colorSpace,
739
+ textureUsesUv2: false,
740
+ transform: math_1.IDENTITY_4X4.slice(),
741
+ transparent: material?.transparent !== false,
742
+ castShadow: objectCastsShadow && !usesCustomShadowMaterial ? true : undefined,
743
+ receiveShadow: undefined,
744
+ clipShadows: clipShadowsForMaterial(material, clippingContext),
745
+ ...clipping,
746
+ ...sortInfo.keys,
747
+ ...pbrProps,
748
+ }, sortInfo.item);
749
+ if (usesCustomShadowMaterial && customShadowMaterial?.visible !== false) {
750
+ appendShadowOnlyBillboardMesh(object, camera, meshes, groupOrder, clippingContext, localClippingEnabled, customShadowMaterial, material, materialContext, positions, indices, uvs);
751
+ }
752
+ invokeObjectRenderCallback(object.onAfterRender, 'onAfterRender', callbackContext, object, camera, object.geometry, material);
753
+ }
754
+ function spriteBillboardExpansion(object, matrix, worldPosition, center, scaleX, scaleY, rotation, sizeAttenuation, axes, camera, cache) {
755
+ const signature = spriteBillboardSignature(matrix, center, scaleX, scaleY, rotation, sizeAttenuation, axes, camera);
756
+ if (cache) {
757
+ const cached = cache.spriteBillboards.get(object);
758
+ if (cached && sameSpriteBillboardSignature(cached.signature, signature)) {
759
+ return cached.expansion;
760
+ }
761
+ }
762
+ const expansion = readSpriteBillboardExpansion(worldPosition, center, scaleX, scaleY, rotation, axes);
763
+ if (cache) {
764
+ cache.spriteBillboards.set(object, { signature, expansion });
765
+ }
766
+ return expansion;
767
+ }
768
+ function readSpriteBillboardExpansion(worldPosition, center, scaleX, scaleY, rotation, axes) {
769
+ const cos = Math.cos(rotation);
770
+ const sin = Math.sin(rotation);
771
+ const corners = [
772
+ [-0.5, -0.5, 0, 0],
773
+ [0.5, -0.5, 1, 0],
774
+ [0.5, 0.5, 1, 1],
775
+ [-0.5, 0.5, 0, 1],
776
+ ];
777
+ const positions = [];
778
+ const uvs = [];
779
+ for (const [x, y, u, v] of corners) {
780
+ const alignedX = (x - (center[0] - 0.5)) * scaleX;
781
+ const alignedY = (y - (center[1] - 0.5)) * scaleY;
782
+ const rotatedX = cos * alignedX - sin * alignedY;
783
+ const rotatedY = sin * alignedX + cos * alignedY;
784
+ positions.push(worldPosition[0] + axes.right[0] * rotatedX + axes.up[0] * rotatedY, worldPosition[1] + axes.right[1] * rotatedX + axes.up[1] * rotatedY, worldPosition[2] + axes.right[2] * rotatedX + axes.up[2] * rotatedY);
785
+ uvs.push(u, v);
786
+ }
787
+ return {
788
+ positions,
789
+ indices: [0, 1, 2, 0, 2, 3],
790
+ uvs,
791
+ };
792
+ }
793
+ function spriteBillboardSignature(matrix, center, scaleX, scaleY, rotation, sizeAttenuation, axes, camera) {
794
+ return {
795
+ matrix: matrix.slice(0, 16),
796
+ center: center.slice(),
797
+ scaleX,
798
+ scaleY,
799
+ rotation,
800
+ sizeAttenuation,
801
+ cameraRight: axes.right.slice(),
802
+ cameraUp: axes.up.slice(),
803
+ cameraView: matrixValues(camera?.matrixWorldInverse?.elements),
804
+ cameraIsPerspective: camera?.isPerspectiveCamera,
805
+ };
806
+ }
807
+ function sameSpriteBillboardSignature(a, b) {
808
+ return sameNumberArray(a.matrix, b.matrix)
809
+ && sameNumberArray(a.center, b.center)
810
+ && a.scaleX === b.scaleX
811
+ && a.scaleY === b.scaleY
812
+ && a.rotation === b.rotation
813
+ && a.sizeAttenuation === b.sizeAttenuation
814
+ && sameNumberArray(a.cameraRight, b.cameraRight)
815
+ && sameNumberArray(a.cameraUp, b.cameraUp)
816
+ && sameOptionalNumberArray(a.cameraView, b.cameraView)
817
+ && a.cameraIsPerspective === b.cameraIsPerspective;
818
+ }
819
+ function validateSpriteScale(object) {
820
+ finiteMaterialOrObjectNumber(object.scale?.x, 'Sprite.scale.x', 1);
821
+ finiteMaterialOrObjectNumber(object.scale?.y, 'Sprite.scale.y', 1);
822
+ finiteMaterialOrObjectNumber(object.scale?.z, 'Sprite.scale.z', 1);
823
+ }
824
+ function appendPoints(object, camera, meshes, groupOrder, viewportHeight, clippingContext, localClippingEnabled, shadowMaterialMode, materialContext, overrideMaterial, cache, callbackContext) {
825
+ const objectCastsShadow = optionalObjectBoolean(object.castShadow, 'object.castShadow') === true;
826
+ optionalObjectBoolean(object.receiveShadow, 'object.receiveShadow');
827
+ const geometry = object.geometry;
828
+ const geometryExtraction = meshGeometryExtraction(geometry, cache);
829
+ if (!geometryExtraction)
830
+ return;
831
+ const { position, positions, vertexColors, index, sourceIndex, groups, instancedGeometryCount, instancedPositionOffset, instancedPositionScale, } = geometryExtraction;
832
+ const pointUvChannels = geometryExtraction.uvChannels;
833
+ const transform = (0, math_1.matrixElements)(object.matrixWorld, 'points.matrixWorld');
834
+ const axes = cameraBillboardAxes(camera);
835
+ for (const group of groups) {
836
+ const material = materialForObjectGroup(object, group.materialIndex, overrideMaterial);
837
+ if (material?.visible === false)
838
+ continue;
839
+ invokeObjectRenderCallback(object.onBeforeRender, 'onBeforeRender', callbackContext, object, camera, geometry, material, group);
840
+ const pointUvStreams = material?.map || material?.alphaMap
841
+ ? textureUvStreamsForMapAlphaMaterial(pointUvChannels, {
842
+ map: material.map,
843
+ alphaMap: material.alphaMap,
844
+ })
845
+ : null;
846
+ const baseColor = (0, materials_1.materialColor)(material, materialContext);
847
+ const useVertexColors = vertexColors && material?.vertexColors !== false;
848
+ const pointSize = positiveMaterialOrObjectNumber(material?.size, 'material.size', 1);
849
+ const sizeAttenuation = optionalSceneBoolean(material?.sizeAttenuation, 'material.sizeAttenuation');
850
+ const billboard = pointBillboardExpansion(object, group, position, positions, index ?? sourceIndex, instancedGeometryCount, instancedPositionOffset, instancedPositionScale, transform, axes, camera, viewportHeight, pointSize, sizeAttenuation, pointUvStreams, vertexColors, useVertexColors ? baseColor : undefined, cache);
851
+ if (billboard.positions.length === 0)
852
+ continue;
853
+ const outputPositions = billboard.positions;
854
+ const outputUvs = billboard.uvs;
855
+ const outputUvs2 = billboard.uvs2;
856
+ const outputColors = billboard.colors;
857
+ const outputIndices = billboard.indices;
858
+ const outputPointRefs = billboard.pointRefs;
859
+ const textureInfo = (0, materials_1.extractTextureData)(material, materialContext);
860
+ const sortInfo = sortInfoForObject(object, material, camera, meshes.length, groupOrder, undefined, geometry, group);
861
+ const pbrProps = (0, materials_1.extractPbrProperties)(material, materialContext);
862
+ assertSupportedCustomFragmentInstancedAttributes(geometry, pbrProps);
863
+ pbrProps.alphaMapUsesUv2 = pointUvStreams?.alphaMapUsesUv2 ?? false;
864
+ const clipping = clippingState(clippingContext, material, localClippingEnabled);
865
+ const customShadowMaterial = customShadowMaterialForMode(object, shadowMaterialMode);
866
+ const usesCustomShadowMaterial = objectCastsShadow && customShadowMaterial != null;
867
+ const effectiveCustomShadowMaterial = usesCustomShadowMaterial
868
+ ? shadowMaterialWithSourceShadowState(customShadowMaterial, material)
869
+ : null;
870
+ const pointShadowUvStreams = effectiveCustomShadowMaterial && (effectiveCustomShadowMaterial.map ||
871
+ effectiveCustomShadowMaterial.alphaMap)
872
+ ? textureUvStreamsForMapAlphaMaterial(pointUvChannels, {
873
+ map: effectiveCustomShadowMaterial.map,
874
+ alphaMap: effectiveCustomShadowMaterial.alphaMap,
875
+ })
876
+ : null;
877
+ pushMesh(meshes, {
878
+ positions: outputPositions,
879
+ indices: outputIndices,
880
+ uvs: outputUvs,
881
+ uvs2: outputUvs2,
882
+ color: baseColor,
883
+ colors: outputColors,
884
+ texture: textureInfo?.data,
885
+ textureWidth: textureInfo?.width ?? undefined,
886
+ textureHeight: textureInfo?.height ?? undefined,
887
+ textureWrapS: textureInfo?.wrapS,
888
+ textureWrapT: textureInfo?.wrapT,
889
+ textureMagFilter: textureInfo?.magFilter,
890
+ textureMinFilter: textureInfo?.minFilter,
891
+ textureAnisotropy: textureInfo?.anisotropy,
892
+ textureTransform: textureInfo?.transform,
893
+ textureColorSpace: textureInfo?.colorSpace,
894
+ textureUsesUv2: pointUvStreams?.textureUsesUv2 ?? false,
895
+ transform: math_1.IDENTITY_4X4.slice(),
896
+ transparent: material?.transparent === true || (material?.opacity != null && material.opacity < 1),
897
+ topology: 'triangles',
898
+ castShadow: objectCastsShadow && !usesCustomShadowMaterial ? true : undefined,
899
+ receiveShadow: false,
900
+ clipShadows: clipShadowsForMaterial(material, clippingContext),
901
+ ...clipping,
902
+ ...sortInfo.keys,
903
+ ...pbrProps,
904
+ shadingModel: 'basic',
905
+ }, sortInfo.item);
906
+ if (usesCustomShadowMaterial && customShadowMaterial?.visible !== false) {
907
+ appendShadowOnlyBillboardMesh(object, camera, meshes, groupOrder, clippingContext, localClippingEnabled, customShadowMaterial, material, materialContext, outputPositions, outputIndices, pointShadowUvStreams?.uvs ? expandPointBillboardUvStream(pointShadowUvStreams.uvs, outputPointRefs) : outputUvs, pointShadowUvStreams?.uvs2 ? expandPointBillboardUvStream(pointShadowUvStreams.uvs2, outputPointRefs) : undefined, pointShadowUvStreams?.textureUsesUv2 ?? false, pointShadowUvStreams?.alphaMapUsesUv2 ?? false);
908
+ }
909
+ invokeObjectRenderCallback(object.onAfterRender, 'onAfterRender', callbackContext, object, camera, geometry, material, group);
910
+ }
911
+ }
912
+ function pointBillboardExpansion(object, group, position, positions, index, instancedGeometryCount, instancedPositionOffset, instancedPositionScale, transform, axes, camera, viewportHeight, pointSize, sizeAttenuation, pointUvStreams, vertexColors, vertexColorBase, cache) {
913
+ const signature = pointBillboardSignature(group, position, positions, index, instancedGeometryCount, instancedPositionOffset, instancedPositionScale, transform, axes, camera, viewportHeight, pointSize, sizeAttenuation, pointUvStreams, vertexColors, vertexColorBase);
914
+ const cacheKey = `${group.start}:${group.count}:${group.materialIndex ?? 0}`;
915
+ if (cache && signature.cacheable) {
916
+ const objectCache = cache.pointBillboards.get(object);
917
+ const cached = objectCache?.get(cacheKey);
918
+ if (cached && samePointBillboardSignature(cached.signature, signature)) {
919
+ return cached.expansion;
920
+ }
921
+ }
922
+ const expansion = readPointBillboardExpansion(group, position, positions, index, instancedGeometryCount, instancedPositionOffset, instancedPositionScale, transform, axes, camera, viewportHeight, pointSize, sizeAttenuation, pointUvStreams, vertexColors, vertexColorBase);
923
+ if (cache && signature.cacheable) {
924
+ let objectCache = cache.pointBillboards.get(object);
925
+ if (!objectCache) {
926
+ objectCache = new Map();
927
+ cache.pointBillboards.set(object, objectCache);
928
+ }
929
+ objectCache.set(cacheKey, { signature, expansion });
930
+ }
931
+ return expansion;
932
+ }
933
+ function readPointBillboardExpansion(group, position, positions, index, instancedGeometryCount, instancedPositionOffset, instancedPositionScale, transform, axes, camera, viewportHeight, pointSize, sizeAttenuation, pointUvStreams, vertexColors, vertexColorBase) {
934
+ const source = index ?? rangeIndices(position.count);
935
+ const points = source.slice(group.start, group.start + group.count);
936
+ const outputPositions = [];
937
+ const outputUvs = [];
938
+ const outputUvs2 = pointUvStreams?.uvs2 ? [] : undefined;
939
+ const outputColors = vertexColorBase ? [] : undefined;
940
+ const outputIndices = [];
941
+ const outputPointRefs = [];
942
+ const corners = [
943
+ [-0.5, -0.5, 0, 0],
944
+ [0.5, -0.5, 1, 0],
945
+ [0.5, 0.5, 1, 1],
946
+ [-0.5, 0.5, 0, 1],
947
+ ];
948
+ for (let instance = 0; instance < instancedGeometryCount; instance += 1) {
949
+ const offsetIndex = instancedPositionOffset
950
+ ? instancedAttributeIndex(instancedPositionOffset.attribute, instance, instancedPositionOffset.label)
951
+ : 0;
952
+ const offset = instancedPositionOffset
953
+ ? [
954
+ (0, attributes_1.attributeComponent)(instancedPositionOffset.attribute, offsetIndex, 0, instancedPositionOffset.label),
955
+ (0, attributes_1.attributeComponent)(instancedPositionOffset.attribute, offsetIndex, 1, instancedPositionOffset.label),
956
+ (0, attributes_1.attributeComponent)(instancedPositionOffset.attribute, offsetIndex, 2, instancedPositionOffset.label),
957
+ ]
958
+ : [0, 0, 0];
959
+ const scale = instanceScaleComponents(instancedPositionScale, instance);
960
+ for (let pointOffset = 0; pointOffset < points.length; pointOffset += 1) {
961
+ const pointIndex = points[pointOffset];
962
+ if (!Number.isInteger(pointIndex) || pointIndex < 0 || pointIndex >= position.count)
963
+ continue;
964
+ const center = transformPoint(transform, [
965
+ positions[pointIndex * 3] * scale[0] + offset[0],
966
+ positions[pointIndex * 3 + 1] * scale[1] + offset[1],
967
+ positions[pointIndex * 3 + 2] * scale[2] + offset[2],
968
+ ]);
969
+ const worldSize = pointWorldSize(pointSize, center, sizeAttenuation, camera, viewportHeight);
970
+ if (worldSize <= 0)
971
+ continue;
972
+ const vertexBase = outputPositions.length / 3;
973
+ outputPointRefs.push({ pointIndex, instance });
974
+ const pointColor = outputColors ? pointVertexColor(vertexColors, vertexColorBase, pointIndex, instance) : null;
975
+ for (const [x, y, u, v] of corners) {
976
+ outputPositions.push(center[0] + axes.right[0] * x * worldSize + axes.up[0] * y * worldSize, center[1] + axes.right[1] * x * worldSize + axes.up[1] * y * worldSize, center[2] + axes.right[2] * x * worldSize + axes.up[2] * y * worldSize);
977
+ if (pointUvStreams) {
978
+ if (pointUvStreams.uvs) {
979
+ appendUvForVertex(outputUvs, pointUvStreams.uvs, pointIndex, instance);
980
+ }
981
+ else {
982
+ outputUvs.push(u, v);
983
+ }
984
+ if (outputUvs2 && pointUvStreams.uvs2) {
985
+ appendUvForVertex(outputUvs2, pointUvStreams.uvs2, pointIndex, instance);
986
+ }
987
+ }
988
+ else {
989
+ outputUvs.push(u, v);
990
+ }
991
+ if (pointColor) {
992
+ outputColors.push(pointColor[0], pointColor[1], pointColor[2], pointColor[3]);
993
+ }
994
+ }
995
+ outputIndices.push(vertexBase, vertexBase + 1, vertexBase + 2, vertexBase, vertexBase + 2, vertexBase + 3);
996
+ }
997
+ }
998
+ return {
999
+ positions: outputPositions,
1000
+ indices: outputIndices,
1001
+ uvs: outputUvs,
1002
+ uvs2: outputUvs2,
1003
+ colors: outputColors,
1004
+ pointRefs: outputPointRefs,
1005
+ };
1006
+ }
1007
+ function pointBillboardSignature(group, position, positions, index, instancedGeometryCount, instancedPositionOffset, instancedPositionScale, transform, axes, camera, viewportHeight, pointSize, sizeAttenuation, pointUvStreams, vertexColors, vertexColorBase) {
1008
+ const signature = {
1009
+ cacheable: true,
1010
+ positions,
1011
+ positionCount: position.count,
1012
+ index,
1013
+ groupStart: group.start,
1014
+ groupCount: group.count,
1015
+ instancedGeometryCount,
1016
+ instancedPositionOffset: attributeSignature(instancedPositionOffset?.attribute),
1017
+ instancedPositionScale: attributeSignature(instancedPositionScale?.attribute),
1018
+ transform: transform.slice(0, 16),
1019
+ cameraRight: axes.right.slice(),
1020
+ cameraUp: axes.up.slice(),
1021
+ cameraProjection: matrixValues(camera?.projectionMatrix?.elements),
1022
+ cameraView: matrixValues(camera?.matrixWorldInverse?.elements),
1023
+ cameraIsPerspective: camera?.isPerspectiveCamera,
1024
+ viewportHeight,
1025
+ pointSize,
1026
+ sizeAttenuation,
1027
+ uvs: uvChannelSignature(pointUvStreams?.uvs),
1028
+ uvs2: uvChannelSignature(pointUvStreams?.uvs2),
1029
+ useVertexColors: !!vertexColorBase,
1030
+ vertexColors: attributeSignature(vertexColorBase ? vertexColors : undefined),
1031
+ baseColor: vertexColorBase ? vertexColorBase.slice() : undefined,
1032
+ };
1033
+ signature.cacheable = pointBillboardSignatureCacheable(signature);
1034
+ return signature;
1035
+ }
1036
+ function pointBillboardSignatureCacheable(signature) {
1037
+ return attributeSignatureCacheable(signature.instancedPositionOffset)
1038
+ && attributeSignatureCacheable(signature.instancedPositionScale)
1039
+ && attributeSignatureCacheable(signature.uvs.attribute)
1040
+ && attributeSignatureCacheable(signature.uvs2.attribute)
1041
+ && attributeSignatureCacheable(signature.vertexColors);
1042
+ }
1043
+ function uvChannelSignature(channel) {
1044
+ if (!channel)
1045
+ return { attribute: {} };
1046
+ return {
1047
+ attribute: attributeSignature(channel.attribute),
1048
+ values: channel.values,
1049
+ label: channel.label,
1050
+ };
1051
+ }
1052
+ function matrixValues(matrix) {
1053
+ if (!matrix || matrix.length < 16)
1054
+ return null;
1055
+ const out = new Array(16);
1056
+ for (let i = 0; i < 16; i += 1)
1057
+ out[i] = matrix[i];
1058
+ return out;
1059
+ }
1060
+ function samePointBillboardSignature(a, b) {
1061
+ return a.cacheable === b.cacheable
1062
+ && a.positions === b.positions
1063
+ && a.positionCount === b.positionCount
1064
+ && a.index === b.index
1065
+ && a.groupStart === b.groupStart
1066
+ && a.groupCount === b.groupCount
1067
+ && a.instancedGeometryCount === b.instancedGeometryCount
1068
+ && sameAttributeSignature(a.instancedPositionOffset, b.instancedPositionOffset)
1069
+ && sameAttributeSignature(a.instancedPositionScale, b.instancedPositionScale)
1070
+ && sameNumberArray(a.transform, b.transform)
1071
+ && sameNumberArray(a.cameraRight, b.cameraRight)
1072
+ && sameNumberArray(a.cameraUp, b.cameraUp)
1073
+ && sameOptionalNumberArray(a.cameraProjection, b.cameraProjection)
1074
+ && sameOptionalNumberArray(a.cameraView, b.cameraView)
1075
+ && a.cameraIsPerspective === b.cameraIsPerspective
1076
+ && a.viewportHeight === b.viewportHeight
1077
+ && a.pointSize === b.pointSize
1078
+ && a.sizeAttenuation === b.sizeAttenuation
1079
+ && sameUvChannelSignature(a.uvs, b.uvs)
1080
+ && sameUvChannelSignature(a.uvs2, b.uvs2)
1081
+ && a.useVertexColors === b.useVertexColors
1082
+ && sameAttributeSignature(a.vertexColors, b.vertexColors)
1083
+ && sameOptionalNumberArray(a.baseColor, b.baseColor);
1084
+ }
1085
+ function sameUvChannelSignature(a, b) {
1086
+ return sameAttributeSignature(a.attribute, b.attribute)
1087
+ && a.values === b.values
1088
+ && a.label === b.label;
1089
+ }
1090
+ function sameOptionalNumberArray(a, b) {
1091
+ if (a == null || b == null)
1092
+ return a == null && b == null;
1093
+ return sameNumberArray(a, b);
1094
+ }
1095
+ function sameNumberArray(a, b) {
1096
+ if (a.length !== b.length)
1097
+ return false;
1098
+ for (let i = 0; i < a.length; i += 1) {
1099
+ if (!Object.is(a[i], b[i]))
1100
+ return false;
1101
+ }
1102
+ return true;
1103
+ }
1104
+ function expandPointBillboardUvStream(channel, pointRefs) {
1105
+ const outputUvs = [];
1106
+ for (const { pointIndex, instance } of pointRefs) {
1107
+ for (let corner = 0; corner < 4; corner += 1) {
1108
+ appendUvForVertex(outputUvs, channel, pointIndex, instance);
1109
+ }
1110
+ }
1111
+ return outputUvs;
1112
+ }
1113
+ function appendShadowOnlyBillboardMesh(object, camera, meshes, groupOrder, clippingContext, localClippingEnabled, material, sourceMaterial, materialContext, positions, indices, uvs, uvs2 = undefined, textureUsesUv2 = false, alphaMapUsesUv2 = false) {
1114
+ const shadowMaterial = shadowMaterialWithSourceShadowState(material, sourceMaterial);
1115
+ const textureInfo = (0, materials_1.extractTextureData)(shadowMaterial, materialContext);
1116
+ const sortInfo = sortInfoForObject(object, material, camera, meshes.length, groupOrder);
1117
+ const clipping = clippingState(clippingContext, shadowMaterial, localClippingEnabled);
1118
+ const hiddenMainPass = shadowOnlyMainPassState();
1119
+ const shadowProps = shadowPbrProperties(shadowMaterial, sourceMaterial, materialContext);
1120
+ shadowProps.alphaMapUsesUv2 = alphaMapUsesUv2;
1121
+ pushMesh(meshes, {
1122
+ positions,
1123
+ indices,
1124
+ uvs,
1125
+ uvs2,
1126
+ color: (0, materials_1.materialColor)(shadowMaterial, materialContext),
1127
+ texture: textureInfo?.data,
1128
+ textureWidth: textureInfo?.width ?? undefined,
1129
+ textureHeight: textureInfo?.height ?? undefined,
1130
+ textureWrapS: textureInfo?.wrapS,
1131
+ textureWrapT: textureInfo?.wrapT,
1132
+ textureMagFilter: textureInfo?.magFilter,
1133
+ textureMinFilter: textureInfo?.minFilter,
1134
+ textureAnisotropy: textureInfo?.anisotropy,
1135
+ textureTransform: textureInfo?.transform,
1136
+ textureColorSpace: textureInfo?.colorSpace,
1137
+ textureUsesUv2,
1138
+ transform: math_1.IDENTITY_4X4.slice(),
1139
+ topology: 'triangles',
1140
+ castShadow: true,
1141
+ receiveShadow: false,
1142
+ clipShadows: clipShadowsForMaterial(shadowMaterial, clippingContext),
1143
+ ...clipping,
1144
+ ...sortInfo.keys,
1145
+ ...shadowProps,
1146
+ ...hiddenMainPass,
1147
+ }, sortInfo.item);
1148
+ }
1149
+ function shadowMaterialWithSourceShadowState(material, sourceMaterial) {
1150
+ if (!sourceMaterialHasShadowState(sourceMaterial))
1151
+ return material;
1152
+ const shadowMaterial = Object.create(material);
1153
+ if (sourceMaterialHasShadowAlphaState(sourceMaterial)) {
1154
+ shadowMaterial.map = sourceMaterial.map ?? null;
1155
+ shadowMaterial.alphaMap = sourceMaterial.alphaMap ?? null;
1156
+ shadowMaterial.alphaTest = sourceMaterial.alphaToCoverage === true ? 0.5 : sourceMaterial.alphaTest;
1157
+ shadowMaterial.alphaToCoverage = sourceMaterial.alphaToCoverage;
1158
+ if (sourceMaterial.alphaHash === true)
1159
+ shadowMaterial.alphaHash = true;
1160
+ if (sourceMaterial.alphaHash === true || sourceMaterial.alphaToCoverage === true) {
1161
+ if (sourceMaterial.opacity != null) {
1162
+ shadowMaterial.opacity = sourceMaterial.opacity;
1163
+ }
116
1164
  }
117
1165
  }
1166
+ if (sourceMaterialHasShadowDisplacementState(sourceMaterial)) {
1167
+ shadowMaterial.displacementMap = sourceMaterial.displacementMap ?? null;
1168
+ shadowMaterial.displacementScale = sourceMaterial.displacementScale;
1169
+ shadowMaterial.displacementBias = sourceMaterial.displacementBias;
1170
+ }
1171
+ if (sourceMaterialHasShadowClippingState(sourceMaterial)) {
1172
+ shadowMaterial.clipShadows = sourceMaterial.clipShadows;
1173
+ shadowMaterial.clippingPlanes = sourceMaterial.clippingPlanes;
1174
+ shadowMaterial.clipIntersection = sourceMaterial.clipIntersection;
1175
+ }
1176
+ if (sourceMaterialHasShadowWireframeState(sourceMaterial)) {
1177
+ shadowMaterial.wireframe = sourceMaterial.wireframe;
1178
+ }
1179
+ return shadowMaterial;
1180
+ }
1181
+ function sourceMaterialHasShadowState(material) {
1182
+ return sourceMaterialHasShadowAlphaState(material) ||
1183
+ sourceMaterialHasShadowDisplacementState(material) ||
1184
+ sourceMaterialHasShadowClippingState(material) ||
1185
+ sourceMaterialHasShadowWireframeState(material);
1186
+ }
1187
+ function sourceMaterialHasShadowAlphaState(material) {
1188
+ if (!material)
1189
+ return false;
1190
+ const hasAlphaTexture = !!(material.map || material.alphaMap);
1191
+ const hasOpacityAlpha = typeof material.opacity === 'number' && Number.isFinite(material.opacity) && material.opacity < 1;
1192
+ if (material.alphaHash === true && (hasAlphaTexture || hasOpacityAlpha))
1193
+ return true;
1194
+ if (material.alphaToCoverage === true && (hasAlphaTexture || hasOpacityAlpha))
1195
+ return true;
1196
+ if (!hasAlphaTexture)
1197
+ return false;
1198
+ if (material.alphaToCoverage === true)
1199
+ return true;
1200
+ return typeof material.alphaTest === 'number' && Number.isFinite(material.alphaTest) && material.alphaTest > 0;
1201
+ }
1202
+ function sourceMaterialHasShadowDisplacementState(material) {
1203
+ return !!material?.displacementMap;
1204
+ }
1205
+ function sourceMaterialHasShadowClippingState(material) {
1206
+ return !!material && ('clipShadows' in material ||
1207
+ material.clippingPlanes != null ||
1208
+ 'clipIntersection' in material);
1209
+ }
1210
+ function sourceMaterialHasShadowWireframeState(material) {
1211
+ return material?.wireframe === true;
1212
+ }
1213
+ function shadowPbrProperties(material, sourceMaterial, materialContext) {
1214
+ const props = (0, materials_1.extractPbrProperties)(material, materialContext);
1215
+ const sourceShadowSide = (0, materials_1.materialShadowSide)(sourceMaterial);
1216
+ if (sourceShadowSide) {
1217
+ props.shadowSide = sourceShadowSide;
1218
+ }
1219
+ return props;
1220
+ }
1221
+ function validateObjectShadowFlags(object) {
1222
+ optionalObjectBoolean(object.castShadow, 'object.castShadow');
1223
+ optionalObjectBoolean(object.receiveShadow, 'object.receiveShadow');
1224
+ }
1225
+ function optionalObjectBoolean(value, label) {
1226
+ return optionalSceneBoolean(value, label);
1227
+ }
1228
+ function optionalSceneBoolean(value, label) {
1229
+ if (value == null)
1230
+ return undefined;
1231
+ if (typeof value === 'boolean')
1232
+ return value;
1233
+ throw new TypeError(`${label} must be a boolean.`);
1234
+ }
1235
+ function pointVertexColor(attribute, materialColor, pointIndex, instanceIndex, label = 'geometry.attributes.color') {
1236
+ const sourceIndex = isInstancedAttribute(attribute)
1237
+ ? instancedAttributeIndex(attribute, instanceIndex, label)
1238
+ : pointIndex;
1239
+ return [
1240
+ (0, math_1.clamp01)((0, attributes_1.attributeComponent)(attribute, sourceIndex, 0, label) * materialColor[0]),
1241
+ (0, math_1.clamp01)((0, attributes_1.attributeComponent)(attribute, sourceIndex, 1, label) * materialColor[1]),
1242
+ (0, math_1.clamp01)((0, attributes_1.attributeComponent)(attribute, sourceIndex, 2, label) * materialColor[2]),
1243
+ (0, math_1.clamp01)((attribute.itemSize && attribute.itemSize >= 4 ? (0, attributes_1.attributeComponent)(attribute, sourceIndex, 3, label) : 1) * materialColor[3]),
1244
+ ];
118
1245
  }
119
1246
  function effectiveGroups(geometry, index, vertexCount) {
120
1247
  const range = geometry.drawRange ?? {};
1248
+ if (range != null && (typeof range !== 'object' || Array.isArray(range))) {
1249
+ throw new TypeError('geometry.drawRange must be an object.');
1250
+ }
121
1251
  const maxCount = index ? index.length : vertexCount;
122
- const drawStart = (0, math_1.clampInteger)(range.start ?? 0, 0, maxCount);
123
- const requestedCount = range.count == null || range.count === Infinity ? maxCount : range.count;
1252
+ const drawStart = (0, math_1.clampInteger)(geometryDrawRangeStart(range.start), 0, maxCount);
1253
+ const requestedCount = geometryDrawRangeCount(range.count, maxCount);
124
1254
  const drawEnd = (0, math_1.clampInteger)(drawStart + requestedCount, drawStart, maxCount);
1255
+ if (geometry.groups != null && !Array.isArray(geometry.groups)) {
1256
+ throw new TypeError('geometry.groups must be an array.');
1257
+ }
125
1258
  const sourceGroups = Array.isArray(geometry.groups) && geometry.groups.length
126
1259
  ? geometry.groups
127
- : [{ start: drawStart, count: drawEnd - drawStart, materialIndex: 0 }];
1260
+ : null;
1261
+ if (!sourceGroups) {
1262
+ return [{ start: drawStart, count: drawEnd - drawStart, materialIndex: 0 }];
1263
+ }
128
1264
  const groups = [];
129
- for (const group of sourceGroups) {
130
- const start = Math.max(drawStart, (0, math_1.clampInteger)(group.start ?? 0, 0, maxCount));
131
- const end = Math.min(drawEnd, (0, math_1.clampInteger)((group.start ?? 0) + (group.count ?? 0), 0, maxCount));
1265
+ for (let groupIndex = 0; groupIndex < sourceGroups.length; groupIndex += 1) {
1266
+ const group = sourceGroups[groupIndex];
1267
+ if (!group || typeof group !== 'object' || Array.isArray(group)) {
1268
+ throw new TypeError(`geometry.groups[${groupIndex}] must be an object.`);
1269
+ }
1270
+ const groupStart = geometryGroupNonNegativeInteger(group.start, `geometry.groups[${groupIndex}].start`);
1271
+ const groupCount = geometryGroupNonNegativeInteger(group.count, `geometry.groups[${groupIndex}].count`);
1272
+ const groupMaterialIndex = group.materialIndex == null
1273
+ ? 0
1274
+ : geometryGroupNonNegativeInteger(group.materialIndex, `geometry.groups[${groupIndex}].materialIndex`);
1275
+ const start = Math.max(drawStart, (0, math_1.clampInteger)(groupStart, 0, maxCount));
1276
+ const end = Math.min(drawEnd, (0, math_1.clampInteger)(groupStart + groupCount, 0, maxCount));
132
1277
  if (end > start) {
133
1278
  groups.push({
134
1279
  start,
135
1280
  count: end - start,
136
- materialIndex: group.materialIndex ?? 0,
1281
+ materialIndex: groupMaterialIndex,
137
1282
  });
138
1283
  }
139
1284
  }
140
1285
  return groups;
141
1286
  }
1287
+ function geometryDrawRangeStart(value) {
1288
+ if (value == null)
1289
+ return 0;
1290
+ return geometryGroupNonNegativeInteger(value, 'geometry.drawRange.start');
1291
+ }
1292
+ function geometryDrawRangeCount(value, fallback) {
1293
+ if (value == null || value === Infinity)
1294
+ return fallback;
1295
+ return geometryGroupNonNegativeInteger(value, 'geometry.drawRange.count');
1296
+ }
1297
+ function geometryGroupNonNegativeInteger(value, label) {
1298
+ if (typeof value === 'number' && Number.isFinite(value) && Number.isInteger(value) && value >= 0) {
1299
+ return value;
1300
+ }
1301
+ throw new TypeError(`${label} must be a non-negative integer.`);
1302
+ }
142
1303
  /**
143
1304
  * Emit a `NativeSceneMesh` with `topology: 'lines'` or `'points'` for
144
1305
  * `THREE.Line` / `THREE.LineSegments` / `THREE.LineLoop` / `THREE.Points`.
145
1306
  * Lines are always expanded to a LineList (pairs of vertex indices) so the
146
1307
  * Rust side only has to deal with one line topology.
147
1308
  */
148
- function appendLineOrPoints(object, meshes, topology) {
1309
+ function appendLineOrPoints(object, camera, meshes, topology, groupOrder, viewportHeight, clippingContext, localClippingEnabled, materialContext, overrideMaterial, cache, callbackContext) {
1310
+ validateObjectShadowFlags(object);
1311
+ const objectCastsShadow = topology === 'lines' && optionalObjectBoolean(object.castShadow, 'object.castShadow') === true;
149
1312
  const geometry = object.geometry;
150
- const position = (0, attributes_1.getAttribute)(geometry, 'position');
151
- if (!position)
152
- return;
153
- const material = (0, materials_1.materialForGroup)(object.material, 0);
154
- if (material?.visible === false)
1313
+ const geometryExtraction = meshGeometryExtraction(geometry, cache);
1314
+ if (!geometryExtraction)
155
1315
  return;
156
- const positions = (0, attributes_1.readVec3Attribute)(position);
157
- const vertexColors = (0, attributes_1.getAttribute)(geometry, 'color');
158
- const indexAttr = geometry.index ? (0, attributes_1.readIndexAttribute)(geometry.index) : null;
1316
+ const { position, positions, uvChannels, uvs, vertexColors, index, sourceIndex, groups, instancedGeometryCount, instancedPositionOffset, instancedPositionScale, } = geometryExtraction;
159
1317
  const vertexCount = position.count;
160
- const range = geometry.drawRange ?? {};
161
- const sourceCount = indexAttr ? indexAttr.length : vertexCount;
162
- const drawStart = (0, math_1.clampInteger)(range.start ?? 0, 0, sourceCount);
163
- const requestedCount = range.count == null || range.count === Infinity ? sourceCount : range.count;
164
- const drawEnd = (0, math_1.clampInteger)(drawStart + requestedCount, drawStart, sourceCount);
165
- let indices = null;
166
- if (topology === 'lines') {
167
- const source = indexAttr ?? rangeIndices(vertexCount);
168
- indices = expandLineIndices(source, drawStart, drawEnd, object);
169
- if (indices.length < 2)
170
- return;
171
- }
172
- else if (indexAttr) {
173
- indices = indexAttr.slice(drawStart, drawEnd);
174
- if (indices.length === 0)
175
- return;
176
- }
177
- const color = (0, materials_1.materialColor)(material);
178
- const useVertexColors = vertexColors && material?.vertexColors !== false;
179
- const colors = useVertexColors ? (0, attributes_1.readColorAttribute)(vertexColors, color) : undefined;
1318
+ const indexAttr = index;
1319
+ for (const group of groups) {
1320
+ const material = materialForObjectGroup(object, group.materialIndex, overrideMaterial);
1321
+ if (material?.visible === false)
1322
+ continue;
1323
+ invokeObjectRenderCallback(object.onBeforeRender, 'onBeforeRender', callbackContext, object, camera, geometry, material, group);
1324
+ const uvStreams = topology === 'lines'
1325
+ ? textureUvStreamsForMapAlphaMaterial(uvChannels, material)
1326
+ : { uvs: uvChannels[0], uvs2: secondaryUvsForMaterial(uvChannels, material) };
1327
+ let indices = null;
1328
+ let outputPositions = positions;
1329
+ let outputUvs = topology === 'lines' ? uvStreams.uvs?.values : undefined;
1330
+ let outputSecondaryUvs = topology === 'lines' ? uvStreams.uvs2?.values : undefined;
1331
+ let outputColors;
1332
+ let thickCenter;
1333
+ const color = (0, materials_1.materialColor)(material, materialContext);
1334
+ const useVertexColors = vertexColors && material?.vertexColors !== false;
1335
+ const pbrProps = (0, materials_1.extractPbrProperties)(material, materialContext);
1336
+ assertSupportedCustomFragmentInstancedAttributes(geometry, pbrProps);
1337
+ if (topology === 'lines') {
1338
+ pbrProps.alphaMapUsesUv2 = uvStreams.alphaMapUsesUv2;
1339
+ }
1340
+ const textureInfo = (0, materials_1.extractTextureData)(material, materialContext);
1341
+ const drawStart = group.start;
1342
+ const drawEnd = group.start + group.count;
1343
+ if (topology === 'lines') {
1344
+ validateLineMaterialCompatibilityHints(material);
1345
+ }
1346
+ const lineWidth = positiveMaterialOrObjectNumber(material?.linewidth, 'material.linewidth', 1);
1347
+ const thickLine = topology === 'lines' && lineWidth > 1;
1348
+ if (topology === 'lines') {
1349
+ const source = sourceIndex;
1350
+ if (material?.isLineDashedMaterial === true) {
1351
+ const lineDistance = (0, attributes_1.getAttribute)(geometry, 'lineDistance');
1352
+ const dashed = instancedGeometryCount > 1 || instancedPositionOffset || instancedPositionScale
1353
+ ? dashedLineAttributesForInstancesWithCache(cache, geometry, position, positions, uvStreams.uvs, uvStreams.uvs2, useVertexColors ? vertexColors : undefined, color, source, drawStart, drawEnd, object, lineDistance, material, instancedGeometryCount, instancedPositionOffset, instancedPositionScale)
1354
+ : dashedLineAttributesWithCache(cache, geometry, position, positions, uvStreams.uvs, uvStreams.uvs2, useVertexColors ? (0, attributes_1.readColorAttribute)(vertexColors, color, 'geometry.attributes.color') : undefined, source, drawStart, drawEnd, object, lineDistance, material);
1355
+ if (dashed.positions.length < 6)
1356
+ continue;
1357
+ outputPositions = dashed.positions;
1358
+ outputUvs = dashed.uvs;
1359
+ outputSecondaryUvs = dashed.uvs2;
1360
+ outputColors = dashed.colors;
1361
+ indices = rangeIndices(dashed.positions.length / 3);
1362
+ if (thickLine) {
1363
+ const transform = (0, math_1.matrixElements)(object.matrixWorld, 'object.matrixWorld');
1364
+ const thick = thickLineAttributes(outputPositions, outputUvs, outputSecondaryUvs, outputColors, indices, transform, camera, viewportHeight, lineWidth);
1365
+ if (thick.positions.length < 12)
1366
+ continue;
1367
+ outputPositions = thick.positions;
1368
+ outputUvs = thick.uvs;
1369
+ outputSecondaryUvs = thick.uvs2;
1370
+ outputColors = thick.colors;
1371
+ indices = thick.indices;
1372
+ thickCenter = thick.center;
1373
+ }
1374
+ else {
1375
+ indices = null;
1376
+ }
1377
+ }
1378
+ else {
1379
+ indices = expandLineIndices(source, drawStart, drawEnd, object);
1380
+ if (indices.length < 2)
1381
+ continue;
1382
+ if (instancedGeometryCount > 1 || instancedPositionOffset || instancedPositionScale) {
1383
+ outputPositions = expandVec3ValuesForInstancesWithCache(cache, geometry, position, positions, 0, vertexCount, instancedGeometryCount, instancedPositionOffset, instancedPositionScale);
1384
+ outputUvs = uvStreams.uvs ? expandUvChannelForInstancesWithCache(cache, geometry, uvStreams.uvs, 0, vertexCount, instancedGeometryCount) : undefined;
1385
+ outputSecondaryUvs = uvStreams.uvs2 ? expandUvChannelForInstancesWithCache(cache, geometry, uvStreams.uvs2, 0, vertexCount, instancedGeometryCount) : undefined;
1386
+ indices = expandIndicesForInstances(indices, vertexCount, instancedGeometryCount);
1387
+ }
1388
+ if (thickLine) {
1389
+ const transform = (0, math_1.matrixElements)(object.matrixWorld, 'object.matrixWorld');
1390
+ const thick = thickLineAttributes(outputPositions, outputUvs, outputSecondaryUvs, useVertexColors ? outputColors ?? expandColorAttributeForInstancesWithCache(cache, geometry, vertexColors, color, 0, vertexCount, instancedGeometryCount) : undefined, indices, transform, camera, viewportHeight, lineWidth);
1391
+ if (thick.positions.length < 12)
1392
+ continue;
1393
+ outputPositions = thick.positions;
1394
+ outputUvs = thick.uvs;
1395
+ outputSecondaryUvs = thick.uvs2;
1396
+ outputColors = thick.colors;
1397
+ indices = thick.indices;
1398
+ thickCenter = thick.center;
1399
+ }
1400
+ }
1401
+ }
1402
+ else if (indexAttr) {
1403
+ indices = indexAttr.slice(drawStart, drawEnd);
1404
+ if (indices.length === 0)
1405
+ continue;
1406
+ }
1407
+ if (useVertexColors && material?.isLineDashedMaterial !== true && !thickLine) {
1408
+ outputColors = expandColorAttributeForInstancesWithCache(cache, geometry, vertexColors, color, 0, vertexCount, instancedGeometryCount);
1409
+ }
1410
+ const sortInfo = sortInfoForObject(object, material, camera, meshes.length, groupOrder, undefined, geometry, group);
1411
+ if (thickCenter && camera) {
1412
+ sortInfo.keys.sortZ = projectedWorldPointZ(thickCenter, camera);
1413
+ sortInfo.item.z = sortInfo.keys.sortZ;
1414
+ }
1415
+ const clipping = clippingState(clippingContext, material, localClippingEnabled);
1416
+ pushMesh(meshes, {
1417
+ positions: outputPositions,
1418
+ indices: indices ?? undefined,
1419
+ uvs: topology === 'lines' ? outputUvs : undefined,
1420
+ uvs2: topology === 'lines' ? outputSecondaryUvs : undefined,
1421
+ color,
1422
+ colors: outputColors,
1423
+ texture: textureInfo?.data,
1424
+ textureWidth: textureInfo?.width ?? undefined,
1425
+ textureHeight: textureInfo?.height ?? undefined,
1426
+ textureWrapS: textureInfo?.wrapS,
1427
+ textureWrapT: textureInfo?.wrapT,
1428
+ textureMagFilter: textureInfo?.magFilter,
1429
+ textureMinFilter: textureInfo?.minFilter,
1430
+ textureAnisotropy: textureInfo?.anisotropy,
1431
+ textureTransform: textureInfo?.transform,
1432
+ textureColorSpace: textureInfo?.colorSpace,
1433
+ textureUsesUv2: topology === 'lines' ? uvStreams.textureUsesUv2 : textureInfo?.usesUv2,
1434
+ transform: thickLine ? math_1.IDENTITY_4X4.slice() : (0, math_1.matrixElements)(object.matrixWorld, 'object.matrixWorld'),
1435
+ transparent: material?.transparent === true || (material?.opacity != null && material.opacity < 1),
1436
+ alphaTest: material && Number.isFinite(material.alphaTest) && material.alphaTest > 0 ? material.alphaTest : undefined,
1437
+ clipShadows: clipShadowsForMaterial(material, clippingContext),
1438
+ ...pbrProps,
1439
+ ...(thickLine ? { side: 'double' } : {}),
1440
+ shadingModel: 'basic',
1441
+ topology: thickLine ? 'triangles' : topology,
1442
+ castShadow: objectCastsShadow ? true : undefined,
1443
+ ...clipping,
1444
+ ...sortInfo.keys,
1445
+ }, sortInfo.item);
1446
+ invokeObjectRenderCallback(object.onAfterRender, 'onAfterRender', callbackContext, object, camera, geometry, material, group);
1447
+ }
1448
+ }
1449
+ function thickLineAttributes(positions, uvs, uvs2, colors, lineIndices, transform, camera, viewportHeight, lineWidth) {
1450
+ const axes = cameraBillboardAxes(camera);
1451
+ const outputPositions = [];
1452
+ const outputUvs = uvs ? [] : undefined;
1453
+ const outputUvs2 = uvs2 ? [] : undefined;
1454
+ const outputColors = colors ? [] : undefined;
1455
+ const outputIndices = [];
1456
+ for (let i = 0; i + 1 < lineIndices.length; i += 2) {
1457
+ const aIndex = lineIndices[i];
1458
+ const bIndex = lineIndices[i + 1];
1459
+ if (!validPositionIndex(positions, aIndex) || !validPositionIndex(positions, bIndex))
1460
+ continue;
1461
+ const a = transformPoint(transform, [
1462
+ positions[aIndex * 3],
1463
+ positions[aIndex * 3 + 1],
1464
+ positions[aIndex * 3 + 2],
1465
+ ]);
1466
+ const b = transformPoint(transform, [
1467
+ positions[bIndex * 3],
1468
+ positions[bIndex * 3 + 1],
1469
+ positions[bIndex * 3 + 2],
1470
+ ]);
1471
+ const dx = b[0] - a[0];
1472
+ const dy = b[1] - a[1];
1473
+ const dz = b[2] - a[2];
1474
+ if (Math.hypot(dx, dy, dz) <= 1e-8)
1475
+ continue;
1476
+ const screenDx = dx * axes.right[0] + dy * axes.right[1] + dz * axes.right[2];
1477
+ const screenDy = dx * axes.up[0] + dy * axes.up[1] + dz * axes.up[2];
1478
+ const side = normalizeVec3([
1479
+ -screenDy * axes.right[0] + screenDx * axes.up[0],
1480
+ -screenDy * axes.right[1] + screenDx * axes.up[1],
1481
+ -screenDy * axes.right[2] + screenDx * axes.up[2],
1482
+ ], axes.up);
1483
+ const midpoint = [
1484
+ (a[0] + b[0]) * 0.5,
1485
+ (a[1] + b[1]) * 0.5,
1486
+ (a[2] + b[2]) * 0.5,
1487
+ ];
1488
+ const halfWidth = linePixelWorldSize(lineWidth, midpoint, camera, viewportHeight) * 0.5;
1489
+ if (halfWidth <= 0)
1490
+ continue;
1491
+ const vertexBase = outputPositions.length / 3;
1492
+ pushThickLineVertex(outputPositions, a, side, -halfWidth);
1493
+ pushThickLineVertex(outputPositions, a, side, halfWidth);
1494
+ pushThickLineVertex(outputPositions, b, side, halfWidth);
1495
+ pushThickLineVertex(outputPositions, b, side, -halfWidth);
1496
+ outputIndices.push(vertexBase, vertexBase + 1, vertexBase + 2, vertexBase, vertexBase + 2, vertexBase + 3);
1497
+ pushRepeatedVec2(outputUvs, uvs, aIndex);
1498
+ pushRepeatedVec2(outputUvs, uvs, aIndex);
1499
+ pushRepeatedVec2(outputUvs, uvs, bIndex);
1500
+ pushRepeatedVec2(outputUvs, uvs, bIndex);
1501
+ pushRepeatedVec2(outputUvs2, uvs2, aIndex);
1502
+ pushRepeatedVec2(outputUvs2, uvs2, aIndex);
1503
+ pushRepeatedVec2(outputUvs2, uvs2, bIndex);
1504
+ pushRepeatedVec2(outputUvs2, uvs2, bIndex);
1505
+ pushRepeatedColor(outputColors, colors, aIndex);
1506
+ pushRepeatedColor(outputColors, colors, aIndex);
1507
+ pushRepeatedColor(outputColors, colors, bIndex);
1508
+ pushRepeatedColor(outputColors, colors, bIndex);
1509
+ }
1510
+ return {
1511
+ center: thickLineCenter(outputPositions),
1512
+ colors: outputColors,
1513
+ indices: outputIndices,
1514
+ positions: outputPositions,
1515
+ uvs: outputUvs,
1516
+ uvs2: outputUvs2,
1517
+ };
1518
+ }
1519
+ function validPositionIndex(positions, index) {
1520
+ return Number.isInteger(index) && index >= 0 && index * 3 + 2 < positions.length;
1521
+ }
1522
+ function pushThickLineVertex(positions, point, side, offset) {
1523
+ positions.push(point[0] + side[0] * offset, point[1] + side[1] * offset, point[2] + side[2] * offset);
1524
+ }
1525
+ function pushRepeatedVec2(target, source, index) {
1526
+ if (!target || !source || index * 2 + 1 >= source.length)
1527
+ return;
1528
+ target.push(source[index * 2], source[index * 2 + 1]);
1529
+ }
1530
+ function pushRepeatedColor(target, source, index) {
1531
+ if (!target || !source || index * 4 + 3 >= source.length)
1532
+ return;
1533
+ target.push(source[index * 4], source[index * 4 + 1], source[index * 4 + 2], source[index * 4 + 3]);
1534
+ }
1535
+ function linePixelWorldSize(lineWidth, worldPosition, camera, viewportHeight) {
1536
+ const projectionY = Math.abs(finiteOrDefault(camera?.projectionMatrix?.elements?.[5], 1));
1537
+ if (projectionY <= 0)
1538
+ return 0;
1539
+ if (camera?.isPerspectiveCamera === true) {
1540
+ const viewZ = viewSpaceZ(worldPosition, camera);
1541
+ const depth = Number.isFinite(viewZ) ? Math.max(0.0001, Math.abs(viewZ)) : 1;
1542
+ return lineWidth * 2 * depth / Math.max(1, viewportHeight) / projectionY;
1543
+ }
1544
+ return lineWidth * 2 / Math.max(1, viewportHeight) / projectionY;
1545
+ }
1546
+ function thickLineCenter(positions) {
1547
+ if (positions.length < 3)
1548
+ return [0, 0, 0];
1549
+ let x = 0;
1550
+ let y = 0;
1551
+ let z = 0;
1552
+ let count = 0;
1553
+ for (let i = 0; i + 2 < positions.length; i += 3) {
1554
+ x += positions[i];
1555
+ y += positions[i + 1];
1556
+ z += positions[i + 2];
1557
+ count += 1;
1558
+ }
1559
+ return count > 0 ? [x / count, y / count, z / count] : [0, 0, 0];
1560
+ }
1561
+ function clippingState(clippingContext, material, localClippingEnabled) {
1562
+ const inheritedUnionPlanes = clippingContext.unionPlanes.slice(0, clipping_1.MAX_CLIPPING_PLANES);
1563
+ const inheritedIntersectionPlanes = clippingContext.intersectionPlanes.slice(0, Math.max(0, clipping_1.MAX_CLIPPING_PLANES - inheritedUnionPlanes.length));
1564
+ const localBudget = Math.max(0, clipping_1.MAX_CLIPPING_PLANES - inheritedUnionPlanes.length - inheritedIntersectionPlanes.length);
1565
+ const localPlanes = localClippingEnabled
1566
+ ? (0, clipping_1.extractClippingPlanes)(material?.clippingPlanes, 'material.clippingPlanes', localBudget)
1567
+ : [];
1568
+ const clipIntersection = optionalObjectBoolean(material?.clipIntersection, 'material.clipIntersection') === true;
1569
+ const localUnionPlanes = clipIntersection ? [] : localPlanes;
1570
+ const localIntersectionPlanes = clipIntersection ? localPlanes : [];
1571
+ const unionPlanes = [...inheritedUnionPlanes, ...localUnionPlanes];
1572
+ const intersectionPlanes = [...inheritedIntersectionPlanes, ...localIntersectionPlanes];
1573
+ const planes = [...unionPlanes, ...intersectionPlanes];
1574
+ if (planes.length === 0)
1575
+ return {};
1576
+ return {
1577
+ clippingPlanes: (0, clipping_1.flattenClippingPlanes)(planes),
1578
+ clippingUnionCount: unionPlanes.length,
1579
+ };
1580
+ }
1581
+ function clippingContextForObject(parent, object) {
1582
+ if (object.isClippingGroup !== true)
1583
+ return parent;
1584
+ if (optionalObjectBoolean(object.enabled, 'ClippingGroup.enabled') === false)
1585
+ return parent;
1586
+ const clipIntersection = optionalObjectBoolean(object.clipIntersection, 'ClippingGroup.clipIntersection') === true;
1587
+ const clipShadows = parent.clipShadows || optionalObjectBoolean(object.clipShadows, 'ClippingGroup.clipShadows') === true;
1588
+ const currentCount = parent.unionPlanes.length + parent.intersectionPlanes.length;
1589
+ const remainingBudget = Math.max(0, clipping_1.MAX_CLIPPING_PLANES - currentCount);
1590
+ const planes = (0, clipping_1.extractClippingPlanes)(object.clippingPlanes, 'ClippingGroup.clippingPlanes', remainingBudget);
1591
+ if (planes.length === 0)
1592
+ return parent;
1593
+ return clipIntersection
1594
+ ? {
1595
+ unionPlanes: parent.unionPlanes,
1596
+ intersectionPlanes: [...parent.intersectionPlanes, ...planes],
1597
+ clipShadows,
1598
+ }
1599
+ : {
1600
+ unionPlanes: [...parent.unionPlanes, ...planes],
1601
+ intersectionPlanes: parent.intersectionPlanes,
1602
+ clipShadows,
1603
+ };
1604
+ }
1605
+ function clipShadowsForMaterial(material, clippingContext) {
1606
+ return optionalObjectBoolean(material?.clipShadows, 'material.clipShadows') === true || clippingContext.clipShadows ? true : undefined;
1607
+ }
1608
+ function pushMesh(meshes, mesh, sortItem, transparentDoubleSidePass = false) {
1609
+ const groupOrder = mesh.groupOrder ?? 0;
1610
+ const renderOrder = mesh.renderOrder ?? 0;
180
1611
  meshes.push({
181
- positions,
182
- indices: indices ?? undefined,
183
- color,
184
- colors,
185
- transform: (0, math_1.matrixElements)(object.matrixWorld, 'object.matrixWorld'),
186
- transparent: material?.transparent === true || (material?.opacity != null && material.opacity < 1),
187
- alphaTest: material && Number.isFinite(material.alphaTest) && material.alphaTest > 0 ? material.alphaTest : undefined,
188
- shadingModel: 'basic',
189
- topology,
1612
+ mesh: {
1613
+ ...mesh,
1614
+ groupOrder: nativeRenderOrderKey(groupOrder),
1615
+ renderOrder: nativeRenderOrderKey(renderOrder),
1616
+ },
1617
+ sortItem,
1618
+ groupOrder,
1619
+ renderOrder,
1620
+ sortZ: mesh.sortZ ?? 0,
1621
+ materialSortKey: mesh.materialSortKey ?? 0,
1622
+ materialVariant: mesh.materialVariant ?? 0,
1623
+ sortIndex: mesh.sortIndex ?? meshes.length,
1624
+ transparentDoubleSidePass,
1625
+ });
1626
+ }
1627
+ function needsTransparentDoubleSidePass(material, props, wireframe) {
1628
+ return !wireframe
1629
+ && material?.transparent === true
1630
+ && material.forceSinglePass === false
1631
+ && props.side === 'double';
1632
+ }
1633
+ function nativeMeshesFromSortedFlattened(meshes) {
1634
+ const nativeMeshes = [];
1635
+ for (const { mesh, transparentDoubleSidePass } of meshes) {
1636
+ if (transparentDoubleSidePass) {
1637
+ nativeMeshes.push({ ...mesh, side: 'back' });
1638
+ nativeMeshes.push({ ...mesh, side: 'front' });
1639
+ }
1640
+ else {
1641
+ nativeMeshes.push(mesh);
1642
+ }
1643
+ }
1644
+ return nativeMeshes;
1645
+ }
1646
+ function sortInfoForObject(object, material, camera, sortIndex, groupOrder, transform, geometry, group, sortItemObject, sortItemZOverride) {
1647
+ const renderOrder = renderOrderNumber(object.renderOrder, 'object.renderOrder', 0);
1648
+ const z = camera ? projectedObjectZ(object, camera, transform) : 0;
1649
+ const itemZ = sortItemZOverride ?? z;
1650
+ const id = unsignedSortKey(object.id, sortIndex);
1651
+ const materialSortKey = finiteOrDefault(material?.id, 0);
1652
+ const materialVariant = materialVariantForObject(object);
1653
+ return {
1654
+ keys: {
1655
+ groupOrder,
1656
+ renderOrder,
1657
+ sortZ: z,
1658
+ sortIndex: id,
1659
+ materialSortKey,
1660
+ materialVariant,
1661
+ },
1662
+ item: {
1663
+ id,
1664
+ object: sortItemObject ?? object,
1665
+ geometry,
1666
+ material,
1667
+ group,
1668
+ groupOrder,
1669
+ renderOrder,
1670
+ z: itemZ,
1671
+ materialVariant,
1672
+ },
1673
+ };
1674
+ }
1675
+ function mergeSortKeys(keys, override) {
1676
+ return override ? { ...keys, ...override } : keys;
1677
+ }
1678
+ function sortFlattenedMeshes(meshes, options) {
1679
+ const sortObjects = options.sortObjects !== false;
1680
+ const buckets = partitionFlattenedMeshes(meshes);
1681
+ if (options.opaque === false)
1682
+ buckets.opaque.length = 0;
1683
+ if (options.transparent === false) {
1684
+ buckets.transmissive.length = 0;
1685
+ buckets.transparent.length = 0;
1686
+ }
1687
+ if (!sortObjects) {
1688
+ normalizeSortKeys(buckets.opaque);
1689
+ normalizeSortKeys(buckets.transmissive);
1690
+ normalizeSortKeys(buckets.transparent);
1691
+ return [...buckets.opaque, ...buckets.transmissive, ...buckets.transparent];
1692
+ }
1693
+ if (options.opaqueSort) {
1694
+ buckets.opaque.sort(compareWithSort(options.opaqueSort));
1695
+ normalizeSortKeys(buckets.opaque);
1696
+ }
1697
+ else {
1698
+ buckets.opaque.sort(compareFlattenedMeshes);
1699
+ }
1700
+ if (options.transparentSort) {
1701
+ const transparentSort = compareWithSort(options.transparentSort);
1702
+ buckets.transmissive.sort(transparentSort);
1703
+ buckets.transparent.sort(transparentSort);
1704
+ normalizeSortKeys(buckets.transmissive);
1705
+ normalizeSortKeys(buckets.transparent);
1706
+ }
1707
+ else {
1708
+ buckets.transmissive.sort(compareTransparentFlattenedMeshes);
1709
+ buckets.transparent.sort(compareTransparentFlattenedMeshes);
1710
+ }
1711
+ return [...buckets.opaque, ...buckets.transmissive, ...buckets.transparent];
1712
+ }
1713
+ function partitionFlattenedMeshes(meshes) {
1714
+ const opaque = [];
1715
+ const transmissive = [];
1716
+ const transparent = [];
1717
+ for (const mesh of meshes) {
1718
+ if (finitePositive(mesh.mesh.transmission)) {
1719
+ transmissive.push(mesh);
1720
+ }
1721
+ else if (meshDefaultsTransparent(mesh.mesh)) {
1722
+ transparent.push(mesh);
1723
+ }
1724
+ else {
1725
+ opaque.push(mesh);
1726
+ }
1727
+ }
1728
+ return { opaque, transmissive, transparent };
1729
+ }
1730
+ function compareWithSort(sort) {
1731
+ return (a, b) => {
1732
+ const result = Number(sort(a.sortItem, b.sortItem));
1733
+ return Number.isFinite(result) ? result : 0;
1734
+ };
1735
+ }
1736
+ function normalizeSortKeys(meshes) {
1737
+ meshes.forEach((entry, index) => {
1738
+ entry.mesh.groupOrder = 0;
1739
+ entry.mesh.renderOrder = 0;
1740
+ entry.mesh.materialSortKey = 0;
1741
+ entry.mesh.materialVariant = 0;
1742
+ entry.mesh.sortZ = 0;
1743
+ entry.mesh.sortIndex = index;
190
1744
  });
191
1745
  }
1746
+ function compareFlattenedMeshes(a, b) {
1747
+ return a.groupOrder - b.groupOrder
1748
+ || a.renderOrder - b.renderOrder
1749
+ || a.materialSortKey - b.materialSortKey
1750
+ || a.materialVariant - b.materialVariant
1751
+ || a.sortZ - b.sortZ
1752
+ || a.sortIndex - b.sortIndex;
1753
+ }
1754
+ function compareTransparentFlattenedMeshes(a, b) {
1755
+ return a.groupOrder - b.groupOrder
1756
+ || a.renderOrder - b.renderOrder
1757
+ || b.sortZ - a.sortZ
1758
+ || a.sortIndex - b.sortIndex;
1759
+ }
1760
+ function meshDefaultsTransparent(mesh) {
1761
+ if (mesh.alphaHash === true)
1762
+ return false;
1763
+ if (mesh.transparent === true)
1764
+ return true;
1765
+ if (mesh.transparent === false)
1766
+ return false;
1767
+ return materialAlpha(mesh) < 0.999;
1768
+ }
1769
+ function materialAlpha(mesh) {
1770
+ return mesh.color && mesh.color.length >= 4 ? finiteOrDefault(mesh.color[3], 1) : 1;
1771
+ }
1772
+ function finitePositive(value) {
1773
+ return typeof value === 'number' && Number.isFinite(value) && value > 0.0001;
1774
+ }
1775
+ function materialVariantForObject(object) {
1776
+ return (object.isInstancedMesh === true ? 2 : 0) + (object.isSkinnedMesh === true ? 1 : 0);
1777
+ }
1778
+ function projectedObjectZ(object, camera, transform) {
1779
+ const world = transform ?? object.matrixWorld?.elements;
1780
+ if (!world || world.length < 16)
1781
+ return 0;
1782
+ const view = camera.matrixWorldInverse?.elements;
1783
+ const projection = camera.projectionMatrix?.elements;
1784
+ if (!view || view.length < 16 || !projection || projection.length < 16)
1785
+ return 0;
1786
+ const center = objectSortCenter(object);
1787
+ const x = world[0] * center[0] + world[4] * center[1] + world[8] * center[2] + world[12];
1788
+ const y = world[1] * center[0] + world[5] * center[1] + world[9] * center[2] + world[13];
1789
+ const z = world[2] * center[0] + world[6] * center[1] + world[10] * center[2] + world[14];
1790
+ const vx = view[0] * x + view[4] * y + view[8] * z + view[12];
1791
+ const vy = view[1] * x + view[5] * y + view[9] * z + view[13];
1792
+ const vz = view[2] * x + view[6] * y + view[10] * z + view[14];
1793
+ const vw = view[3] * x + view[7] * y + view[11] * z + view[15];
1794
+ const clipZ = projection[2] * vx + projection[6] * vy + projection[10] * vz + projection[14] * vw;
1795
+ const clipW = projection[3] * vx + projection[7] * vy + projection[11] * vz + projection[15] * vw;
1796
+ return clipW === 0 ? clipZ : clipZ / clipW;
1797
+ }
1798
+ function projectedWorldPointZ(worldPoint, camera) {
1799
+ const view = camera.matrixWorldInverse?.elements;
1800
+ const projection = camera.projectionMatrix?.elements;
1801
+ if (!view || view.length < 16 || !projection || projection.length < 16)
1802
+ return 0;
1803
+ const x = worldPoint[0];
1804
+ const y = worldPoint[1];
1805
+ const z = worldPoint[2];
1806
+ const vx = view[0] * x + view[4] * y + view[8] * z + view[12];
1807
+ const vy = view[1] * x + view[5] * y + view[9] * z + view[13];
1808
+ const vz = view[2] * x + view[6] * y + view[10] * z + view[14];
1809
+ const vw = view[3] * x + view[7] * y + view[11] * z + view[15];
1810
+ const clipZ = projection[2] * vx + projection[6] * vy + projection[10] * vz + projection[14] * vw;
1811
+ const clipW = projection[3] * vx + projection[7] * vy + projection[11] * vz + projection[15] * vw;
1812
+ return clipW === 0 ? clipZ : clipZ / clipW;
1813
+ }
1814
+ function objectSortCenter(object) {
1815
+ if (object.boundingSphere !== undefined) {
1816
+ if (object.boundingSphere == null && typeof object.computeBoundingSphere === 'function') {
1817
+ try {
1818
+ object.computeBoundingSphere();
1819
+ }
1820
+ catch {
1821
+ return [0, 0, 0];
1822
+ }
1823
+ }
1824
+ if (object.boundingSphere == null)
1825
+ return [0, 0, 0];
1826
+ if (!object.boundingSphere || typeof object.boundingSphere !== 'object') {
1827
+ throw new TypeError('object.boundingSphere must be a THREE.Sphere-like object.');
1828
+ }
1829
+ return requiredVec3Like(object.boundingSphere.center, 'object.boundingSphere.center');
1830
+ }
1831
+ const geometry = object.geometry;
1832
+ if (!geometry)
1833
+ return [0, 0, 0];
1834
+ if (geometry.boundingSphere == null && typeof geometry.computeBoundingSphere === 'function') {
1835
+ try {
1836
+ geometry.computeBoundingSphere();
1837
+ }
1838
+ catch {
1839
+ return [0, 0, 0];
1840
+ }
1841
+ }
1842
+ const sphere = geometry.boundingSphere;
1843
+ if (sphere == null)
1844
+ return [0, 0, 0];
1845
+ if (!sphere || typeof sphere !== 'object') {
1846
+ throw new TypeError('geometry.boundingSphere must be a THREE.Sphere-like object.');
1847
+ }
1848
+ return requiredVec3Like(sphere.center, 'geometry.boundingSphere.center');
1849
+ }
1850
+ function renderableObjectOutsideFrustum(object, camera, viewportHeight = 512, overrideMaterial) {
1851
+ if (!camera)
1852
+ return false;
1853
+ const frustumCulled = optionalObjectBoolean(object.frustumCulled, 'object.frustumCulled');
1854
+ if (frustumCulled === false)
1855
+ return false;
1856
+ const sphere = objectBoundingSphere(object);
1857
+ if (!sphere)
1858
+ return false;
1859
+ const transform = (0, math_1.matrixElements)(object.matrixWorld, renderableMatrixWorldLabel(object));
1860
+ const extraRadius = object.isPoints === true
1861
+ ? pointBillboardCullRadius(object, camera, transform, sphere, viewportHeight, overrideMaterial)
1862
+ : 0;
1863
+ return transformedSphereOutsideFrustum(camera, transform, sphere, extraRadius);
1864
+ }
1865
+ function validateInstancedMeshMatrices(object) {
1866
+ const instanceMatrix = object.instanceMatrix;
1867
+ if (!instanceMatrix || instanceMatrix.count == null)
1868
+ return;
1869
+ const instanceMatrixCount = (0, attributes_1.attributeCount)(instanceMatrix, 'InstancedMesh.instanceMatrix');
1870
+ const count = (0, math_1.clampInteger)(integerCountOrDefault(object.count, 'InstancedMesh.count', instanceMatrixCount), 0, instanceMatrixCount);
1871
+ for (let i = 0; i < count; i += 1) {
1872
+ readMat4Attribute(instanceMatrix, i);
1873
+ }
1874
+ }
1875
+ function renderableMatrixWorldLabel(object) {
1876
+ if (object.isBatchedMesh === true)
1877
+ return 'batchedMesh.matrixWorld';
1878
+ if (object.isMesh === true)
1879
+ return 'mesh.matrixWorld';
1880
+ if (object.isPoints === true)
1881
+ return 'points.matrixWorld';
1882
+ return 'object.matrixWorld';
1883
+ }
1884
+ function objectBoundingSphere(object) {
1885
+ if (object.isBatchedMesh === true) {
1886
+ if (object.boundingSphere != null)
1887
+ return sphereLike(object.boundingSphere, 'object.boundingSphere');
1888
+ return batchedMeshBoundingSphere(object);
1889
+ }
1890
+ if (object.boundingSphere !== undefined) {
1891
+ const hadBoundingSphere = object.boundingSphere != null;
1892
+ if (object.boundingSphere == null && typeof object.computeBoundingSphere === 'function') {
1893
+ if (object.isInstancedMesh === true) {
1894
+ validateInstancedMeshMatrices(object);
1895
+ }
1896
+ try {
1897
+ object.computeBoundingSphere();
1898
+ }
1899
+ catch {
1900
+ return null;
1901
+ }
1902
+ }
1903
+ if (object.boundingSphere == null)
1904
+ return null;
1905
+ try {
1906
+ return object.isInstancedMesh === true
1907
+ ? instancedMeshBoundingSphere(object)
1908
+ : sphereLike(object.boundingSphere, 'object.boundingSphere');
1909
+ }
1910
+ catch (error) {
1911
+ if (!hadBoundingSphere)
1912
+ return null;
1913
+ throw error;
1914
+ }
1915
+ }
1916
+ const geometry = object.geometry;
1917
+ if (!geometry)
1918
+ return null;
1919
+ const hadBoundingSphere = geometry.boundingSphere != null;
1920
+ if (geometry.boundingSphere == null && typeof geometry.computeBoundingSphere === 'function') {
1921
+ try {
1922
+ geometry.computeBoundingSphere();
1923
+ }
1924
+ catch {
1925
+ return null;
1926
+ }
1927
+ }
1928
+ if (geometry.boundingSphere == null)
1929
+ return null;
1930
+ try {
1931
+ return sphereLike(geometry.boundingSphere, 'geometry.boundingSphere');
1932
+ }
1933
+ catch (error) {
1934
+ if (!hadBoundingSphere)
1935
+ return null;
1936
+ throw error;
1937
+ }
1938
+ }
1939
+ function batchedMeshBoundingSphere(object) {
1940
+ const geometry = object.geometry;
1941
+ if (!geometry)
1942
+ return null;
1943
+ const instanceInfo = object._instanceInfo;
1944
+ if (!Array.isArray(instanceInfo)) {
1945
+ throw new Error('THREE.BatchedMesh instance table is not readable. Use a real THREE.BatchedMesh or expand the batch to ordinary Mesh or InstancedMesh objects before rendering.');
1946
+ }
1947
+ let bounds = null;
1948
+ for (let instanceId = 0; instanceId < instanceInfo.length; instanceId += 1) {
1949
+ const info = instanceInfo[instanceId];
1950
+ if (!info || typeof info !== 'object') {
1951
+ throw new TypeError(`THREE.BatchedMesh._instanceInfo[${instanceId}] must be an object.`);
1952
+ }
1953
+ if (!batchedOptionalBoolean(info.active, `THREE.BatchedMesh._instanceInfo[${instanceId}].active`, true))
1954
+ continue;
1955
+ batchedOptionalBoolean(info.visible, `THREE.BatchedMesh._instanceInfo[${instanceId}].visible`, true);
1956
+ const geometryId = batchedNonNegativeInteger(info.geometryIndex, `THREE.BatchedMesh._instanceInfo[${instanceId}].geometryIndex`);
1957
+ const range = batchedGeometryRange(object, geometry, geometryId);
1958
+ if (range.count === 0)
1959
+ continue;
1960
+ const sphere = batchedGeometryBoundingSphere(object, geometry, geometryId, range);
1961
+ if (!sphere)
1962
+ continue;
1963
+ bounds = unionSpheres(bounds, transformSphere(sphere, batchedInstanceMatrix(object, instanceId)));
1964
+ }
1965
+ return bounds;
1966
+ }
1967
+ function transformSphere(sphere, transform) {
1968
+ const center = transformPoint(transform, sphere.center);
1969
+ const scale = Math.max(columnLength3(transform, 0), columnLength3(transform, 4), columnLength3(transform, 8));
1970
+ return { center, radius: sphere.radius * scale };
1971
+ }
1972
+ function unionSpheres(a, b) {
1973
+ if (!a)
1974
+ return { center: [...b.center], radius: b.radius };
1975
+ const dx = b.center[0] - a.center[0];
1976
+ const dy = b.center[1] - a.center[1];
1977
+ const dz = b.center[2] - a.center[2];
1978
+ const distance = Math.hypot(dx, dy, dz);
1979
+ if (distance <= Math.abs(a.radius - b.radius)) {
1980
+ return a.radius >= b.radius ? a : { center: [...b.center], radius: b.radius };
1981
+ }
1982
+ if (distance === 0)
1983
+ return { center: [...a.center], radius: Math.max(a.radius, b.radius) };
1984
+ const radius = (distance + a.radius + b.radius) * 0.5;
1985
+ const centerShift = (radius - a.radius) / distance;
1986
+ return {
1987
+ center: [
1988
+ a.center[0] + dx * centerShift,
1989
+ a.center[1] + dy * centerShift,
1990
+ a.center[2] + dz * centerShift,
1991
+ ],
1992
+ radius,
1993
+ };
1994
+ }
1995
+ function instancedMeshBoundingSphere(object) {
1996
+ try {
1997
+ return sphereLike(object.boundingSphere, 'object.boundingSphere');
1998
+ }
1999
+ catch (error) {
2000
+ validateInstancedMeshMatrices(object);
2001
+ throw error;
2002
+ }
2003
+ }
2004
+ function pointBillboardCullRadius(object, camera, transform, sphere, viewportHeight, overrideMaterial) {
2005
+ const center = transformPoint(transform, sphere.center);
2006
+ let radius = 0;
2007
+ for (const material of pointCullMaterials(object, overrideMaterial)) {
2008
+ const pointSize = safePositiveNumber(material?.size, 1);
2009
+ const sizeAttenuation = typeof material?.sizeAttenuation === 'boolean'
2010
+ ? material.sizeAttenuation
2011
+ : undefined;
2012
+ const worldSize = pointWorldSize(pointSize, center, sizeAttenuation, camera, viewportHeight);
2013
+ radius = Math.max(radius, worldSize * Math.SQRT1_2);
2014
+ }
2015
+ return radius;
2016
+ }
2017
+ function pointCullMaterials(object, overrideMaterial) {
2018
+ if (overrideMaterial !== undefined)
2019
+ return [overrideMaterial];
2020
+ if (Array.isArray(object.material)) {
2021
+ return object.material.filter((material) => material != null && typeof material === 'object' && !Array.isArray(material));
2022
+ }
2023
+ return object.material != null && typeof object.material === 'object' && !Array.isArray(object.material)
2024
+ ? [object.material]
2025
+ : [undefined];
2026
+ }
2027
+ function spriteOutsideFrustum(object, camera, transform, center) {
2028
+ if (!camera)
2029
+ return false;
2030
+ const frustumCulled = optionalObjectBoolean(object.frustumCulled, 'object.frustumCulled');
2031
+ if (frustumCulled === false)
2032
+ return false;
2033
+ const offset = Math.hypot(center[0] - 0.5, center[1] - 0.5);
2034
+ return transformedSphereOutsideFrustum(camera, transform, {
2035
+ center: [0, 0, 0],
2036
+ radius: 0.7071067811865476 + offset,
2037
+ });
2038
+ }
2039
+ function transformedSphereOutsideFrustum(camera, transform, sphere, extraRadius = 0) {
2040
+ const center = transformPoint(transform, sphere.center);
2041
+ const scale = Math.max(columnLength3(transform, 0), columnLength3(transform, 4), columnLength3(transform, 8));
2042
+ const radius = sphere.radius * scale + extraRadius;
2043
+ if (!Number.isFinite(radius) || radius < 0)
2044
+ return false;
2045
+ return !cameraFrustumIntersectsSphere(camera, center, radius);
2046
+ }
2047
+ function vec3Like(value) {
2048
+ if (!value)
2049
+ return null;
2050
+ const objectValue = value;
2051
+ const x = typeof objectValue.x === 'number' ? objectValue.x : value[0];
2052
+ const y = typeof objectValue.y === 'number' ? objectValue.y : value[1];
2053
+ const z = typeof objectValue.z === 'number' ? objectValue.z : value[2];
2054
+ return typeof x === 'number' && Number.isFinite(x)
2055
+ && typeof y === 'number' && Number.isFinite(y)
2056
+ && typeof z === 'number' && Number.isFinite(z)
2057
+ ? [x, y, z]
2058
+ : null;
2059
+ }
2060
+ function requiredVec3Like(value, label) {
2061
+ const vector = vec3Like(value);
2062
+ if (vector)
2063
+ return vector;
2064
+ throw new TypeError(`${label} must be a finite Vector3-like value.`);
2065
+ }
2066
+ function columnLength3(matrix, start) {
2067
+ const x = matrix[start];
2068
+ const y = matrix[start + 1];
2069
+ const z = matrix[start + 2];
2070
+ return Math.hypot(x, y, z);
2071
+ }
2072
+ function cameraBillboardAxes(camera) {
2073
+ const matrix = camera?.matrixWorld?.elements;
2074
+ if (!matrix || matrix.length < 16) {
2075
+ return { right: [1, 0, 0], up: [0, 1, 0] };
2076
+ }
2077
+ return {
2078
+ right: normalizeVec3([matrix[0], matrix[1], matrix[2]], [1, 0, 0]),
2079
+ up: normalizeVec3([matrix[4], matrix[5], matrix[6]], [0, 1, 0]),
2080
+ };
2081
+ }
2082
+ function normalizeVec3(value, fallback) {
2083
+ const length = Math.hypot(value[0], value[1], value[2]);
2084
+ if (!Number.isFinite(length) || length <= 1e-8)
2085
+ return fallback;
2086
+ return [value[0] / length, value[1] / length, value[2] / length];
2087
+ }
2088
+ function viewSpaceZ(worldPosition, camera) {
2089
+ const view = camera.matrixWorldInverse?.elements;
2090
+ if (!view || view.length < 16)
2091
+ return Number.NaN;
2092
+ return view[2] * worldPosition[0] + view[6] * worldPosition[1] + view[10] * worldPosition[2] + view[14];
2093
+ }
2094
+ function transformPoint(matrix, point) {
2095
+ const x = point[0];
2096
+ const y = point[1];
2097
+ const z = point[2];
2098
+ return [
2099
+ matrix[0] * x + matrix[4] * y + matrix[8] * z + matrix[12],
2100
+ matrix[1] * x + matrix[5] * y + matrix[9] * z + matrix[13],
2101
+ matrix[2] * x + matrix[6] * y + matrix[10] * z + matrix[14],
2102
+ ];
2103
+ }
2104
+ function pointWorldSize(pointSize, worldPosition, sizeAttenuation, camera, viewportHeight) {
2105
+ const projectionY = Math.abs(finiteOrDefault(camera?.projectionMatrix?.elements?.[5], 1));
2106
+ if (projectionY <= 0)
2107
+ return 0;
2108
+ if (camera?.isPerspectiveCamera === true && sizeAttenuation !== false) {
2109
+ const viewZ = viewSpaceZ(worldPosition, camera);
2110
+ const depth = Number.isFinite(viewZ) ? Math.max(0.0001, Math.abs(viewZ)) : 1;
2111
+ const pixelSize = clampPointSpriteSize(pointSize * Math.max(1, viewportHeight) / (2 * depth));
2112
+ return pixelSize * 2 * depth / Math.max(1, viewportHeight) / projectionY;
2113
+ }
2114
+ const cappedPointSize = clampPointSpriteSize(pointSize);
2115
+ if (camera?.isPerspectiveCamera !== true) {
2116
+ return cappedPointSize * 2 / Math.max(1, viewportHeight) / projectionY;
2117
+ }
2118
+ const viewZ = camera ? viewSpaceZ(worldPosition, camera) : -1;
2119
+ const depth = Number.isFinite(viewZ) ? Math.max(0.0001, Math.abs(viewZ)) : 1;
2120
+ return cappedPointSize * 2 * depth / Math.max(1, viewportHeight) / projectionY;
2121
+ }
2122
+ function clampPointSpriteSize(pixelSize) {
2123
+ return Math.min(pixelSize, MAX_POINT_SPRITE_SIZE);
2124
+ }
2125
+ function finiteOrDefault(value, fallback) {
2126
+ return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
2127
+ }
2128
+ function safePositiveNumber(value, fallback) {
2129
+ return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;
2130
+ }
2131
+ function finiteMaterialOrObjectNumber(value, label, fallback) {
2132
+ if (value == null)
2133
+ return fallback;
2134
+ if (typeof value === 'number' && Number.isFinite(value))
2135
+ return value;
2136
+ throw new TypeError(`${label} must be a finite number.`);
2137
+ }
2138
+ function renderOrderNumber(value, label, fallback) {
2139
+ if (value == null)
2140
+ return fallback;
2141
+ if (typeof value === 'number' && (Number.isFinite(value) || value === Infinity || value === -Infinity)) {
2142
+ return value;
2143
+ }
2144
+ throw new TypeError(`${label} must be a finite number or +/-Infinity.`);
2145
+ }
2146
+ function nativeRenderOrderKey(value) {
2147
+ if (value === Infinity)
2148
+ return NativeRenderOrderLimit;
2149
+ if (value === -Infinity)
2150
+ return -NativeRenderOrderLimit;
2151
+ return value;
2152
+ }
2153
+ function nonNegativeMaterialOrObjectNumber(value, label, fallback) {
2154
+ const number = finiteMaterialOrObjectNumber(value, label, fallback);
2155
+ if (number < 0) {
2156
+ throw new TypeError(`${label} must be non-negative.`);
2157
+ }
2158
+ return number;
2159
+ }
2160
+ function positiveMaterialOrObjectNumber(value, label, fallback) {
2161
+ const number = finiteMaterialOrObjectNumber(value, label, fallback);
2162
+ if (number <= 0) {
2163
+ throw new TypeError(`${label} must be positive.`);
2164
+ }
2165
+ return number;
2166
+ }
2167
+ function validateLineMaterialCompatibilityHints(material) {
2168
+ if (!material)
2169
+ return;
2170
+ optionalLineCap(material.linecap);
2171
+ optionalLineJoin(material.linejoin);
2172
+ }
2173
+ function optionalLineCap(value) {
2174
+ if (value == null)
2175
+ return;
2176
+ if (typeof value !== 'string') {
2177
+ throw new TypeError('material.linecap must be a string.');
2178
+ }
2179
+ if (value !== 'butt' && value !== 'round' && value !== 'square') {
2180
+ throw new Error(`material.linecap ${JSON.stringify(value)} is not supported by @headless-three/renderer. Use "butt", "round", "square", null, or undefined.`);
2181
+ }
2182
+ }
2183
+ function optionalLineJoin(value) {
2184
+ if (value == null)
2185
+ return;
2186
+ if (typeof value !== 'string') {
2187
+ throw new TypeError('material.linejoin must be a string.');
2188
+ }
2189
+ if (value !== 'round' && value !== 'bevel' && value !== 'miter') {
2190
+ throw new Error(`material.linejoin ${JSON.stringify(value)} is not supported by @headless-three/renderer. Use "round", "bevel", "miter", null, or undefined.`);
2191
+ }
2192
+ }
2193
+ function normalizedMaterialOrObjectNumber(value, label, fallback) {
2194
+ const number = finiteMaterialOrObjectNumber(value, label, fallback);
2195
+ if (number < 0 || number > 1) {
2196
+ throw new TypeError(`${label} must be between 0 and 1.`);
2197
+ }
2198
+ return number;
2199
+ }
2200
+ function cameraZoomOrDefault(value) {
2201
+ if (value == null)
2202
+ return 1;
2203
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
2204
+ throw new TypeError('camera.zoom must be a finite number.');
2205
+ }
2206
+ if (value <= 0) {
2207
+ throw new TypeError('camera.zoom must be positive.');
2208
+ }
2209
+ return value;
2210
+ }
2211
+ function finiteCountOrDefault(value, label, fallback, allowInfinity = false) {
2212
+ if (value == null)
2213
+ return fallback;
2214
+ if (allowInfinity && value === Infinity)
2215
+ return value;
2216
+ if (typeof value === 'number' && Number.isFinite(value))
2217
+ return value;
2218
+ throw new TypeError(`${label} must be a finite number.`);
2219
+ }
2220
+ function integerCountOrDefault(value, label, fallback, allowInfinity = false) {
2221
+ const number = finiteCountOrDefault(value, label, fallback, allowInfinity);
2222
+ if (number === Infinity)
2223
+ return number;
2224
+ if (!Number.isInteger(number)) {
2225
+ throw new TypeError(`${label} must be an integer.`);
2226
+ }
2227
+ if (number < 0) {
2228
+ throw new TypeError(`${label} must be non-negative.`);
2229
+ }
2230
+ return number;
2231
+ }
2232
+ function unsignedSortKey(value, fallback) {
2233
+ return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : fallback;
2234
+ }
2235
+ function instancedBufferGeometryCount(geometry) {
2236
+ const attributes = Object.entries((0, attributes_1.geometryAttributes)(geometry));
2237
+ const instancedAttributes = attributes.filter((entry) => isInstancedAttribute(entry[1]));
2238
+ if (geometry.isInstancedBufferGeometry !== true && instancedAttributes.length === 0)
2239
+ return 1;
2240
+ let maxCount = Infinity;
2241
+ for (const [name, attribute] of instancedAttributes) {
2242
+ const label = `geometry.attributes.${name}`;
2243
+ maxCount = Math.min(maxCount, (0, attributes_1.attributeCount)(attribute, label) * meshPerAttribute(attribute, `${label}.meshPerAttribute`));
2244
+ }
2245
+ const requested = integerCountOrDefault(geometry.instanceCount, 'geometry.instanceCount', Infinity, true);
2246
+ const effectiveCount = Math.min(requested, maxCount);
2247
+ if (effectiveCount === Infinity)
2248
+ return 1;
2249
+ return (0, math_1.clampInteger)(effectiveCount, 0, Math.max(0, Math.floor(maxCount)));
2250
+ }
2251
+ function assertSupportedCustomFragmentInstancedAttributes(geometry, pbrProps) {
2252
+ if (!pbrProps.customFragmentShader)
2253
+ return;
2254
+ const unsupported = [];
2255
+ for (const [name, attribute] of Object.entries((0, attributes_1.geometryAttributes)(geometry))) {
2256
+ if (!isInstancedAttribute(attribute))
2257
+ continue;
2258
+ if (!SupportedInstancedBufferGeometryAttributes.has(name))
2259
+ unsupported.push(`geometry.attributes.${name}`);
2260
+ }
2261
+ if (unsupported.length === 0)
2262
+ return;
2263
+ throw new Error(`Custom WGSL fragment materials cannot bind arbitrary InstancedBufferGeometry vertex attributes (${unsupported.join(', ')}) in @headless-three/renderer yet. Use supported instanced offset, color, normal, or UV attributes, expand the geometry on the CPU, or render separate draws until custom vertex-attribute shader integration is implemented.`);
2264
+ }
2265
+ function isInstancedAttribute(attribute) {
2266
+ return attribute?.isInstancedBufferAttribute === true;
2267
+ }
2268
+ function meshPerAttribute(attribute, label = 'InstancedBufferAttribute.meshPerAttribute') {
2269
+ const value = attribute.meshPerAttribute;
2270
+ if (value == null)
2271
+ return 1;
2272
+ if (typeof value === 'number' && Number.isFinite(value)) {
2273
+ if (value <= 0) {
2274
+ throw new TypeError(`${label} must be a positive finite number.`);
2275
+ }
2276
+ if (!Number.isInteger(value)) {
2277
+ throw new TypeError(`${label} must be a positive integer.`);
2278
+ }
2279
+ return value;
2280
+ }
2281
+ throw new TypeError(`${label} must be a positive finite number.`);
2282
+ }
2283
+ function instancedAttributeIndex(attribute, instanceIndex, label = 'InstancedBufferAttribute') {
2284
+ return Math.min((0, attributes_1.attributeCount)(attribute, label) - 1, Math.floor(instanceIndex / meshPerAttribute(attribute, `${label}.meshPerAttribute`)));
2285
+ }
2286
+ function instancedOffsetAttribute(geometry) {
2287
+ const names = ['instanceOffset', 'instancePosition', 'offset', 'translate', 'translation'];
2288
+ for (const name of names) {
2289
+ const attribute = (0, attributes_1.getAttribute)(geometry, name);
2290
+ if (isInstancedAttribute(attribute))
2291
+ return { attribute, label: `geometry.attributes.${name}` };
2292
+ }
2293
+ return null;
2294
+ }
2295
+ function instancedScaleAttribute(geometry) {
2296
+ const names = ['instanceScale', 'scale'];
2297
+ for (const name of names) {
2298
+ const attribute = (0, attributes_1.getAttribute)(geometry, name);
2299
+ if (isInstancedAttribute(attribute))
2300
+ return { attribute, label: `geometry.attributes.${name}` };
2301
+ }
2302
+ return null;
2303
+ }
2304
+ function expandVec3ValuesForInstances(values, start, count, instanceCount, offsetAttribute, scaleAttribute) {
2305
+ if (instanceCount <= 1 && !offsetAttribute && !scaleAttribute) {
2306
+ return values.slice(start * 3, (start + count) * 3);
2307
+ }
2308
+ const out = new Array(count * instanceCount * 3);
2309
+ let dst = 0;
2310
+ for (let instance = 0; instance < instanceCount; instance += 1) {
2311
+ const offsetIndex = offsetAttribute
2312
+ ? instancedAttributeIndex(offsetAttribute.attribute, instance, offsetAttribute.label)
2313
+ : 0;
2314
+ const ox = offsetAttribute ? (0, attributes_1.attributeComponent)(offsetAttribute.attribute, offsetIndex, 0, offsetAttribute.label) : 0;
2315
+ const oy = offsetAttribute ? (0, attributes_1.attributeComponent)(offsetAttribute.attribute, offsetIndex, 1, offsetAttribute.label) : 0;
2316
+ const oz = offsetAttribute ? (0, attributes_1.attributeComponent)(offsetAttribute.attribute, offsetIndex, 2, offsetAttribute.label) : 0;
2317
+ const scale = instanceScaleComponents(scaleAttribute, instance);
2318
+ for (let vertex = start; vertex < start + count; vertex += 1) {
2319
+ out[dst++] = values[vertex * 3] * scale[0] + ox;
2320
+ out[dst++] = values[vertex * 3 + 1] * scale[1] + oy;
2321
+ out[dst++] = values[vertex * 3 + 2] * scale[2] + oz;
2322
+ }
2323
+ }
2324
+ return out;
2325
+ }
2326
+ function expandVec3ValuesForInstancesWithCache(cache, geometry, position, values, start, count, instanceCount, offsetAttribute, scaleAttribute) {
2327
+ if (instanceCount <= 1 && !offsetAttribute && !scaleAttribute) {
2328
+ return expandVec3ValuesForInstances(values, start, count, instanceCount, offsetAttribute, scaleAttribute);
2329
+ }
2330
+ const signature = instancedPositionExpansionSignature(geometry, position, values, start, count, instanceCount, offsetAttribute, scaleAttribute);
2331
+ if (!cache || !signature.cacheable) {
2332
+ return expandVec3ValuesForInstances(values, start, count, instanceCount, offsetAttribute, scaleAttribute);
2333
+ }
2334
+ const key = `${start}:${count}:${instanceCount}`;
2335
+ let geometryCache = cache.instancedPositionExpansions.get(geometry);
2336
+ const cached = geometryCache?.get(key);
2337
+ if (cached && sameInstancedPositionExpansionSignature(cached.signature, signature)) {
2338
+ return cached.positions;
2339
+ }
2340
+ const positions = expandVec3ValuesForInstances(values, start, count, instanceCount, offsetAttribute, scaleAttribute);
2341
+ if (!geometryCache) {
2342
+ geometryCache = new Map();
2343
+ cache.instancedPositionExpansions.set(geometry, geometryCache);
2344
+ }
2345
+ geometryCache.set(key, { signature, positions });
2346
+ return positions;
2347
+ }
2348
+ function instancedPositionExpansionSignature(geometry, position, values, start, count, instanceCount, offsetAttribute, scaleAttribute) {
2349
+ const signature = {
2350
+ cacheable: true,
2351
+ geometryVersion: geometry.version,
2352
+ sourcePositions: values,
2353
+ start,
2354
+ count,
2355
+ instanceCount,
2356
+ position: attributeSignature(position),
2357
+ instancedPositionOffset: attributeSignature(offsetAttribute?.attribute),
2358
+ instancedPositionScale: attributeSignature(scaleAttribute?.attribute),
2359
+ };
2360
+ signature.cacheable = attributeSignatureCacheable(signature.position)
2361
+ && attributeSignatureCacheable(signature.instancedPositionOffset)
2362
+ && attributeSignatureCacheable(signature.instancedPositionScale);
2363
+ return signature;
2364
+ }
2365
+ function sameInstancedPositionExpansionSignature(a, b) {
2366
+ return a.cacheable === b.cacheable
2367
+ && a.geometryVersion === b.geometryVersion
2368
+ && a.sourcePositions === b.sourcePositions
2369
+ && a.start === b.start
2370
+ && a.count === b.count
2371
+ && a.instanceCount === b.instanceCount
2372
+ && sameAttributeSignature(a.position, b.position)
2373
+ && sameAttributeSignature(a.instancedPositionOffset, b.instancedPositionOffset)
2374
+ && sameAttributeSignature(a.instancedPositionScale, b.instancedPositionScale);
2375
+ }
2376
+ function instanceScaleComponents(scaleAttribute, instance) {
2377
+ if (!scaleAttribute)
2378
+ return [1, 1, 1];
2379
+ const sourceIndex = instancedAttributeIndex(scaleAttribute.attribute, instance, scaleAttribute.label);
2380
+ const itemSize = scaleAttribute.attribute.itemSize ?? 1;
2381
+ const sx = (0, attributes_1.attributeComponent)(scaleAttribute.attribute, sourceIndex, 0, scaleAttribute.label);
2382
+ const sy = itemSize >= 2
2383
+ ? (0, attributes_1.attributeComponent)(scaleAttribute.attribute, sourceIndex, 1, scaleAttribute.label)
2384
+ : sx;
2385
+ const sz = itemSize >= 3
2386
+ ? (0, attributes_1.attributeComponent)(scaleAttribute.attribute, sourceIndex, 2, scaleAttribute.label)
2387
+ : sx;
2388
+ return [sx, sy, sz];
2389
+ }
2390
+ function expandVec2ValuesForInstances(values, start, count, instanceCount) {
2391
+ if (instanceCount <= 1)
2392
+ return values.slice(start * 2, (start + count) * 2);
2393
+ const out = new Array(count * instanceCount * 2);
2394
+ let dst = 0;
2395
+ for (let instance = 0; instance < instanceCount; instance += 1) {
2396
+ for (let vertex = start; vertex < start + count; vertex += 1) {
2397
+ out[dst++] = values[vertex * 2];
2398
+ out[dst++] = values[vertex * 2 + 1];
2399
+ }
2400
+ }
2401
+ return out;
2402
+ }
2403
+ function expandNormalValuesForInstances(attribute, values, start, count, instanceCount, label = 'geometry.attributes.normal') {
2404
+ if (!isInstancedAttribute(attribute)) {
2405
+ return expandVec3ValuesForInstances(values, start, count, instanceCount);
2406
+ }
2407
+ const out = new Array(count * instanceCount * 3);
2408
+ let dst = 0;
2409
+ for (let instance = 0; instance < instanceCount; instance += 1) {
2410
+ const sourceIndex = instancedAttributeIndex(attribute, instance, label);
2411
+ const nx = (0, attributes_1.attributeComponent)(attribute, sourceIndex, 0, label);
2412
+ const ny = (0, attributes_1.attributeComponent)(attribute, sourceIndex, 1, label);
2413
+ const nz = (0, attributes_1.attributeComponent)(attribute, sourceIndex, 2, label);
2414
+ for (let vertex = 0; vertex < count; vertex += 1) {
2415
+ out[dst++] = nx;
2416
+ out[dst++] = ny;
2417
+ out[dst++] = nz;
2418
+ }
2419
+ }
2420
+ return out;
2421
+ }
2422
+ function expandNormalValuesForInstancesWithCache(cache, geometry, attribute, values, start, count, instanceCount, label = 'geometry.attributes.normal') {
2423
+ if (instanceCount <= 1 && !isInstancedAttribute(attribute)) {
2424
+ return expandNormalValuesForInstances(attribute, values, start, count, instanceCount, label);
2425
+ }
2426
+ const signature = instancedNormalExpansionSignature(geometry, attribute, values, start, count, instanceCount, label);
2427
+ if (!cache || !signature.cacheable) {
2428
+ return expandNormalValuesForInstances(attribute, values, start, count, instanceCount, label);
2429
+ }
2430
+ const key = `${label}:${start}:${count}:${instanceCount}`;
2431
+ let geometryCache = cache.instancedNormalExpansions.get(geometry);
2432
+ const cached = geometryCache?.get(key);
2433
+ if (cached && sameInstancedNormalExpansionSignature(cached.signature, signature)) {
2434
+ return cached.normals;
2435
+ }
2436
+ const normals = expandNormalValuesForInstances(attribute, values, start, count, instanceCount, label);
2437
+ if (!geometryCache) {
2438
+ geometryCache = new Map();
2439
+ cache.instancedNormalExpansions.set(geometry, geometryCache);
2440
+ }
2441
+ geometryCache.set(key, { signature, normals });
2442
+ return normals;
2443
+ }
2444
+ function instancedNormalExpansionSignature(geometry, attribute, values, start, count, instanceCount, label) {
2445
+ const signature = {
2446
+ cacheable: true,
2447
+ geometryVersion: geometry.version,
2448
+ sourceNormals: values,
2449
+ start,
2450
+ count,
2451
+ instanceCount,
2452
+ normal: attributeSignature(attribute),
2453
+ label,
2454
+ };
2455
+ signature.cacheable = attributeSignatureCacheable(signature.normal);
2456
+ return signature;
2457
+ }
2458
+ function sameInstancedNormalExpansionSignature(a, b) {
2459
+ return a.cacheable === b.cacheable
2460
+ && a.geometryVersion === b.geometryVersion
2461
+ && a.sourceNormals === b.sourceNormals
2462
+ && a.start === b.start
2463
+ && a.count === b.count
2464
+ && a.instanceCount === b.instanceCount
2465
+ && sameAttributeSignature(a.normal, b.normal)
2466
+ && a.label === b.label;
2467
+ }
2468
+ function expandUvChannelForInstances(channel, start, count, instanceCount) {
2469
+ if (!isInstancedAttribute(channel.attribute)) {
2470
+ return expandVec2ValuesForInstances(channel.values, start, count, instanceCount);
2471
+ }
2472
+ const out = new Array(count * instanceCount * 2);
2473
+ let dst = 0;
2474
+ for (let instance = 0; instance < instanceCount; instance += 1) {
2475
+ const sourceIndex = instancedAttributeIndex(channel.attribute, instance, labelForMeshPerAttribute(channel));
2476
+ const u = (0, attributes_1.attributeComponent)(channel.attribute, sourceIndex, 0, channel.label);
2477
+ const v = (0, attributes_1.attributeComponent)(channel.attribute, sourceIndex, 1, channel.label);
2478
+ for (let vertex = 0; vertex < count; vertex += 1) {
2479
+ out[dst++] = u;
2480
+ out[dst++] = v;
2481
+ }
2482
+ }
2483
+ return out;
2484
+ }
2485
+ function expandUvChannelForInstancesWithCache(cache, geometry, channel, start, count, instanceCount) {
2486
+ if (instanceCount <= 1 && !isInstancedAttribute(channel.attribute)) {
2487
+ return expandUvChannelForInstances(channel, start, count, instanceCount);
2488
+ }
2489
+ const signature = instancedUvExpansionSignature(geometry, channel, start, count, instanceCount);
2490
+ if (!cache || !signature.cacheable) {
2491
+ return expandUvChannelForInstances(channel, start, count, instanceCount);
2492
+ }
2493
+ const key = `${channel.label}:${start}:${count}:${instanceCount}`;
2494
+ let geometryCache = cache.instancedUvExpansions.get(geometry);
2495
+ const cached = geometryCache?.get(key);
2496
+ if (cached && sameInstancedUvExpansionSignature(cached.signature, signature)) {
2497
+ return cached.uvs;
2498
+ }
2499
+ const uvs = expandUvChannelForInstances(channel, start, count, instanceCount);
2500
+ if (!geometryCache) {
2501
+ geometryCache = new Map();
2502
+ cache.instancedUvExpansions.set(geometry, geometryCache);
2503
+ }
2504
+ geometryCache.set(key, { signature, uvs });
2505
+ return uvs;
2506
+ }
2507
+ function instancedUvExpansionSignature(geometry, channel, start, count, instanceCount) {
2508
+ const signature = {
2509
+ cacheable: true,
2510
+ geometryVersion: geometry.version,
2511
+ channel: uvChannelSignature(channel),
2512
+ start,
2513
+ count,
2514
+ instanceCount,
2515
+ };
2516
+ signature.cacheable = attributeSignatureCacheable(signature.channel.attribute);
2517
+ return signature;
2518
+ }
2519
+ function sameInstancedUvExpansionSignature(a, b) {
2520
+ return a.cacheable === b.cacheable
2521
+ && a.geometryVersion === b.geometryVersion
2522
+ && sameUvChannelSignature(a.channel, b.channel)
2523
+ && a.start === b.start
2524
+ && a.count === b.count
2525
+ && a.instanceCount === b.instanceCount;
2526
+ }
2527
+ function appendUvForVertex(out, channel, vertexIndex, instanceIndex) {
2528
+ const sourceIndex = isInstancedAttribute(channel.attribute)
2529
+ ? instancedAttributeIndex(channel.attribute, instanceIndex, labelForMeshPerAttribute(channel))
2530
+ : vertexIndex;
2531
+ out.push((0, attributes_1.attributeComponent)(channel.attribute, sourceIndex, 0, channel.label), (0, attributes_1.attributeComponent)(channel.attribute, sourceIndex, 1, channel.label));
2532
+ }
2533
+ function uvValuesForInstance(channel, vertexCount, instanceIndex) {
2534
+ if (!channel)
2535
+ return null;
2536
+ if (!isInstancedAttribute(channel.attribute))
2537
+ return channel.values;
2538
+ const out = new Array(vertexCount * 2);
2539
+ let dst = 0;
2540
+ const sourceIndex = instancedAttributeIndex(channel.attribute, instanceIndex, labelForMeshPerAttribute(channel));
2541
+ const u = (0, attributes_1.attributeComponent)(channel.attribute, sourceIndex, 0, channel.label);
2542
+ const v = (0, attributes_1.attributeComponent)(channel.attribute, sourceIndex, 1, channel.label);
2543
+ for (let vertex = 0; vertex < vertexCount; vertex += 1) {
2544
+ out[dst++] = u;
2545
+ out[dst++] = v;
2546
+ }
2547
+ return out;
2548
+ }
2549
+ function labelForMeshPerAttribute(channel) {
2550
+ return `${channel.label}.meshPerAttribute`;
2551
+ }
2552
+ function expandColorAttributeForInstances(attribute, materialColor, start, count, instanceCount, label = 'geometry.attributes.color') {
2553
+ if (!isInstancedAttribute(attribute)) {
2554
+ const colors = (0, attributes_1.readColorAttribute)(attribute, materialColor, label);
2555
+ if (instanceCount <= 1)
2556
+ return colors.slice(start * 4, (start + count) * 4);
2557
+ const out = new Array(count * instanceCount * 4);
2558
+ let dst = 0;
2559
+ for (let instance = 0; instance < instanceCount; instance += 1) {
2560
+ for (let vertex = start; vertex < start + count; vertex += 1) {
2561
+ out[dst++] = colors[vertex * 4];
2562
+ out[dst++] = colors[vertex * 4 + 1];
2563
+ out[dst++] = colors[vertex * 4 + 2];
2564
+ out[dst++] = colors[vertex * 4 + 3];
2565
+ }
2566
+ }
2567
+ return out;
2568
+ }
2569
+ const itemSize = attribute.itemSize ?? 3;
2570
+ const out = new Array(count * instanceCount * 4);
2571
+ let dst = 0;
2572
+ for (let instance = 0; instance < instanceCount; instance += 1) {
2573
+ const sourceIndex = instancedAttributeIndex(attribute, instance, label);
2574
+ const r = (0, math_1.clamp01)((0, attributes_1.attributeComponent)(attribute, sourceIndex, 0, label) * materialColor[0]);
2575
+ const g = (0, math_1.clamp01)((0, attributes_1.attributeComponent)(attribute, sourceIndex, 1, label) * materialColor[1]);
2576
+ const b = (0, math_1.clamp01)((0, attributes_1.attributeComponent)(attribute, sourceIndex, 2, label) * materialColor[2]);
2577
+ const a = (0, math_1.clamp01)((itemSize >= 4 ? (0, attributes_1.attributeComponent)(attribute, sourceIndex, 3, label) : 1) * materialColor[3]);
2578
+ for (let vertex = 0; vertex < count; vertex += 1) {
2579
+ out[dst++] = r;
2580
+ out[dst++] = g;
2581
+ out[dst++] = b;
2582
+ out[dst++] = a;
2583
+ }
2584
+ }
2585
+ return out;
2586
+ }
2587
+ function expandColorAttributeForInstancesWithCache(cache, geometry, attribute, materialColor, start, count, instanceCount, label = 'geometry.attributes.color') {
2588
+ if (instanceCount <= 1 && !isInstancedAttribute(attribute)) {
2589
+ return expandColorAttributeForInstances(attribute, materialColor, start, count, instanceCount, label);
2590
+ }
2591
+ const signature = instancedColorExpansionSignature(geometry, attribute, materialColor, start, count, instanceCount, label);
2592
+ if (!cache || !signature.cacheable) {
2593
+ return expandColorAttributeForInstances(attribute, materialColor, start, count, instanceCount, label);
2594
+ }
2595
+ const key = `${label}:${start}:${count}:${instanceCount}`;
2596
+ let geometryCache = cache.instancedColorExpansions.get(geometry);
2597
+ const cached = geometryCache?.get(key);
2598
+ if (cached && sameInstancedColorExpansionSignature(cached.signature, signature)) {
2599
+ return cached.colors;
2600
+ }
2601
+ const colors = expandColorAttributeForInstances(attribute, materialColor, start, count, instanceCount, label);
2602
+ if (!geometryCache) {
2603
+ geometryCache = new Map();
2604
+ cache.instancedColorExpansions.set(geometry, geometryCache);
2605
+ }
2606
+ geometryCache.set(key, { signature, colors });
2607
+ return colors;
2608
+ }
2609
+ function instancedColorExpansionSignature(geometry, attribute, materialColor, start, count, instanceCount, label) {
2610
+ const signature = {
2611
+ cacheable: true,
2612
+ geometryVersion: geometry.version,
2613
+ materialColor: materialColor.slice(),
2614
+ start,
2615
+ count,
2616
+ instanceCount,
2617
+ color: attributeSignature(attribute),
2618
+ label,
2619
+ };
2620
+ signature.cacheable = attributeSignatureCacheable(signature.color);
2621
+ return signature;
2622
+ }
2623
+ function sameInstancedColorExpansionSignature(a, b) {
2624
+ return a.cacheable === b.cacheable
2625
+ && a.geometryVersion === b.geometryVersion
2626
+ && sameNumberArray(a.materialColor, b.materialColor)
2627
+ && a.start === b.start
2628
+ && a.count === b.count
2629
+ && a.instanceCount === b.instanceCount
2630
+ && sameAttributeSignature(a.color, b.color)
2631
+ && a.label === b.label;
2632
+ }
2633
+ function expandIndicesForInstances(indices, vertexCount, instanceCount) {
2634
+ if (instanceCount <= 1)
2635
+ return indices;
2636
+ const out = new Array(indices.length * instanceCount);
2637
+ let dst = 0;
2638
+ for (let instance = 0; instance < instanceCount; instance += 1) {
2639
+ const offset = instance * vertexCount;
2640
+ for (const index of indices) {
2641
+ out[dst++] = index + offset;
2642
+ }
2643
+ }
2644
+ return out;
2645
+ }
2646
+ function wireframeIndicesForTriangles(indices) {
2647
+ const out = new Array(indices.length * 2);
2648
+ let dst = 0;
2649
+ for (let i = 0; i < indices.length; i += 3) {
2650
+ const a = indices[i];
2651
+ const b = indices[i + 1];
2652
+ const c = indices[i + 2];
2653
+ out[dst++] = a;
2654
+ out[dst++] = b;
2655
+ out[dst++] = b;
2656
+ out[dst++] = c;
2657
+ out[dst++] = c;
2658
+ out[dst++] = a;
2659
+ }
2660
+ return out;
2661
+ }
2662
+ function wireframeIndicesForUnindexedTriangles(vertexCount) {
2663
+ const out = new Array(vertexCount * 2);
2664
+ let dst = 0;
2665
+ for (let i = 0; i < vertexCount; i += 3) {
2666
+ out[dst++] = i;
2667
+ out[dst++] = i + 1;
2668
+ out[dst++] = i + 1;
2669
+ out[dst++] = i + 2;
2670
+ out[dst++] = i + 2;
2671
+ out[dst++] = i;
2672
+ }
2673
+ return out;
2674
+ }
2675
+ function isDepthDistanceWireframeMaterial(material) {
2676
+ return material?.wireframe === true
2677
+ && (material.isMeshDepthMaterial === true || material.isMeshDistanceMaterial === true);
2678
+ }
2679
+ function isMeshWireframeMaterial(material) {
2680
+ return material?.wireframe === true;
2681
+ }
2682
+ function readUvChannels(geometry) {
2683
+ const primaryUvs = readOptionalUvAttribute(geometry, 'uv');
2684
+ return [
2685
+ primaryUvs,
2686
+ readOptionalUvAttribute(geometry, 'uv1') ?? readOptionalUvAttribute(geometry, 'uv2') ?? primaryUvs,
2687
+ readOptionalUvAttribute(geometry, 'uv2') ?? readOptionalUvAttribute(geometry, 'uv1') ?? primaryUvs,
2688
+ readOptionalUvAttribute(geometry, 'uv3') ?? primaryUvs,
2689
+ ];
2690
+ }
2691
+ function readOptionalUvAttribute(geometry, name) {
2692
+ const attribute = (0, attributes_1.getAttribute)(geometry, name);
2693
+ if (!attribute)
2694
+ return null;
2695
+ const label = `geometry.attributes.${name}`;
2696
+ return {
2697
+ attribute,
2698
+ label,
2699
+ values: (0, attributes_1.readVec2Attribute)(attribute, label),
2700
+ };
2701
+ }
2702
+ function textureUvStreamsForMapAlphaMaterial(channels, material) {
2703
+ const mapChannel = material?.map ? (0, materials_1.textureUvChannel)(material.map) : undefined;
2704
+ const alphaChannel = material?.alphaMap ? (0, materials_1.textureUvChannel)(material.alphaMap) : undefined;
2705
+ const requestedChannels = [mapChannel, alphaChannel]
2706
+ .filter((channel) => channel !== undefined);
2707
+ const distinctChannels = [...new Set(requestedChannels)];
2708
+ let primaryChannel = 0;
2709
+ let secondaryChannel;
2710
+ if (mapChannel !== undefined
2711
+ && alphaChannel !== undefined
2712
+ && mapChannel !== alphaChannel
2713
+ && mapChannel > 0
2714
+ && alphaChannel > 0) {
2715
+ primaryChannel = mapChannel;
2716
+ secondaryChannel = alphaChannel;
2717
+ }
2718
+ else {
2719
+ secondaryChannel = distinctChannels.find((channel) => channel > 0);
2720
+ }
2721
+ return {
2722
+ uvs: channels[primaryChannel] ?? channels[0],
2723
+ uvs2: secondaryChannel !== undefined
2724
+ ? channels[secondaryChannel] ?? channels[0]
2725
+ : null,
2726
+ textureUsesUv2: mapChannel !== undefined ? mapChannel !== primaryChannel : undefined,
2727
+ alphaMapUsesUv2: alphaChannel !== undefined ? alphaChannel !== primaryChannel : undefined,
2728
+ };
2729
+ }
2730
+ function textureUvStreamsForMeshMaterial(channels, material) {
2731
+ return textureUvStreamsForMaterialSlots(channels, meshTextureUvSlots(material));
2732
+ }
2733
+ function meshTextureUvSlots(material) {
2734
+ if (!material)
2735
+ return [];
2736
+ const slots = material.isMeshMatcapMaterial === true
2737
+ ? [{ texture: material.map, pbrFlag: 'matcapMapUsesUv2' }]
2738
+ : [{ texture: material.map, textureFlag: 'textureUsesUv2' }];
2739
+ slots.push({ texture: material.clearcoatMap, pbrFlag: 'clearcoatMapUsesUv2' }, { texture: material.clearcoatRoughnessMap, pbrFlag: 'clearcoatRoughnessMapUsesUv2' }, { texture: material.clearcoatNormalMap, pbrFlag: 'clearcoatNormalMapUsesUv2' }, { texture: material.sheenColorMap, pbrFlag: 'sheenColorMapUsesUv2' }, { texture: material.sheenRoughnessMap, pbrFlag: 'sheenRoughnessMapUsesUv2' }, { texture: material.anisotropyMap, pbrFlag: 'anisotropyMapUsesUv2' }, { texture: material.iridescenceMap, pbrFlag: 'iridescenceMapUsesUv2' }, { texture: material.iridescenceThicknessMap, pbrFlag: 'iridescenceThicknessMapUsesUv2' }, { texture: material.normalMap, pbrFlag: 'normalMapUsesUv2' }, { texture: material.bumpMap, pbrFlag: 'bumpMapUsesUv2' }, { texture: material.transmissionMap, pbrFlag: 'transmissionMapUsesUv2' }, { texture: material.thicknessMap, pbrFlag: 'thicknessMapUsesUv2' }, { texture: material.specularColorMap, pbrFlag: 'specularColorMapUsesUv2' }, { texture: material.specularIntensityMap, pbrFlag: 'specularIntensityMapUsesUv2' }, { texture: material.displacementMap, pbrFlag: 'displacementMapUsesUv2' }, { texture: material.metalnessMap ?? material.roughnessMap, pbrFlag: 'metallicRoughnessTextureUsesUv2' }, { texture: material.emissiveMap, pbrFlag: 'emissiveMapUsesUv2' }, { texture: material.lightMap, pbrFlag: 'lightMapUsesUv2' }, { texture: material.aoMap, pbrFlag: 'aoMapUsesUv2' }, { texture: material.specularMap, pbrFlag: 'specularMapUsesUv2' }, { texture: material.alphaMap, textureFlag: 'alphaMapUsesUv2' });
2740
+ return slots;
2741
+ }
2742
+ function textureUvStreamsForMaterialSlots(channels, slots) {
2743
+ const activeSlots = slots.filter((slot) => slot.texture != null);
2744
+ const requestedChannels = [];
2745
+ for (const slot of activeSlots) {
2746
+ const channel = (0, materials_1.textureUvChannel)(slot.texture);
2747
+ if (!requestedChannels.includes(channel))
2748
+ requestedChannels.push(channel);
2749
+ }
2750
+ if (requestedChannels.length > 2) {
2751
+ const channelList = [...requestedChannels].sort((a, b) => a - b).join(', ');
2752
+ throw new Error(`Material uses texture.channel values ${channelList}, but @headless-three/renderer can bind only two UV attributes per draw. Use at most two texture channels or render separate passes.`);
2753
+ }
2754
+ const preferredPrimary = preferredPrimaryTextureChannel(activeSlots);
2755
+ const primaryChannel = requestedChannels.includes(0)
2756
+ ? 0
2757
+ : preferredPrimary !== undefined && requestedChannels.includes(preferredPrimary)
2758
+ ? preferredPrimary
2759
+ : requestedChannels[0] ?? 0;
2760
+ const secondaryChannel = requestedChannels.find((channel) => channel !== primaryChannel);
2761
+ const out = {
2762
+ uvs: channels[primaryChannel] ?? channels[0],
2763
+ uvs2: secondaryChannel !== undefined
2764
+ ? channels[secondaryChannel] ?? channels[0]
2765
+ : null,
2766
+ };
2767
+ for (const slot of activeSlots) {
2768
+ const usesUv2 = secondaryChannel !== undefined
2769
+ && (0, materials_1.textureUvChannel)(slot.texture) === secondaryChannel;
2770
+ if (slot.textureFlag === 'textureUsesUv2') {
2771
+ out.textureUsesUv2 = usesUv2;
2772
+ }
2773
+ else if (slot.textureFlag === 'alphaMapUsesUv2') {
2774
+ out.alphaMapUsesUv2 = usesUv2;
2775
+ }
2776
+ else if (slot.pbrFlag) {
2777
+ out.pbrUsesUv2 ?? (out.pbrUsesUv2 = {});
2778
+ out.pbrUsesUv2[slot.pbrFlag] = usesUv2;
2779
+ }
2780
+ }
2781
+ return out;
2782
+ }
2783
+ function preferredPrimaryTextureChannel(slots) {
2784
+ for (const flag of ['textureUsesUv2', 'alphaMapUsesUv2']) {
2785
+ const slot = slots.find((candidate) => candidate.textureFlag === flag);
2786
+ if (slot?.texture != null)
2787
+ return (0, materials_1.textureUvChannel)(slot.texture);
2788
+ }
2789
+ return slots[0]?.texture ? (0, materials_1.textureUvChannel)(slots[0].texture) : undefined;
2790
+ }
2791
+ function applyPbrUvStreamFlags(props, uvStreams) {
2792
+ if (!uvStreams.pbrUsesUv2)
2793
+ return;
2794
+ for (const [flag, usesUv2] of Object.entries(uvStreams.pbrUsesUv2)) {
2795
+ props[flag] = usesUv2;
2796
+ }
2797
+ }
2798
+ function secondaryUvsForMaterial(channels, material) {
2799
+ const textures = [
2800
+ material?.clearcoatMap,
2801
+ material?.clearcoatRoughnessMap,
2802
+ material?.clearcoatNormalMap,
2803
+ material?.sheenColorMap,
2804
+ material?.sheenRoughnessMap,
2805
+ material?.anisotropyMap,
2806
+ material?.iridescenceMap,
2807
+ material?.iridescenceThicknessMap,
2808
+ material?.normalMap,
2809
+ material?.bumpMap,
2810
+ material?.transmissionMap,
2811
+ material?.thicknessMap,
2812
+ material?.specularColorMap,
2813
+ material?.specularIntensityMap,
2814
+ material?.displacementMap,
2815
+ material?.map,
2816
+ material?.metalnessMap,
2817
+ material?.roughnessMap,
2818
+ material?.emissiveMap,
2819
+ material?.lightMap,
2820
+ material?.aoMap,
2821
+ material?.specularMap,
2822
+ material?.alphaMap,
2823
+ ];
2824
+ let channel = 0;
2825
+ for (const texture of textures) {
2826
+ const textureChannel = (0, materials_1.textureUvChannel)(texture);
2827
+ if (textureChannel === 0)
2828
+ continue;
2829
+ if (channel === 0) {
2830
+ channel = textureChannel;
2831
+ continue;
2832
+ }
2833
+ if (channel !== textureChannel) {
2834
+ throw new Error(`Material uses multiple non-primary texture.channel values (${channel} and ${textureChannel}), but @headless-three/renderer can bind one secondary UV attribute per draw. Use one shared non-primary channel or render separate passes.`);
2835
+ }
2836
+ }
2837
+ return channels[channel] ?? channels[0];
2838
+ }
2839
+ function updateLodObject(object, camera) {
2840
+ if (object.isLOD !== true || !camera)
2841
+ return;
2842
+ if (optionalObjectBoolean(object.autoUpdate, 'LOD.autoUpdate') === false)
2843
+ return;
2844
+ const levels = object.levels;
2845
+ const normalizedLevels = normalizeLodLevels(levels);
2846
+ const cameraZoom = cameraZoomOrDefault(camera.zoom);
2847
+ if (typeof object.update === 'function') {
2848
+ object.update(camera);
2849
+ return;
2850
+ }
2851
+ if (normalizedLevels.length <= 1)
2852
+ return;
2853
+ const distance = distanceBetweenMatrices(camera.matrixWorld, object.matrixWorld) / cameraZoom;
2854
+ normalizedLevels[0].object.visible = true;
2855
+ let i = 1;
2856
+ for (; i < normalizedLevels.length; i += 1) {
2857
+ const level = normalizedLevels[i];
2858
+ let levelDistance = level.distance;
2859
+ if (level.object.visible) {
2860
+ levelDistance -= levelDistance * level.hysteresis;
2861
+ }
2862
+ if (distance >= levelDistance) {
2863
+ normalizedLevels[i - 1].object.visible = false;
2864
+ level.object.visible = true;
2865
+ }
2866
+ else {
2867
+ break;
2868
+ }
2869
+ }
2870
+ ;
2871
+ object._currentLevel = i - 1;
2872
+ for (; i < normalizedLevels.length; i += 1) {
2873
+ normalizedLevels[i].object.visible = false;
2874
+ }
2875
+ }
2876
+ function normalizeLodLevels(levels) {
2877
+ if (levels == null)
2878
+ return [];
2879
+ if (!Array.isArray(levels)) {
2880
+ throw new TypeError('LOD.levels must be an array.');
2881
+ }
2882
+ return levels.map((level, index) => {
2883
+ if (!level || typeof level !== 'object' || Array.isArray(level)) {
2884
+ throw new TypeError(`LOD.levels[${index}] must be an object.`);
2885
+ }
2886
+ const object = level.object;
2887
+ if (!object || typeof object !== 'object' || Array.isArray(object)) {
2888
+ throw new TypeError(`LOD.levels[${index}].object must be a THREE.Object3D-like object.`);
2889
+ }
2890
+ return {
2891
+ object: object,
2892
+ distance: nonNegativeMaterialOrObjectNumber(level.distance, `LOD.levels[${index}].distance`, 0),
2893
+ hysteresis: normalizedMaterialOrObjectNumber(level.hysteresis, `LOD.levels[${index}].hysteresis`, 0),
2894
+ };
2895
+ });
2896
+ }
2897
+ function distanceBetweenMatrices(a, b) {
2898
+ const ae = a?.elements;
2899
+ const be = b?.elements;
2900
+ if (!ae || ae.length < 16 || !be || be.length < 16)
2901
+ return 0;
2902
+ const dx = ae[12] - be[12];
2903
+ const dy = ae[13] - be[13];
2904
+ const dz = ae[14] - be[14];
2905
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
2906
+ }
2907
+ function dashedLineAttributes(positions, uvs, uvs2, colors, source, start, end, object, lineDistance, material) {
2908
+ const dashSize = positiveMaterialOrObjectNumber(material.dashSize, 'material.dashSize', 3);
2909
+ const gapSize = nonNegativeMaterialOrObjectNumber(material.gapSize, 'material.gapSize', 1);
2910
+ const scale = nonNegativeMaterialOrObjectNumber(material.scale, 'material.scale', 1);
2911
+ const segments = lineSegmentsWithDistances(positions, source, start, end, object, lineDistance);
2912
+ const out = createDashedLineExpansion(uvs, uvs2, colors);
2913
+ if (!lineDistance || gapSize <= 0 || scale === 0) {
2914
+ for (const segment of segments) {
2915
+ appendInterpolatedLine(out, positions, uvs, uvs2, colors, segment.a, segment.b, 0, 1);
2916
+ }
2917
+ return out;
2918
+ }
2919
+ const totalSize = dashSize + gapSize;
2920
+ for (const segment of segments) {
2921
+ appendDashedSegment(out, positions, uvs, uvs2, colors, segment, scale, dashSize, totalSize);
2922
+ }
2923
+ return out;
2924
+ }
2925
+ function dashedLineAttributesWithCache(cache, geometry, position, positions, uvChannel, uvChannel2, colors, source, start, end, object, lineDistance, material) {
2926
+ const dashSize = positiveMaterialOrObjectNumber(material.dashSize, 'material.dashSize', 3);
2927
+ const gapSize = nonNegativeMaterialOrObjectNumber(material.gapSize, 'material.gapSize', 1);
2928
+ const scale = nonNegativeMaterialOrObjectNumber(material.scale, 'material.scale', 1);
2929
+ const uvs = uvChannel?.values ?? null;
2930
+ const uvs2 = uvChannel2?.values ?? null;
2931
+ if (!cache || colors) {
2932
+ return dashedLineAttributes(positions, uvs, uvs2, colors, source, start, end, object, lineDistance, material);
2933
+ }
2934
+ const signature = dashedLineSignature(geometry, position, uvChannel, uvChannel2, lineDistance, start, end, source.length, object, dashSize, gapSize, scale);
2935
+ if (!signature.cacheable) {
2936
+ return dashedLineAttributes(positions, uvs, uvs2, colors, source, start, end, object, lineDistance, material);
2937
+ }
2938
+ const cacheKey = dashedLineCacheKey(signature);
2939
+ const geometryCache = cache.dashedLines.get(geometry);
2940
+ const cached = geometryCache?.get(cacheKey);
2941
+ if (cached && sameDashedLineSignature(cached.signature, signature)) {
2942
+ return cached.expansion;
2943
+ }
2944
+ const expansion = dashedLineAttributes(positions, uvs, uvs2, colors, source, start, end, object, lineDistance, material);
2945
+ let writableGeometryCache = geometryCache;
2946
+ if (!writableGeometryCache) {
2947
+ writableGeometryCache = new Map();
2948
+ cache.dashedLines.set(geometry, writableGeometryCache);
2949
+ }
2950
+ writableGeometryCache.set(cacheKey, { signature, expansion });
2951
+ return expansion;
2952
+ }
2953
+ function dashedLineSignature(geometry, position, uvChannel, uvChannel2, lineDistance, start, end, sourceLength, object, dashSize, gapSize, scale) {
2954
+ const signature = {
2955
+ cacheable: true,
2956
+ geometryVersion: geometry.version,
2957
+ position: attributeSignature(position),
2958
+ index: attributeSignature(geometry.index),
2959
+ uv: uvChannelSignature(uvChannel),
2960
+ uv2: uvChannelSignature(uvChannel2),
2961
+ lineDistance: attributeSignature(lineDistance),
2962
+ start,
2963
+ end,
2964
+ sourceLength,
2965
+ isLineSegments: object.isLineSegments,
2966
+ isLineLoop: object.isLineLoop,
2967
+ isLine: object.isLine,
2968
+ dashSize,
2969
+ gapSize,
2970
+ scale,
2971
+ };
2972
+ signature.cacheable = dashedLineSignatureCacheable(signature);
2973
+ return signature;
2974
+ }
2975
+ function dashedLineSignatureCacheable(signature) {
2976
+ return attributeSignatureCacheable(signature.position)
2977
+ && attributeSignatureCacheable(signature.index)
2978
+ && attributeSignatureCacheable(signature.uv.attribute)
2979
+ && attributeSignatureCacheable(signature.uv2.attribute)
2980
+ && attributeSignatureCacheable(signature.lineDistance);
2981
+ }
2982
+ function dashedLineCacheKey(signature) {
2983
+ return [
2984
+ signature.start,
2985
+ signature.end,
2986
+ signature.sourceLength,
2987
+ signature.isLineSegments ? 1 : 0,
2988
+ signature.isLineLoop ? 1 : 0,
2989
+ signature.isLine ? 1 : 0,
2990
+ signature.uv.attribute.ref ? 1 : 0,
2991
+ signature.uv2.attribute.ref ? 1 : 0,
2992
+ signature.dashSize,
2993
+ signature.gapSize,
2994
+ signature.scale,
2995
+ ].join(':');
2996
+ }
2997
+ function sameDashedLineSignature(a, b) {
2998
+ return a.cacheable === b.cacheable
2999
+ && a.geometryVersion === b.geometryVersion
3000
+ && sameAttributeSignature(a.position, b.position)
3001
+ && sameAttributeSignature(a.index, b.index)
3002
+ && sameUvChannelSignature(a.uv, b.uv)
3003
+ && sameUvChannelSignature(a.uv2, b.uv2)
3004
+ && sameAttributeSignature(a.lineDistance, b.lineDistance)
3005
+ && a.start === b.start
3006
+ && a.end === b.end
3007
+ && a.sourceLength === b.sourceLength
3008
+ && a.isLineSegments === b.isLineSegments
3009
+ && a.isLineLoop === b.isLineLoop
3010
+ && a.isLine === b.isLine
3011
+ && Object.is(a.dashSize, b.dashSize)
3012
+ && Object.is(a.gapSize, b.gapSize)
3013
+ && Object.is(a.scale, b.scale);
3014
+ }
3015
+ function dashedLineAttributesForInstancesWithCache(cache, geometry, position, positions, uvChannel, uvChannel2, vertexColors, materialColor, source, start, end, object, lineDistance, material, instanceCount, offsetAttribute, scaleAttribute) {
3016
+ const dashSize = positiveMaterialOrObjectNumber(material.dashSize, 'material.dashSize', 3);
3017
+ const gapSize = nonNegativeMaterialOrObjectNumber(material.gapSize, 'material.gapSize', 1);
3018
+ const scale = nonNegativeMaterialOrObjectNumber(material.scale, 'material.scale', 1);
3019
+ if (!cache) {
3020
+ return dashedLineAttributesForInstances(positions, uvChannel, uvChannel2, vertexColors, materialColor, source, start, end, object, lineDistance, material, instanceCount, offsetAttribute, scaleAttribute);
3021
+ }
3022
+ const signature = instancedDashedLineSignature(geometry, position, uvChannel, uvChannel2, vertexColors, materialColor, lineDistance, start, end, source.length, object, dashSize, gapSize, scale, instanceCount, offsetAttribute, scaleAttribute);
3023
+ if (!signature.cacheable) {
3024
+ return dashedLineAttributesForInstances(positions, uvChannel, uvChannel2, vertexColors, materialColor, source, start, end, object, lineDistance, material, instanceCount, offsetAttribute, scaleAttribute);
3025
+ }
3026
+ const cacheKey = instancedDashedLineCacheKey(signature);
3027
+ let geometryCache = cache.instancedDashedLines.get(geometry);
3028
+ const cached = geometryCache?.get(cacheKey);
3029
+ if (cached && sameInstancedDashedLineSignature(cached.signature, signature)) {
3030
+ return cached.expansion;
3031
+ }
3032
+ const expansion = dashedLineAttributesForInstances(positions, uvChannel, uvChannel2, vertexColors, materialColor, source, start, end, object, lineDistance, material, instanceCount, offsetAttribute, scaleAttribute);
3033
+ if (!geometryCache) {
3034
+ geometryCache = new Map();
3035
+ cache.instancedDashedLines.set(geometry, geometryCache);
3036
+ }
3037
+ geometryCache.set(cacheKey, { signature, expansion });
3038
+ return expansion;
3039
+ }
3040
+ function instancedDashedLineSignature(geometry, position, uvChannel, uvChannel2, vertexColors, materialColor, lineDistance, start, end, sourceLength, object, dashSize, gapSize, scale, instanceCount, offsetAttribute, scaleAttribute) {
3041
+ const signature = {
3042
+ cacheable: true,
3043
+ geometryVersion: geometry.version,
3044
+ position: attributeSignature(position),
3045
+ index: attributeSignature(geometry.index),
3046
+ uv: uvChannelSignature(uvChannel),
3047
+ uv2: uvChannelSignature(uvChannel2),
3048
+ lineDistance: attributeSignature(lineDistance),
3049
+ vertexColors: attributeSignature(vertexColors),
3050
+ instancedPositionOffset: attributeSignature(offsetAttribute?.attribute),
3051
+ instancedPositionScale: attributeSignature(scaleAttribute?.attribute),
3052
+ materialColor: vertexColors ? materialColor.slice() : undefined,
3053
+ start,
3054
+ end,
3055
+ sourceLength,
3056
+ instanceCount,
3057
+ isLineSegments: object.isLineSegments,
3058
+ isLineLoop: object.isLineLoop,
3059
+ isLine: object.isLine,
3060
+ dashSize,
3061
+ gapSize,
3062
+ scale,
3063
+ };
3064
+ signature.cacheable = instancedDashedLineSignatureCacheable(signature);
3065
+ return signature;
3066
+ }
3067
+ function instancedDashedLineSignatureCacheable(signature) {
3068
+ return attributeSignatureCacheable(signature.position)
3069
+ && attributeSignatureCacheable(signature.index)
3070
+ && attributeSignatureCacheable(signature.uv.attribute)
3071
+ && attributeSignatureCacheable(signature.uv2.attribute)
3072
+ && attributeSignatureCacheable(signature.lineDistance)
3073
+ && attributeSignatureCacheable(signature.vertexColors)
3074
+ && attributeSignatureCacheable(signature.instancedPositionOffset)
3075
+ && attributeSignatureCacheable(signature.instancedPositionScale);
3076
+ }
3077
+ function instancedDashedLineCacheKey(signature) {
3078
+ return [
3079
+ 'instanced',
3080
+ signature.start,
3081
+ signature.end,
3082
+ signature.sourceLength,
3083
+ signature.instanceCount,
3084
+ signature.isLineSegments ? 1 : 0,
3085
+ signature.isLineLoop ? 1 : 0,
3086
+ signature.isLine ? 1 : 0,
3087
+ signature.uv.attribute.ref ? 1 : 0,
3088
+ signature.uv2.attribute.ref ? 1 : 0,
3089
+ signature.vertexColors.ref ? 1 : 0,
3090
+ signature.instancedPositionOffset.ref ? 1 : 0,
3091
+ signature.instancedPositionScale.ref ? 1 : 0,
3092
+ signature.dashSize,
3093
+ signature.gapSize,
3094
+ signature.scale,
3095
+ ].join(':');
3096
+ }
3097
+ function sameInstancedDashedLineSignature(a, b) {
3098
+ return a.cacheable === b.cacheable
3099
+ && a.geometryVersion === b.geometryVersion
3100
+ && sameAttributeSignature(a.position, b.position)
3101
+ && sameAttributeSignature(a.index, b.index)
3102
+ && sameUvChannelSignature(a.uv, b.uv)
3103
+ && sameUvChannelSignature(a.uv2, b.uv2)
3104
+ && sameAttributeSignature(a.lineDistance, b.lineDistance)
3105
+ && sameAttributeSignature(a.vertexColors, b.vertexColors)
3106
+ && sameAttributeSignature(a.instancedPositionOffset, b.instancedPositionOffset)
3107
+ && sameAttributeSignature(a.instancedPositionScale, b.instancedPositionScale)
3108
+ && sameOptionalNumberArray(a.materialColor, b.materialColor)
3109
+ && a.start === b.start
3110
+ && a.end === b.end
3111
+ && a.sourceLength === b.sourceLength
3112
+ && a.instanceCount === b.instanceCount
3113
+ && a.isLineSegments === b.isLineSegments
3114
+ && a.isLineLoop === b.isLineLoop
3115
+ && a.isLine === b.isLine
3116
+ && Object.is(a.dashSize, b.dashSize)
3117
+ && Object.is(a.gapSize, b.gapSize)
3118
+ && Object.is(a.scale, b.scale);
3119
+ }
3120
+ function dashedLineAttributesForInstances(positions, uvChannel, uvChannel2, vertexColors, materialColor, source, start, end, object, lineDistance, material, instanceCount, offsetAttribute, scaleAttribute) {
3121
+ const out = {
3122
+ positions: [],
3123
+ uvs: uvChannel ? [] : undefined,
3124
+ uvs2: uvChannel2 ? [] : undefined,
3125
+ colors: vertexColors ? [] : undefined,
3126
+ };
3127
+ const baseColors = vertexColors && !isInstancedAttribute(vertexColors)
3128
+ ? (0, attributes_1.readColorAttribute)(vertexColors, materialColor, 'geometry.attributes.color')
3129
+ : undefined;
3130
+ const vertexCount = positions.length / 3;
3131
+ for (let instance = 0; instance < instanceCount; instance += 1) {
3132
+ const instancePositions = offsetAttribute
3133
+ ? transformVec3ValuesForInstance(positions, offsetAttribute, scaleAttribute, instance)
3134
+ : scaleAttribute
3135
+ ? transformVec3ValuesForInstance(positions, null, scaleAttribute, instance)
3136
+ : positions;
3137
+ const instanceUvs = uvValuesForInstance(uvChannel, vertexCount, instance);
3138
+ const instanceUvs2 = uvValuesForInstance(uvChannel2, vertexCount, instance);
3139
+ const instanceColors = vertexColors
3140
+ ? baseColors ?? repeatedInstancedColorValues(vertexColors, materialColor, positions.length / 3, instance)
3141
+ : undefined;
3142
+ const dashed = dashedLineAttributes(instancePositions, instanceUvs, instanceUvs2, instanceColors, source, start, end, object, lineDistance, material);
3143
+ appendDashedLineExpansion(out, dashed);
3144
+ }
3145
+ return out;
3146
+ }
3147
+ function transformVec3ValuesForInstance(values, offsetAttribute, scaleAttribute, instance) {
3148
+ const offsetIndex = offsetAttribute
3149
+ ? instancedAttributeIndex(offsetAttribute.attribute, instance, offsetAttribute.label)
3150
+ : 0;
3151
+ const ox = offsetAttribute ? (0, attributes_1.attributeComponent)(offsetAttribute.attribute, offsetIndex, 0, offsetAttribute.label) : 0;
3152
+ const oy = offsetAttribute ? (0, attributes_1.attributeComponent)(offsetAttribute.attribute, offsetIndex, 1, offsetAttribute.label) : 0;
3153
+ const oz = offsetAttribute ? (0, attributes_1.attributeComponent)(offsetAttribute.attribute, offsetIndex, 2, offsetAttribute.label) : 0;
3154
+ const scale = instanceScaleComponents(scaleAttribute, instance);
3155
+ const out = new Array(values.length);
3156
+ for (let i = 0; i < values.length; i += 3) {
3157
+ out[i] = values[i] * scale[0] + ox;
3158
+ out[i + 1] = values[i + 1] * scale[1] + oy;
3159
+ out[i + 2] = values[i + 2] * scale[2] + oz;
3160
+ }
3161
+ return out;
3162
+ }
3163
+ function repeatedInstancedColorValues(attribute, materialColor, vertexCount, instance, label = 'geometry.attributes.color') {
3164
+ const sourceIndex = instancedAttributeIndex(attribute, instance, label);
3165
+ const itemSize = attribute.itemSize ?? 3;
3166
+ const color = [
3167
+ (0, math_1.clamp01)((0, attributes_1.attributeComponent)(attribute, sourceIndex, 0, label) * materialColor[0]),
3168
+ (0, math_1.clamp01)((0, attributes_1.attributeComponent)(attribute, sourceIndex, 1, label) * materialColor[1]),
3169
+ (0, math_1.clamp01)((0, attributes_1.attributeComponent)(attribute, sourceIndex, 2, label) * materialColor[2]),
3170
+ (0, math_1.clamp01)((itemSize >= 4 ? (0, attributes_1.attributeComponent)(attribute, sourceIndex, 3, label) : 1) * materialColor[3]),
3171
+ ];
3172
+ const out = new Array(vertexCount * 4);
3173
+ let dst = 0;
3174
+ for (let i = 0; i < vertexCount; i += 1) {
3175
+ out[dst++] = color[0];
3176
+ out[dst++] = color[1];
3177
+ out[dst++] = color[2];
3178
+ out[dst++] = color[3];
3179
+ }
3180
+ return out;
3181
+ }
3182
+ function appendDashedLineExpansion(out, value) {
3183
+ appendNumberArray(out.positions, value.positions);
3184
+ if (out.uvs && value.uvs)
3185
+ appendNumberArray(out.uvs, value.uvs);
3186
+ if (out.uvs2 && value.uvs2)
3187
+ appendNumberArray(out.uvs2, value.uvs2);
3188
+ if (out.colors && value.colors)
3189
+ appendNumberArray(out.colors, value.colors);
3190
+ }
3191
+ function batchedMeshDraws(object, camera, geometry) {
3192
+ const instanceInfo = object._instanceInfo;
3193
+ if (!Array.isArray(instanceInfo)) {
3194
+ throw new Error('THREE.BatchedMesh instance table is not readable. Use a real THREE.BatchedMesh or expand the batch to ordinary Mesh or InstancedMesh objects before rendering.');
3195
+ }
3196
+ const baseTransform = (0, math_1.matrixElements)(object.matrixWorld, 'batchedMesh.matrixWorld');
3197
+ const cullPerObject = batchedPerObjectFrustumCulled(object, camera);
3198
+ const sortObjects = batchedSortObjects(object);
3199
+ const customSort = batchedCustomSort(object);
3200
+ const draws = [];
3201
+ for (let instanceId = 0; instanceId < instanceInfo.length; instanceId += 1) {
3202
+ const info = instanceInfo[instanceId];
3203
+ if (!info || typeof info !== 'object') {
3204
+ throw new TypeError(`THREE.BatchedMesh._instanceInfo[${instanceId}] must be an object.`);
3205
+ }
3206
+ if (!batchedOptionalBoolean(info.active, `THREE.BatchedMesh._instanceInfo[${instanceId}].active`, true))
3207
+ continue;
3208
+ if (!batchedOptionalBoolean(info.visible, `THREE.BatchedMesh._instanceInfo[${instanceId}].visible`, true))
3209
+ continue;
3210
+ const geometryId = batchedNonNegativeInteger(info.geometryIndex, `THREE.BatchedMesh._instanceInfo[${instanceId}].geometryIndex`);
3211
+ const range = batchedGeometryRange(object, geometry, geometryId);
3212
+ if (range.count === 0)
3213
+ continue;
3214
+ const transform = multiplyMat4(baseTransform, batchedInstanceMatrix(object, instanceId));
3215
+ if (cullPerObject && batchedDrawOutsideFrustum(object, geometry, geometryId, range, transform, camera)) {
3216
+ continue;
3217
+ }
3218
+ draws.push({
3219
+ range,
3220
+ instanceId,
3221
+ instance: {
3222
+ transform,
3223
+ color: batchedInstanceColor(object, instanceId),
3224
+ },
3225
+ z: sortObjects ? batchedDrawDistanceZ(object, geometry, geometryId, range, transform, camera) : 0,
3226
+ });
3227
+ }
3228
+ if (!sortObjects || draws.length < 2)
3229
+ return draws;
3230
+ if (customSort) {
3231
+ return customSortedBatchedMeshDraws(object, draws, customSort, camera);
3232
+ }
3233
+ return draws
3234
+ .slice()
3235
+ .sort(batchedMeshUsesTransparentSort(object) ? compareBatchedDrawsTransparent : compareBatchedDrawsOpaque);
3236
+ }
3237
+ function batchedPerObjectFrustumCulled(object, camera) {
3238
+ if (!camera || camera.isArrayCamera === true)
3239
+ return false;
3240
+ return batchedOptionalBoolean(object.perObjectFrustumCulled, 'THREE.BatchedMesh.perObjectFrustumCulled', true);
3241
+ }
3242
+ function batchedSortObjects(object) {
3243
+ return batchedOptionalBoolean(object.sortObjects, 'THREE.BatchedMesh.sortObjects', true);
3244
+ }
3245
+ function batchedCustomSort(object) {
3246
+ if (object.customSort == null)
3247
+ return null;
3248
+ if (typeof object.customSort === 'function')
3249
+ return object.customSort;
3250
+ throw new TypeError('THREE.BatchedMesh.customSort must be a function or null.');
3251
+ }
3252
+ function customSortedBatchedMeshDraws(object, draws, customSort, camera) {
3253
+ const drawByInstance = new Map(draws.map((draw) => [draw.instanceId, draw]));
3254
+ const list = draws.map((draw) => ({
3255
+ start: draw.range.start,
3256
+ count: draw.range.count,
3257
+ z: draw.z,
3258
+ index: draw.instanceId,
3259
+ }));
3260
+ customSort.call(object, list, camera);
3261
+ if (list.length !== draws.length) {
3262
+ throw new Error(`THREE.BatchedMesh.customSort must keep ${draws.length} draw items; received ${list.length}.`);
3263
+ }
3264
+ const seenInstances = new Set();
3265
+ return list.map((item, itemIndex) => {
3266
+ if (!item || typeof item !== 'object') {
3267
+ throw new TypeError(`THREE.BatchedMesh.customSort list[${itemIndex}] must be an object.`);
3268
+ }
3269
+ const instanceId = batchedNonNegativeInteger(item.index, `THREE.BatchedMesh.customSort list[${itemIndex}].index`);
3270
+ if (seenInstances.has(instanceId)) {
3271
+ throw new Error(`THREE.BatchedMesh.customSort returned duplicate instance index ${instanceId}.`);
3272
+ }
3273
+ seenInstances.add(instanceId);
3274
+ const draw = drawByInstance.get(instanceId);
3275
+ if (!draw) {
3276
+ throw new Error(`THREE.BatchedMesh.customSort returned unknown instance index ${instanceId}.`);
3277
+ }
3278
+ return draw;
3279
+ });
3280
+ }
3281
+ function compareBatchedDrawsOpaque(a, b) {
3282
+ return a.z - b.z || a.instanceId - b.instanceId;
3283
+ }
3284
+ function compareBatchedDrawsTransparent(a, b) {
3285
+ return b.z - a.z || a.instanceId - b.instanceId;
3286
+ }
3287
+ function batchedMeshUsesTransparentSort(object) {
3288
+ const materials = Array.isArray(object.material)
3289
+ ? object.material
3290
+ : [object.material];
3291
+ return materials.some((material) => material?.transparent === true || finitePositive(material?.transmission));
3292
+ }
3293
+ function batchedDrawDistanceZ(object, geometry, geometryId, range, transform, camera) {
3294
+ if (!camera)
3295
+ return 0;
3296
+ const sphere = batchedGeometryBoundingSphere(object, geometry, geometryId, range);
3297
+ if (!sphere)
3298
+ return 0;
3299
+ const center = transformPoint(transform, sphere.center);
3300
+ const viewZ = viewSpaceZ(center, camera);
3301
+ return Number.isFinite(viewZ) ? -viewZ : 0;
3302
+ }
3303
+ function batchedDrawSortIndex(base, drawOrder) {
3304
+ return Math.max(0, Math.min(0xffffffff, base + drawOrder));
3305
+ }
3306
+ function batchedDrawOutsideFrustum(object, geometry, geometryId, range, transform, camera) {
3307
+ const sphere = batchedGeometryBoundingSphere(object, geometry, geometryId, range);
3308
+ if (!sphere)
3309
+ return false;
3310
+ return transformedSphereOutsideFrustum(camera, transform, sphere);
3311
+ }
3312
+ function batchedGeometryRange(object, geometry, geometryId) {
3313
+ const cachedRange = object._geometryInfo?.[geometryId];
3314
+ const cachedActive = cachedRange && typeof cachedRange === 'object' && !Array.isArray(cachedRange)
3315
+ ? cachedRange.active
3316
+ : null;
3317
+ if (cachedActive != null && !batchedOptionalBoolean(cachedActive, `THREE.BatchedMesh._geometryInfo[${geometryId}].active`, true)) {
3318
+ return { start: 0, count: 0 };
3319
+ }
3320
+ const range = typeof object.getGeometryRangeAt === 'function'
3321
+ ? object.getGeometryRangeAt(geometryId, {})
3322
+ : cachedRange ?? null;
3323
+ if (!range || typeof range !== 'object') {
3324
+ throw new Error(`THREE.BatchedMesh geometry range ${geometryId} is not readable. Use a real THREE.BatchedMesh or expand the batch before rendering.`);
3325
+ }
3326
+ const rawActive = range.active;
3327
+ if (rawActive != null && !batchedOptionalBoolean(rawActive, `THREE.BatchedMesh._geometryInfo[${geometryId}].active`, true)) {
3328
+ return { start: 0, count: 0 };
3329
+ }
3330
+ const startLabel = `THREE.BatchedMesh._geometryInfo[${geometryId}].start`;
3331
+ const countLabel = `THREE.BatchedMesh._geometryInfo[${geometryId}].count`;
3332
+ const start = batchedNonNegativeInteger(range.start, startLabel);
3333
+ const count = batchedNonNegativeInteger(range.count, countLabel);
3334
+ const limit = batchedGeometryRangeLimit(geometry);
3335
+ if (start > limit) {
3336
+ throw new RangeError(`${startLabel} must be less than or equal to packed geometry count (${limit}).`);
3337
+ }
3338
+ if (count > limit - start) {
3339
+ throw new RangeError(`${countLabel} must fit within packed geometry count (${limit}) from start ${start}.`);
3340
+ }
3341
+ return { start, count };
3342
+ }
3343
+ function batchedGeometryRangeLimit(geometry) {
3344
+ if (geometry.index)
3345
+ return (0, attributes_1.attributeCount)(geometry.index, 'geometry.index');
3346
+ const position = (0, attributes_1.getAttribute)(geometry, 'position');
3347
+ return position ? (0, attributes_1.attributeCount)(position, 'geometry.attributes.position') : 0;
3348
+ }
3349
+ function batchedGeometryBoundingSphere(object, geometry, geometryId, range) {
3350
+ const cached = object._geometryInfo?.[geometryId]?.boundingSphere;
3351
+ if (cached != null) {
3352
+ return batchedSphereLike(cached, `THREE.BatchedMesh._geometryInfo[${geometryId}].boundingSphere`);
3353
+ }
3354
+ const computed = batchedGeometryRangeBoundingSphere(geometry, range);
3355
+ return computed ? batchedSphereLike(computed, `THREE.BatchedMesh._geometryInfo[${geometryId}].computedBoundingSphere`) : null;
3356
+ }
3357
+ function batchedSphereLike(sphere, label) {
3358
+ return sphereLike(sphere, label);
3359
+ }
3360
+ function sphereLike(sphere, label) {
3361
+ if (!sphere || typeof sphere !== 'object') {
3362
+ throw new TypeError(`${label} must be a THREE.Sphere-like object.`);
3363
+ }
3364
+ const center = vec3Like(sphere.center);
3365
+ if (!center) {
3366
+ throw new TypeError(`${label}.center must be a finite Vector3-like value.`);
3367
+ }
3368
+ const radius = nonNegativeMaterialOrObjectNumber(sphere.radius, `${label}.radius`, 0);
3369
+ return { center, radius };
3370
+ }
3371
+ function batchedGeometryView(geometry, range, cache) {
3372
+ const signature = batchedGeometryViewSignature(geometry, range);
3373
+ if (cache && signature.cacheable) {
3374
+ let views = cache.batchedGeometryViews.get(geometry);
3375
+ if (!views) {
3376
+ views = new Map();
3377
+ cache.batchedGeometryViews.set(geometry, views);
3378
+ }
3379
+ const key = batchedGeometryViewKey(range);
3380
+ const cached = views.get(key);
3381
+ if (cached && sameBatchedGeometryViewSignature(cached.signature, signature)) {
3382
+ return cached.view;
3383
+ }
3384
+ const view = cached?.view ?? createBatchedGeometryView(geometry);
3385
+ updateBatchedGeometryView(view, geometry, range);
3386
+ views.set(key, { signature, view });
3387
+ return view;
3388
+ }
3389
+ const view = createBatchedGeometryView(geometry);
3390
+ updateBatchedGeometryView(view, geometry, range);
3391
+ return view;
3392
+ }
3393
+ function createBatchedGeometryView(geometry) {
3394
+ return Object.create(geometry);
3395
+ }
3396
+ function updateBatchedGeometryView(view, geometry, range) {
3397
+ view.drawRange = { start: range.start, count: range.count };
3398
+ view.boundingSphere = batchedGeometryRangeBoundingSphere(geometry, range);
3399
+ }
3400
+ function batchedGeometryViewSignature(geometry, range) {
3401
+ const signature = {
3402
+ cacheable: true,
3403
+ geometryVersion: geometry.version,
3404
+ rangeStart: range.start,
3405
+ rangeCount: range.count,
3406
+ position: attributeSignature((0, attributes_1.getAttribute)(geometry, 'position')),
3407
+ index: attributeSignature(geometry.index),
3408
+ };
3409
+ signature.cacheable = attributeSignatureCacheable(signature.position)
3410
+ && attributeSignatureCacheable(signature.index);
3411
+ return signature;
3412
+ }
3413
+ function sameBatchedGeometryViewSignature(a, b) {
3414
+ return a.cacheable === b.cacheable
3415
+ && a.geometryVersion === b.geometryVersion
3416
+ && a.rangeStart === b.rangeStart
3417
+ && a.rangeCount === b.rangeCount
3418
+ && sameAttributeSignature(a.position, b.position)
3419
+ && sameAttributeSignature(a.index, b.index);
3420
+ }
3421
+ function batchedGeometryViewKey(range) {
3422
+ return `${range.start}:${range.count}`;
3423
+ }
3424
+ function batchedGeometryRangeBoundingSphere(geometry, range) {
3425
+ const position = (0, attributes_1.getAttribute)(geometry, 'position');
3426
+ if (!position || range.count <= 0)
3427
+ return undefined;
3428
+ const index = geometry.index ? (0, attributes_1.readIndexAttribute)(geometry.index, 'geometry.index', position.count) : null;
3429
+ const start = Math.max(0, range.start);
3430
+ const end = Math.max(start, range.start + range.count);
3431
+ let minX = Number.POSITIVE_INFINITY;
3432
+ let minY = Number.POSITIVE_INFINITY;
3433
+ let minZ = Number.POSITIVE_INFINITY;
3434
+ let maxX = Number.NEGATIVE_INFINITY;
3435
+ let maxY = Number.NEGATIVE_INFINITY;
3436
+ let maxZ = Number.NEGATIVE_INFINITY;
3437
+ const vertexIndices = [];
3438
+ for (let offset = start; offset < end; offset += 1) {
3439
+ const vertexIndex = index ? index[offset] : offset;
3440
+ if (!Number.isInteger(vertexIndex) || vertexIndex < 0 || vertexIndex >= position.count)
3441
+ continue;
3442
+ vertexIndices.push(vertexIndex);
3443
+ const x = (0, attributes_1.attributeComponent)(position, vertexIndex, 0, 'geometry.attributes.position');
3444
+ const y = (0, attributes_1.attributeComponent)(position, vertexIndex, 1, 'geometry.attributes.position');
3445
+ const z = (0, attributes_1.attributeComponent)(position, vertexIndex, 2, 'geometry.attributes.position');
3446
+ minX = Math.min(minX, x);
3447
+ minY = Math.min(minY, y);
3448
+ minZ = Math.min(minZ, z);
3449
+ maxX = Math.max(maxX, x);
3450
+ maxY = Math.max(maxY, y);
3451
+ maxZ = Math.max(maxZ, z);
3452
+ }
3453
+ if (vertexIndices.length === 0)
3454
+ return undefined;
3455
+ const center = {
3456
+ x: (minX + maxX) * 0.5,
3457
+ y: (minY + maxY) * 0.5,
3458
+ z: (minZ + maxZ) * 0.5,
3459
+ };
3460
+ let radiusSq = 0;
3461
+ for (const vertexIndex of vertexIndices) {
3462
+ const dx = (0, attributes_1.attributeComponent)(position, vertexIndex, 0, 'geometry.attributes.position') - center.x;
3463
+ const dy = (0, attributes_1.attributeComponent)(position, vertexIndex, 1, 'geometry.attributes.position') - center.y;
3464
+ const dz = (0, attributes_1.attributeComponent)(position, vertexIndex, 2, 'geometry.attributes.position') - center.z;
3465
+ radiusSq = Math.max(radiusSq, dx * dx + dy * dy + dz * dz);
3466
+ }
3467
+ return { center, radius: Math.sqrt(radiusSq) };
3468
+ }
3469
+ function batchedInstanceMatrix(object, instanceId) {
3470
+ const data = batchedTextureImageData(object._matricesTexture, 'THREE.BatchedMesh._matricesTexture', instanceId);
3471
+ const offset = instanceId * 16;
3472
+ if (typeof data.length !== 'number' || data.length < offset + 16) {
3473
+ throw new Error(`THREE.BatchedMesh matrix texture is not readable for instance ${instanceId}. Use a real THREE.BatchedMesh or expand the batch before rendering.`);
3474
+ }
3475
+ const matrix = new Array(16);
3476
+ for (let component = 0; component < 16; component += 1) {
3477
+ matrix[component] = finiteArrayValue(data, offset + component, 'THREE.BatchedMesh._matricesTexture.image.data');
3478
+ }
3479
+ return matrix;
3480
+ }
3481
+ function batchedInstanceColor(object, instanceId) {
3482
+ const texture = object._colorsTexture;
3483
+ if (texture == null)
3484
+ return undefined;
3485
+ const data = batchedTextureImageData(texture, 'THREE.BatchedMesh._colorsTexture', instanceId);
3486
+ const offset = instanceId * 4;
3487
+ if (typeof data.length !== 'number' || data.length < offset + 4) {
3488
+ throw new Error(`THREE.BatchedMesh color texture is not readable for instance ${instanceId}. Use a real THREE.BatchedMesh or expand the batch before rendering.`);
3489
+ }
3490
+ return [
3491
+ finiteArrayValue(data, offset, 'THREE.BatchedMesh._colorsTexture.image.data'),
3492
+ finiteArrayValue(data, offset + 1, 'THREE.BatchedMesh._colorsTexture.image.data'),
3493
+ finiteArrayValue(data, offset + 2, 'THREE.BatchedMesh._colorsTexture.image.data'),
3494
+ finiteArrayValue(data, offset + 3, 'THREE.BatchedMesh._colorsTexture.image.data'),
3495
+ ];
3496
+ }
3497
+ function batchedTextureImageData(texture, label, instanceId) {
3498
+ if (!texture || typeof texture !== 'object') {
3499
+ throw new TypeError(`${label} must be a texture-like object for instance ${instanceId}.`);
3500
+ }
3501
+ const image = texture.image;
3502
+ if (!image || typeof image !== 'object') {
3503
+ throw new TypeError(`${label}.image must be an image-like object for instance ${instanceId}.`);
3504
+ }
3505
+ const data = image.data;
3506
+ if (!data || typeof data !== 'object') {
3507
+ throw new TypeError(`${label}.image.data must be an array-like object for instance ${instanceId}.`);
3508
+ }
3509
+ return data;
3510
+ }
3511
+ function batchedOptionalBoolean(value, label, fallback) {
3512
+ if (value == null)
3513
+ return fallback;
3514
+ if (typeof value === 'boolean')
3515
+ return value;
3516
+ throw new TypeError(`${label} must be a boolean.`);
3517
+ }
3518
+ function batchedNonNegativeInteger(value, label) {
3519
+ if (typeof value === 'number' && Number.isFinite(value) && Number.isInteger(value) && value >= 0) {
3520
+ return value;
3521
+ }
3522
+ throw new TypeError(`${label} must be a non-negative integer.`);
3523
+ }
3524
+ function finiteArrayValue(values, index, label) {
3525
+ const value = values[index];
3526
+ if (typeof value === 'number' && Number.isFinite(value))
3527
+ return value;
3528
+ throw new TypeError(`${label}[${index}] must be a finite number.`);
3529
+ }
3530
+ function appendNumberArray(out, values) {
3531
+ for (const value of values)
3532
+ out.push(value);
3533
+ }
3534
+ function createDashedLineExpansion(uvs, uvs2, colors) {
3535
+ return {
3536
+ positions: [],
3537
+ uvs: uvs ? [] : undefined,
3538
+ uvs2: uvs2 ? [] : undefined,
3539
+ colors: colors ? [] : undefined,
3540
+ };
3541
+ }
3542
+ function cameraFrustumIntersectsSphere(camera, center, radius) {
3543
+ const view = camera.matrixWorldInverse?.elements;
3544
+ const projection = camera.projectionMatrix?.elements;
3545
+ if (!view || view.length < 16 || !projection || projection.length < 16)
3546
+ return true;
3547
+ const viewProjection = multiplyMat4(projection, view);
3548
+ const planes = [
3549
+ [viewProjection[3] + viewProjection[0], viewProjection[7] + viewProjection[4], viewProjection[11] + viewProjection[8], viewProjection[15] + viewProjection[12]],
3550
+ [viewProjection[3] - viewProjection[0], viewProjection[7] - viewProjection[4], viewProjection[11] - viewProjection[8], viewProjection[15] - viewProjection[12]],
3551
+ [viewProjection[3] + viewProjection[1], viewProjection[7] + viewProjection[5], viewProjection[11] + viewProjection[9], viewProjection[15] + viewProjection[13]],
3552
+ [viewProjection[3] - viewProjection[1], viewProjection[7] - viewProjection[5], viewProjection[11] - viewProjection[9], viewProjection[15] - viewProjection[13]],
3553
+ [viewProjection[3] + viewProjection[2], viewProjection[7] + viewProjection[6], viewProjection[11] + viewProjection[10], viewProjection[15] + viewProjection[14]],
3554
+ [viewProjection[3] - viewProjection[2], viewProjection[7] - viewProjection[6], viewProjection[11] - viewProjection[10], viewProjection[15] - viewProjection[14]],
3555
+ ];
3556
+ for (const [a, b, c, d] of planes) {
3557
+ const length = Math.hypot(a, b, c);
3558
+ if (!Number.isFinite(length) || length <= 1e-8)
3559
+ continue;
3560
+ const distance = (a * center[0] + b * center[1] + c * center[2] + d) / length;
3561
+ if (distance < -radius)
3562
+ return false;
3563
+ }
3564
+ return true;
3565
+ }
3566
+ function lineSegmentsWithDistances(positions, source, start, end, object, lineDistance) {
3567
+ const count = end - start;
3568
+ const segments = [];
3569
+ if (count < 2)
3570
+ return segments;
3571
+ if (object.isLineSegments === true) {
3572
+ let cumulative = 0;
3573
+ const aligned = count - (count % 2);
3574
+ for (let i = 0; i < aligned; i += 2) {
3575
+ const a = source[start + i];
3576
+ const b = source[start + i + 1];
3577
+ const length = vertexDistance(positions, a, b);
3578
+ const d0 = lineDistance ? (0, attributes_1.attributeComponent)(lineDistance, a, 0, 'geometry.attributes.lineDistance') : cumulative;
3579
+ const d1 = lineDistance ? (0, attributes_1.attributeComponent)(lineDistance, b, 0, 'geometry.attributes.lineDistance') : d0 + length;
3580
+ segments.push({ a, b, d0, d1 });
3581
+ cumulative = d1;
3582
+ }
3583
+ return segments;
3584
+ }
3585
+ let previous = source[start];
3586
+ let previousDistance = lineDistance ? (0, attributes_1.attributeComponent)(lineDistance, previous, 0, 'geometry.attributes.lineDistance') : 0;
3587
+ for (let i = 1; i < count; i += 1) {
3588
+ const current = source[start + i];
3589
+ const length = vertexDistance(positions, previous, current);
3590
+ const currentDistance = lineDistance ? (0, attributes_1.attributeComponent)(lineDistance, current, 0, 'geometry.attributes.lineDistance') : previousDistance + length;
3591
+ segments.push({ a: previous, b: current, d0: previousDistance, d1: currentDistance });
3592
+ previous = current;
3593
+ previousDistance = currentDistance;
3594
+ }
3595
+ if (object.isLineLoop === true && count >= 2) {
3596
+ const first = source[start];
3597
+ segments.push({
3598
+ a: previous,
3599
+ b: first,
3600
+ d0: previousDistance,
3601
+ d1: lineDistance
3602
+ ? (0, attributes_1.attributeComponent)(lineDistance, first, 0, 'geometry.attributes.lineDistance')
3603
+ : previousDistance + vertexDistance(positions, previous, first),
3604
+ });
3605
+ }
3606
+ return segments;
3607
+ }
3608
+ function appendDashedSegment(out, positions, uvs, uvs2, colors, segment, scale, dashSize, totalSize) {
3609
+ const s0 = segment.d0 * scale;
3610
+ const s1 = segment.d1 * scale;
3611
+ const span = s1 - s0;
3612
+ const direction = Math.sign(span);
3613
+ if (Math.abs(span) <= 1e-6 || direction === 0)
3614
+ return;
3615
+ let cursor = s0;
3616
+ let guard = 0;
3617
+ while ((direction > 0 ? cursor < s1 - 1e-6 : cursor > s1 + 1e-6) && guard < 10000) {
3618
+ guard += 1;
3619
+ const cycle = Math.floor(cursor / totalSize);
3620
+ const cycleStart = cycle * totalSize;
3621
+ const inCycle = cursor - cycleStart;
3622
+ const visible = inCycle <= dashSize;
3623
+ const boundary = direction > 0
3624
+ ? cycleStart + (visible ? dashSize : totalSize)
3625
+ : cycleStart + (visible ? 0 : dashSize);
3626
+ const next = direction > 0
3627
+ ? Math.min(s1, boundary <= cursor + 1e-6 ? cursor + 1e-6 : boundary)
3628
+ : Math.max(s1, boundary >= cursor - 1e-6 ? cursor - 1e-6 : boundary);
3629
+ const hasVisibleSpan = direction > 0 ? next > cursor + 1e-6 : next < cursor - 1e-6;
3630
+ if (visible && hasVisibleSpan) {
3631
+ const t0 = (cursor - s0) / span;
3632
+ const t1 = (next - s0) / span;
3633
+ appendInterpolatedLine(out, positions, uvs, uvs2, colors, segment.a, segment.b, t0, t1);
3634
+ }
3635
+ cursor = next;
3636
+ }
3637
+ }
3638
+ function appendInterpolatedLine(out, positions, uvs, uvs2, colors, a, b, t0, t1) {
3639
+ appendInterpolatedAttribute(out.positions, positions, 3, a, b, t0);
3640
+ appendInterpolatedAttribute(out.positions, positions, 3, a, b, t1);
3641
+ if (out.uvs && uvs) {
3642
+ appendInterpolatedAttribute(out.uvs, uvs, 2, a, b, t0);
3643
+ appendInterpolatedAttribute(out.uvs, uvs, 2, a, b, t1);
3644
+ }
3645
+ if (out.uvs2 && uvs2) {
3646
+ appendInterpolatedAttribute(out.uvs2, uvs2, 2, a, b, t0);
3647
+ appendInterpolatedAttribute(out.uvs2, uvs2, 2, a, b, t1);
3648
+ }
3649
+ if (out.colors && colors) {
3650
+ appendInterpolatedAttribute(out.colors, colors, 4, a, b, t0);
3651
+ appendInterpolatedAttribute(out.colors, colors, 4, a, b, t1);
3652
+ }
3653
+ }
3654
+ function appendInterpolatedAttribute(out, values, itemSize, a, b, t) {
3655
+ const aBase = a * itemSize;
3656
+ const bBase = b * itemSize;
3657
+ for (let component = 0; component < itemSize; component += 1) {
3658
+ const av = values[aBase + component];
3659
+ const bv = values[bBase + component];
3660
+ out.push(av + (bv - av) * t);
3661
+ }
3662
+ }
3663
+ function vertexDistance(positions, a, b) {
3664
+ const dx = positions[a * 3] - positions[b * 3];
3665
+ const dy = positions[a * 3 + 1] - positions[b * 3 + 1];
3666
+ const dz = positions[a * 3 + 2] - positions[b * 3 + 2];
3667
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
3668
+ }
3669
+ function meshInstances(object, baseTransform, cache) {
3670
+ if (object.isInstancedMesh !== true) {
3671
+ return [{ transform: baseTransform }];
3672
+ }
3673
+ const instanceMatrix = object.instanceMatrix;
3674
+ if (!instanceMatrix || instanceMatrix.count == null)
3675
+ return [];
3676
+ const instanceMatrixCount = (0, attributes_1.attributeCount)(instanceMatrix, 'InstancedMesh.instanceMatrix');
3677
+ const count = (0, math_1.clampInteger)(integerCountOrDefault(object.count, 'InstancedMesh.count', instanceMatrixCount), 0, instanceMatrixCount);
3678
+ const signature = instancedMeshSignature(object, instanceMatrix, count);
3679
+ let localInstances;
3680
+ if (cache && signature.cacheable) {
3681
+ const cached = cache.instancedMeshes.get(object);
3682
+ if (cached && sameInstancedMeshSignature(cached.signature, signature)) {
3683
+ localInstances = cached.localInstances;
3684
+ }
3685
+ }
3686
+ if (!localInstances) {
3687
+ localInstances = readLocalInstancedMeshInstances(object, instanceMatrix, count);
3688
+ if (cache && signature.cacheable) {
3689
+ cache.instancedMeshes.set(object, { signature, localInstances });
3690
+ }
3691
+ }
3692
+ return localInstances.map((instance) => ({
3693
+ transform: multiplyMat4(baseTransform, instance.transform),
3694
+ color: instance.color,
3695
+ }));
3696
+ }
3697
+ function instancedMeshSignature(object, instanceMatrix, count) {
3698
+ const signature = {
3699
+ cacheable: true,
3700
+ count,
3701
+ instanceMatrix: attributeSignature(instanceMatrix),
3702
+ instanceColor: attributeSignature(object.instanceColor),
3703
+ };
3704
+ signature.cacheable = attributeSignatureCacheable(signature.instanceMatrix)
3705
+ && attributeSignatureCacheable(signature.instanceColor);
3706
+ return signature;
3707
+ }
3708
+ function sameInstancedMeshSignature(a, b) {
3709
+ return a.cacheable === b.cacheable
3710
+ && a.count === b.count
3711
+ && sameAttributeSignature(a.instanceMatrix, b.instanceMatrix)
3712
+ && sameAttributeSignature(a.instanceColor, b.instanceColor);
3713
+ }
3714
+ function readLocalInstancedMeshInstances(object, instanceMatrix, count) {
3715
+ const instances = new Array(count);
3716
+ for (let i = 0; i < count; i += 1) {
3717
+ instances[i] = {
3718
+ transform: readMat4Attribute(instanceMatrix, i),
3719
+ color: readInstanceColor(object.instanceColor, i),
3720
+ };
3721
+ }
3722
+ return instances;
3723
+ }
3724
+ function readMat4Attribute(attribute, index) {
3725
+ if (!attribute)
3726
+ return math_1.IDENTITY_4X4.slice();
3727
+ const matrix = new Array(16);
3728
+ for (let component = 0; component < 16; component += 1) {
3729
+ matrix[component] = (0, attributes_1.attributeComponent)(attribute, index, component, 'InstancedMesh.instanceMatrix');
3730
+ }
3731
+ return matrix;
3732
+ }
3733
+ function readInstanceColor(attribute, index) {
3734
+ if (!attribute || index >= (0, attributes_1.attributeCount)(attribute, 'InstancedMesh.instanceColor'))
3735
+ return undefined;
3736
+ return [
3737
+ (0, attributes_1.attributeComponent)(attribute, index, 0, 'InstancedMesh.instanceColor'),
3738
+ (0, attributes_1.attributeComponent)(attribute, index, 1, 'InstancedMesh.instanceColor'),
3739
+ (0, attributes_1.attributeComponent)(attribute, index, 2, 'InstancedMesh.instanceColor'),
3740
+ attribute.itemSize && attribute.itemSize >= 4 ? (0, attributes_1.attributeComponent)(attribute, index, 3, 'InstancedMesh.instanceColor') : 1,
3741
+ ];
3742
+ }
3743
+ function instanceColor(baseColor, instance) {
3744
+ if (!instance.color)
3745
+ return baseColor;
3746
+ return [
3747
+ baseColor[0] * instance.color[0],
3748
+ baseColor[1] * instance.color[1],
3749
+ baseColor[2] * instance.color[2],
3750
+ baseColor[3] * instance.color[3],
3751
+ ];
3752
+ }
3753
+ function multiplyMat4(a, b) {
3754
+ const out = new Array(16);
3755
+ for (let col = 0; col < 4; col += 1) {
3756
+ for (let row = 0; row < 4; row += 1) {
3757
+ out[col * 4 + row] =
3758
+ a[row] * b[col * 4]
3759
+ + a[4 + row] * b[col * 4 + 1]
3760
+ + a[8 + row] * b[col * 4 + 2]
3761
+ + a[12 + row] * b[col * 4 + 3];
3762
+ }
3763
+ }
3764
+ return out;
3765
+ }
192
3766
  function rangeIndices(count) {
193
3767
  const out = new Array(count);
194
3768
  for (let i = 0; i < count; i++)