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