fl-web-component 1.4.7 → 1.4.9-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +1 -28
  2. package/dist/fl-web-component.common.1.js +2 -2
  3. package/dist/fl-web-component.common.1.js.map +1 -1
  4. package/dist/fl-web-component.common.2.js.map +1 -1
  5. package/dist/fl-web-component.common.js +77420 -47296
  6. package/dist/fl-web-component.common.js.map +1 -1
  7. package/dist/fl-web-component.css +1 -1
  8. package/package.json +12 -4
  9. package/packages/components/com-flcanvas/components/entityFormatting.js +9 -1
  10. package/packages/components/com-graphics/box.json +77 -0
  11. package/packages/components/com-graphics/component/ann-tool.vue +465 -0
  12. package/packages/components/com-graphics/index copy.vue +1679 -0
  13. package/packages/components/com-graphics/index.vue +3890 -301
  14. package/packages/components/com-graphics/pid.vue +210 -44
  15. package/packages/components/com-graphics/test.html +127 -0
  16. package/packages/components/com-tiles/index.vue +187 -0
  17. package/packages/utils/StreamLoader.js +1498 -0
  18. package/packages/utils/StreamLoaderParser.worker.js +595 -0
  19. package/patches/camera-controls+2.9.0.patch +63 -63
  20. package/src/main.js +9 -1
  21. package/src/static/ann-img/mark_circle@2x.png +0 -0
  22. package/src/static/ann-img/mark_clear@2x.png +0 -0
  23. package/src/static/ann-img/mark_cloud@2x.png +0 -0
  24. package/src/static/ann-img/mark_color@2x.png +0 -0
  25. package/src/static/ann-img/mark_eraser@2x.png +0 -0
  26. package/src/static/ann-img/mark_exit@2x.png +0 -0
  27. package/src/static/ann-img/mark_finish@2x.png +0 -0
  28. package/src/static/ann-img/mark_font@2x.png +0 -0
  29. package/src/static/ann-img/mark_polyline@2x.png +0 -0
  30. package/src/static/ann-img/mark_rectangle@2x.png +0 -0
  31. package/src/static/ann-img/mark_zoomin@2x.png +0 -0
  32. package/src/static/ann-img/mark_zoomout@2x.png +0 -0
  33. package/src/utils/cloud.js +110 -0
  34. package/src/utils/cursor.js +10 -0
  35. package/src/utils/flgltf-parser.js +245 -193
  36. package/src/utils/instance-parser.js +718 -170
  37. package/dist/fl-web-component.common.3.js +0 -7740
  38. package/dist/fl-web-component.common.3.js.map +0 -1
@@ -0,0 +1,595 @@
1
+ import { gunzipSync } from 'fflate';
2
+
3
+ self.__streamLoaderStates = self.__streamLoaderStates || new Map();
4
+
5
+ const utf8Decoder = new TextDecoder('utf-8');
6
+
7
+ self.onmessage = function (e) {
8
+ const { id, type, data } = e.data;
9
+ if (type === 'parseBufferData') {
10
+ try {
11
+ const { buffer, prefixIdKey } = data;
12
+ const result = parseBufferData(buffer, prefixIdKey);
13
+ // Transfer the buffer back if possible, but we constructed new arrays.
14
+ // We rely on structured cloning.
15
+ self.postMessage({ id, type: 'success', result });
16
+ } catch (error) {
17
+ self.postMessage({ id, type: 'error', error: error.message });
18
+ }
19
+ } else if (type === 'streamInit') {
20
+ try {
21
+ const { streamId, prefixIdKey } = data || {};
22
+ if (!streamId) throw new Error('Missing streamId');
23
+ const state = createStreamState(prefixIdKey);
24
+ self.__streamLoaderStates.set(streamId, state);
25
+ self.postMessage({ id, type: 'success', result: true });
26
+ } catch (error) {
27
+ self.postMessage({ id, type: 'error', error: error.message });
28
+ }
29
+ } else if (type === 'streamPush') {
30
+ try {
31
+ const { streamId, chunk, byteOffset = 0, byteLength, prefixIdKey } = data || {};
32
+ if (!streamId) throw new Error('Missing streamId');
33
+ let state = self.__streamLoaderStates.get(streamId);
34
+ if (!state) {
35
+ state = createStreamState(prefixIdKey);
36
+ self.__streamLoaderStates.set(streamId, state);
37
+ }
38
+ const view = new Uint8Array(chunk, byteOffset, byteLength ?? chunk.byteLength - byteOffset);
39
+ const result = parseStreamChunk(state, view);
40
+ self.postMessage({ id, type: 'success', result });
41
+ } catch (error) {
42
+ self.postMessage({ id, type: 'error', error: error.message });
43
+ }
44
+ } else if (type === 'streamFlush') {
45
+ try {
46
+ const { streamId } = data || {};
47
+ if (!streamId) throw new Error('Missing streamId');
48
+ const state = self.__streamLoaderStates.get(streamId);
49
+ if (!state) {
50
+ self.postMessage({ id, type: 'success', result: { meshes: [], primitives: [] } });
51
+ return;
52
+ }
53
+ const result = parseStreamChunk(state, null, true);
54
+ self.postMessage({ id, type: 'success', result });
55
+ } catch (error) {
56
+ self.postMessage({ id, type: 'error', error: error.message });
57
+ }
58
+ } else if (type === 'streamDispose') {
59
+ try {
60
+ const { streamId } = data || {};
61
+ if (streamId) self.__streamLoaderStates.delete(streamId);
62
+ self.postMessage({ id, type: 'success', result: true });
63
+ } catch (error) {
64
+ self.postMessage({ id, type: 'error', error: error.message });
65
+ }
66
+ } else if (type === 'buildInstancedMapping') {
67
+ try {
68
+ const { instances, drawObjs } = data || {};
69
+ const result = buildInstancedMapping(instances || [], drawObjs || []);
70
+ self.postMessage({ id, type: 'success', result });
71
+ } catch (error) {
72
+ self.postMessage({ id, type: 'error', error: error.message });
73
+ }
74
+ } else if (type === 'occlusionScan') {
75
+ const transfer = data && data.buffer ? [data.buffer] : [];
76
+ try {
77
+ const { buffer, sw, sh, stride, maxIdx, minSampleCount } = data || {};
78
+ const result = scanOcclusionBuffer(buffer, sw, sh, stride, maxIdx, minSampleCount);
79
+ self.postMessage({ id, type: 'success', result, buffer }, transfer);
80
+ } catch (error) {
81
+ const buffer = data && data.buffer ? data.buffer : null;
82
+ self.postMessage({ id, type: 'error', error: error.message, buffer }, transfer);
83
+ }
84
+ }
85
+ };
86
+
87
+ function createStreamState(prefixIdKey) {
88
+ return {
89
+ prefixIdKey: prefixIdKey || '',
90
+ buffer: new Uint8Array(),
91
+ pendingPrimitives: new Map(),
92
+ expectingPrimitive: true,
93
+ isFullProps: true,
94
+ };
95
+ }
96
+
97
+ function parseStreamChunk(state, chunkView, flushing = false) {
98
+ if (chunkView && chunkView.length > 0) {
99
+ const merged = new Uint8Array(state.buffer.length + chunkView.length);
100
+ merged.set(state.buffer);
101
+ merged.set(chunkView, state.buffer.length);
102
+ state.buffer = merged;
103
+ }
104
+
105
+ const meshes = [];
106
+ const primitives = [];
107
+
108
+ while (state.buffer.length > 0) {
109
+ if (state.expectingPrimitive) {
110
+ if (state.buffer.length < 12) break;
111
+
112
+ try {
113
+ const dataView = new DataView(
114
+ state.buffer.buffer,
115
+ state.buffer.byteOffset,
116
+ state.buffer.byteLength
117
+ );
118
+ const primitiveResult = parsePrimitive(dataView, state.buffer, 0, state.isFullProps);
119
+ const primitiveData = primitiveResult.primitive;
120
+
121
+ _applyPrefixId(primitiveData, 'id', null, state.prefixIdKey);
122
+ _applyPrefixId(primitiveData, 'material', null, state.prefixIdKey);
123
+
124
+ if (state.isFullProps) {
125
+ const { position, normal, posindex, nolindex, indices } =
126
+ parsePrimitiveData(primitiveData);
127
+ const formatted = formatPrimitiveData({ position, normal, posindex, nolindex });
128
+ delete primitiveData.nolindex;
129
+ delete primitiveData.posindex;
130
+ primitiveData.position = formatted.position;
131
+ primitiveData.normal = formatted.normal;
132
+ primitiveData.indices = indices;
133
+ }
134
+
135
+ state.pendingPrimitives.set(primitiveData.id, primitiveData);
136
+ state.expectingPrimitive = false;
137
+ state.buffer = state.buffer.slice(primitiveResult.offset);
138
+ } catch (e) {
139
+ break;
140
+ }
141
+ } else {
142
+ if (state.buffer.length < 4) break;
143
+
144
+ const length = new DataView(state.buffer.buffer, state.buffer.byteOffset).getUint32(0, false);
145
+ if (length > 10 * 1024 * 1024) {
146
+ state.expectingPrimitive = true;
147
+ continue;
148
+ }
149
+
150
+ const totalLen = 4 + length;
151
+ if (state.buffer.length < totalLen) break;
152
+
153
+ const data = state.buffer.slice(4, totalLen);
154
+ let mesh = null;
155
+
156
+ try {
157
+ const jsonStr = utf8Decoder.decode(data);
158
+ mesh = JSON.parse(jsonStr);
159
+ } catch (e) {
160
+ state.expectingPrimitive = true;
161
+ continue;
162
+ }
163
+
164
+ if (mesh) {
165
+ state.isFullProps = true;
166
+
167
+ _applyPrefixId(mesh, 'id', null, state.prefixIdKey);
168
+ if (Array.isArray(mesh)) {
169
+ mesh.forEach(item => {
170
+ _applyPrefixId(item.primitives, 'prmid', item.documentId, state.prefixIdKey);
171
+ });
172
+ } else {
173
+ _applyPrefixId(mesh.primitives, 'prmid', mesh.documentId, state.prefixIdKey);
174
+ }
175
+
176
+ const docId = mesh.id;
177
+ const meshToPrimId = mesh.primitives.map(item => item.prmid);
178
+ const hasPendingPrimitive = meshToPrimId.some(item => state.pendingPrimitives.has(item));
179
+ const hasPendingMesh = state.pendingPrimitives.has(docId);
180
+
181
+ if (hasPendingMesh || hasPendingPrimitive) {
182
+ let primitiveData = state.pendingPrimitives.get(docId);
183
+ if (!hasPendingMesh && hasPendingPrimitive) {
184
+ primitiveData = state.pendingPrimitives.get(meshToPrimId[0]);
185
+ }
186
+ meshes.push(mesh);
187
+ primitives.push(primitiveData);
188
+ }
189
+
190
+ state.expectingPrimitive = false;
191
+ state.buffer = state.buffer.slice(totalLen);
192
+ }
193
+ }
194
+ }
195
+
196
+ if (flushing) {
197
+ state.buffer = new Uint8Array();
198
+ }
199
+
200
+ return { meshes, primitives };
201
+ }
202
+
203
+ function parseBufferData(buffer, prefixIdKey) {
204
+ let uint8Array = null;
205
+ let dataView = null;
206
+ let meshJsonBytes = null;
207
+ let primitives = [];
208
+ let mesh = null;
209
+
210
+ try {
211
+ uint8Array = new Uint8Array(buffer);
212
+ dataView = new DataView(buffer);
213
+
214
+ let offset = 0;
215
+ const meshJsonLength = dataView.getInt32(offset, false);
216
+ offset += 4;
217
+
218
+ meshJsonBytes = new Uint8Array(uint8Array.buffer.slice(offset, offset + meshJsonLength));
219
+ offset += meshJsonLength;
220
+
221
+ const meshJsonStr = utf8Decoder.decode(meshJsonBytes);
222
+ meshJsonBytes = null;
223
+
224
+ try {
225
+ mesh = JSON.parse(meshJsonStr);
226
+ _applyPrefixId(mesh, 'id', null, prefixIdKey);
227
+ if (Array.isArray(mesh)) {
228
+ mesh.forEach(item => {
229
+ _applyPrefixId(item.primitives, 'prmid', item.documentId, prefixIdKey);
230
+ });
231
+ } else {
232
+ _applyPrefixId(mesh.primitives, 'prmid', mesh.documentId, prefixIdKey);
233
+ }
234
+ } catch (e) {
235
+ console.error('JSON 解析失败:', e, meshJsonStr);
236
+ throw new Error('JSON解析失败');
237
+ }
238
+
239
+ while (offset < uint8Array.length) {
240
+ try {
241
+ const result = parsePrimitive(dataView, uint8Array, offset);
242
+ if (!result) break;
243
+ primitives.push(result.primitive);
244
+ offset = result.offset;
245
+ } catch (e) {
246
+ console.error('解析失败,偏移:', offset, e);
247
+ break;
248
+ }
249
+ }
250
+
251
+ for (let i = 0; i < primitives.length; i++) {
252
+ const primitive = primitives[i];
253
+ _applyPrefixId(primitive, 'id', null, prefixIdKey);
254
+ _applyPrefixId(primitive, 'material', null, prefixIdKey);
255
+ const { position, normal, posindex, nolindex, indices } =
256
+ parsePrimitiveData(primitive);
257
+ const formatted = formatPrimitiveData({ position, normal, posindex, nolindex });
258
+ delete primitive.nolindex;
259
+ delete primitive.posindex;
260
+ primitive.position = formatted.position;
261
+ primitive.normal = formatted.normal;
262
+ primitive.indices = indices;
263
+ }
264
+
265
+ return { mesh, primitives };
266
+ } catch (error) {
267
+ throw error;
268
+ }
269
+ }
270
+
271
+ function _applyPrefixId(target, propKey, customPrefixId, prefixIdKey) {
272
+ if (!prefixIdKey || !target) return;
273
+
274
+ if (Array.isArray(target)) {
275
+ target.forEach(item => {
276
+ const prefixId = customPrefixId || item[prefixIdKey];
277
+ if (item && prefixId && item[propKey] != null) {
278
+ item[propKey] = `${item[propKey]}:${prefixId}`;
279
+ }
280
+ });
281
+ } else if ((customPrefixId || target[prefixIdKey]) && target[propKey] != null) {
282
+ const prefixId = customPrefixId || target[prefixIdKey];
283
+ target[propKey] = `${target[propKey]}:${prefixId}`;
284
+ }
285
+ }
286
+
287
+ function parsePrimitive(dataView, uint8Array, offset, isFullProps = true) {
288
+ const primitive = {};
289
+ if (dataView.byteLength < offset + 12) {
290
+ throw new Error('Insufficient data for primitive header');
291
+ }
292
+ primitive.id = dataView.getInt32(offset, false);
293
+ offset += 4;
294
+
295
+ if (dataView.byteLength < offset + 4) {
296
+ throw new Error('Insufficient data for GeomText length');
297
+ }
298
+ const documentTextLen = dataView.getUint32(offset, false);
299
+ offset += 4;
300
+ if (documentTextLen === 0xffffffff) {
301
+ primitive.documentId = null;
302
+ } else if (documentTextLen > 0) {
303
+ if (dataView.byteLength < offset + documentTextLen) {
304
+ throw new Error('Insufficient data for GeomText content');
305
+ }
306
+ const textBytes = uint8Array.subarray(offset, offset + documentTextLen);
307
+ primitive.documentId = utf8Decoder.decode(textBytes);
308
+ offset += documentTextLen;
309
+ } else {
310
+ primitive.documentId = '';
311
+ }
312
+
313
+ primitive.material = dataView.getInt32(offset, false);
314
+ offset += 4;
315
+
316
+ if (dataView.byteLength < offset + 4) {
317
+ throw new Error('Insufficient data for GeomText length');
318
+ }
319
+ const geomTextLen = dataView.getUint32(offset, false);
320
+ offset += 4;
321
+ if (geomTextLen === 0xffffffff) {
322
+ primitive.geomText = null;
323
+ } else if (geomTextLen > 0) {
324
+ if (dataView.byteLength < offset + geomTextLen) {
325
+ throw new Error('Insufficient data for GeomText content');
326
+ }
327
+ const textBytes = uint8Array.subarray(offset, offset + geomTextLen);
328
+ primitive.geomText = utf8Decoder.decode(textBytes);
329
+ offset += geomTextLen;
330
+ } else {
331
+ primitive.geomText = '';
332
+ }
333
+
334
+ primitive.isCompressed = dataView.getInt32(offset, false);
335
+ offset += 4;
336
+
337
+ if (isFullProps) {
338
+ if (dataView.byteLength < offset + 4)
339
+ throw new Error('Insufficient data for Position length');
340
+ const positionLen = dataView.getUint32(offset, false);
341
+ offset += 4;
342
+ if (positionLen === 0xffffffff) {
343
+ primitive.position = null;
344
+ } else {
345
+ if (dataView.byteLength < offset + positionLen)
346
+ throw new Error('Insufficient data for Position content');
347
+ primitive.position = uint8Array.subarray(offset, offset + positionLen);
348
+ offset += positionLen;
349
+ }
350
+ }
351
+
352
+ if (isFullProps) {
353
+ if (dataView.byteLength < offset + 4) throw new Error('Insufficient data for Normal length');
354
+ const normalLen = dataView.getUint32(offset, false);
355
+ offset += 4;
356
+ if (normalLen === 0xffffffff) {
357
+ primitive.normal = null;
358
+ } else {
359
+ if (dataView.byteLength < offset + normalLen)
360
+ throw new Error('Insufficient data for Normal content');
361
+ primitive.normal = uint8Array.subarray(offset, offset + normalLen);
362
+ offset += normalLen;
363
+ }
364
+ }
365
+
366
+ if (isFullProps) {
367
+ if (dataView.byteLength < offset + 4) throw new Error('Insufficient data for Indices length');
368
+ const indicesLen = dataView.getUint32(offset, false);
369
+ offset += 4;
370
+ if (indicesLen === 0xffffffff) {
371
+ primitive.indices = null;
372
+ } else {
373
+ if (dataView.byteLength < offset + indicesLen)
374
+ throw new Error('Insufficient data for Indices content');
375
+ primitive.indices = uint8Array.subarray(offset, offset + indicesLen);
376
+ offset += indicesLen;
377
+ }
378
+ }
379
+
380
+ if (isFullProps) {
381
+ if (dataView.byteLength < offset + 4)
382
+ throw new Error('Insufficient data for Posindex length');
383
+ const posindexLen = dataView.getUint32(offset, false);
384
+ offset += 4;
385
+ if (posindexLen === 0xffffffff) {
386
+ primitive.posindex = null;
387
+ } else {
388
+ if (dataView.byteLength < offset + posindexLen)
389
+ throw new Error('Insufficient data for Posindex content');
390
+ primitive.posindex = uint8Array.subarray(offset, offset + posindexLen);
391
+ offset += posindexLen;
392
+ }
393
+ }
394
+
395
+ if (isFullProps) {
396
+ if (dataView.byteLength < offset + 4)
397
+ throw new Error('Insufficient data for Nolindex length');
398
+ const nolindexLen = dataView.getUint32(offset, false);
399
+ offset += 4;
400
+ if (nolindexLen === 0xffffffff) {
401
+ primitive.nolindex = null;
402
+ } else {
403
+ if (dataView.byteLength < offset + nolindexLen)
404
+ throw new Error('Insufficient data for Nolindex content');
405
+ primitive.nolindex = uint8Array.subarray(offset, offset + nolindexLen);
406
+ offset += nolindexLen;
407
+ }
408
+ }
409
+
410
+ if (dataView.byteLength < offset + 4) throw new Error('Insufficient data for DataType length');
411
+ const dataTypeLen = dataView.getUint32(offset, false);
412
+ offset += 4;
413
+ if (dataTypeLen === 0xffffffff) {
414
+ primitive.dataType = null;
415
+ } else if (dataTypeLen > 0) {
416
+ if (dataView.byteLength < offset + dataTypeLen)
417
+ throw new Error('Insufficient data for DataType content');
418
+ const textBytes = uint8Array.subarray(offset, offset + dataTypeLen);
419
+ primitive.dataType = utf8Decoder.decode(textBytes);
420
+ offset += dataTypeLen;
421
+ } else {
422
+ primitive.dataType = '';
423
+ }
424
+
425
+ if (dataView.byteLength < offset + 4) throw new Error('Insufficient data for Min count');
426
+ const minCount = dataView.getUint32(offset, false);
427
+ offset += 4;
428
+ if (minCount === 0xffffffff) {
429
+ primitive.min = null;
430
+ } else {
431
+ if (dataView.byteLength < offset + minCount * 8)
432
+ throw new Error('Insufficient data for Min values');
433
+ primitive.min = [];
434
+ for (let i = 0; i < minCount; i++) {
435
+ primitive.min.push(dataView.getFloat64(offset, false));
436
+ offset += 8;
437
+ }
438
+ }
439
+
440
+ if (dataView.byteLength < offset + 4) throw new Error('Insufficient data for Max count');
441
+ const maxCount = dataView.getUint32(offset, false);
442
+ offset += 4;
443
+ if (maxCount === 0xffffffff) {
444
+ primitive.max = null;
445
+ } else {
446
+ if (dataView.byteLength < offset + maxCount * 8)
447
+ throw new Error('Insufficient data for Max values');
448
+ primitive.max = [];
449
+ for (let i = 0; i < maxCount; i++) {
450
+ primitive.max.push(dataView.getFloat64(offset, false));
451
+ offset += 8;
452
+ }
453
+ }
454
+
455
+ if (dataView.byteLength < offset + 4) throw new Error('Insufficient data for GeomType');
456
+ primitive.geomType = dataView.getInt32(offset, false);
457
+ offset += 4;
458
+
459
+ return { primitive, offset };
460
+ }
461
+
462
+ function parsePrimitiveData(primitive) {
463
+ const keyOrder = ['position', 'normal', 'indices', 'posindex', 'nolindex'];
464
+ const typeMap = {
465
+ 0: { bytes: 4, getter: 'getFloat32' },
466
+ 1: { bytes: 4, getter: 'getInt32' },
467
+ 2: { bytes: 2, getter: 'getInt16' },
468
+ };
469
+
470
+ const parsedResult = {};
471
+ const dataTypeString = primitive.dataType;
472
+
473
+ for (let i = 0; i < keyOrder.length; i++) {
474
+ const key = keyOrder[i];
475
+ const typeCode = dataTypeString[i];
476
+ const typeInfo = typeMap[typeCode];
477
+ const uint8Array = primitive.isCompressed === 1 || primitive.isCompressed === undefined ? gunzipSync(primitive[key]) : primitive[key];
478
+ if (!typeInfo || !uint8Array) {
479
+ // console.warn(`无法找到键 "${key}" 或其类型定义,已跳过。`);
480
+ parsedResult[key] = [];
481
+ continue;
482
+ }
483
+ const result_array = [];
484
+ const dataView = new DataView(
485
+ uint8Array.buffer,
486
+ uint8Array.byteOffset,
487
+ uint8Array.byteLength
488
+ );
489
+ const littleEndian = true;
490
+ for (let byteOffset = 0; byteOffset < dataView.byteLength; byteOffset += typeInfo.bytes) {
491
+ if (byteOffset + typeInfo.bytes > dataView.byteLength) {
492
+ console.warn(
493
+ `键 "${key}" 的数据长度 (${dataView.byteLength}) 不是其类型 (${typeInfo.bytes}字节) 的整数倍,末尾数据可能不完整。`
494
+ );
495
+ break;
496
+ }
497
+ const value = dataView[typeInfo.getter](byteOffset, littleEndian);
498
+ result_array.push(value);
499
+ }
500
+ parsedResult[key] = result_array;
501
+ }
502
+ return parsedResult;
503
+ }
504
+
505
+ function formatPrimitiveData(primitive) {
506
+ const { position, normal, posindex, nolindex } = primitive;
507
+ const restoredPosition = new Array(posindex.length);
508
+ const restoredNormal = new Array(nolindex.length);
509
+ for (let i = 0; i < posindex.length; i++) {
510
+ const posIndex = posindex[i];
511
+ restoredPosition[i] = position[posIndex];
512
+ }
513
+ for (let i = 0; i < nolindex.length; i++) {
514
+ const normalIndex = nolindex[i];
515
+ restoredNormal[i] = normal[normalIndex];
516
+ }
517
+ primitive.position = null;
518
+ primitive.normal = null;
519
+ primitive.posindex = null;
520
+ primitive.nolindex = null;
521
+ return { position: restoredPosition, normal: restoredNormal };
522
+ }
523
+
524
+ function buildInstancedMapping(instances, drawObjs) {
525
+ const drawObjMapInstance = {};
526
+ const instanceToDrawObject = [];
527
+
528
+ const meshByPrimId = new Map();
529
+ for (let i = 0; i < drawObjs.length; i++) {
530
+ const item = drawObjs[i];
531
+ if (!item || !Array.isArray(item.geoms)) continue;
532
+ const geoms = item.geoms;
533
+ for (let j = 0; j < geoms.length; j++) {
534
+ const geom = geoms[j];
535
+ if (geom && geom.prmid !== undefined && geom.prmid !== null) {
536
+ meshByPrimId.set(geom.prmid, geoms);
537
+ }
538
+ }
539
+ }
540
+
541
+ const seenByDrawObject = new Map();
542
+ for (let i = 0; i < instances.length; i++) {
543
+ const instance = instances[i];
544
+ if (!instance) continue;
545
+ const drawObject = instance.drawObject;
546
+ if (!drawObject) continue;
547
+
548
+ instanceToDrawObject.push({
549
+ instanceId: instance.instanceId,
550
+ drawObjectId: drawObject,
551
+ });
552
+
553
+ let record = drawObjMapInstance[drawObject];
554
+ if (!record) {
555
+ record = {
556
+ MapInstance: [],
557
+ MapMesh: meshByPrimId.get(drawObject),
558
+ };
559
+ drawObjMapInstance[drawObject] = record;
560
+ seenByDrawObject.set(drawObject, new Set());
561
+ }
562
+
563
+ const seen = seenByDrawObject.get(drawObject);
564
+ if (!seen.has(instance.instanceId)) {
565
+ record.MapInstance.push(instance);
566
+ seen.add(instance.instanceId);
567
+ }
568
+ }
569
+
570
+ return { drawObjMapInstance, instanceToDrawObject };
571
+ }
572
+
573
+ function scanOcclusionBuffer(buffer, sw, sh, stride, maxIdx, minSampleCount) {
574
+ const indices = [];
575
+ if (!buffer || !sw || !sh || !maxIdx) return { indices };
576
+ const view = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
577
+ const step = Math.max(1, stride || 1);
578
+ const minCount = Math.max(1, minSampleCount || 1);
579
+ const counts = new Uint32Array(maxIdx + 1);
580
+ for (let y = 0; y < sh; y += step) {
581
+ const row = y * sw * 4;
582
+ for (let x = 0; x < sw; x += step) {
583
+ const p = row + x * 4;
584
+ const idxColor = view[p] + (view[p + 1] << 8) + (view[p + 2] << 16);
585
+ if (idxColor > 0 && idxColor <= maxIdx) {
586
+ const next = counts[idxColor] + 1;
587
+ counts[idxColor] = next;
588
+ if (next === minCount) {
589
+ indices.push(idxColor);
590
+ }
591
+ }
592
+ }
593
+ }
594
+ return { indices };
595
+ }