@ifc-lite/renderer 1.44.0 → 1.45.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.
- package/dist/entity-visibility.d.ts +25 -0
- package/dist/entity-visibility.d.ts.map +1 -0
- package/dist/entity-visibility.js +34 -0
- package/dist/entity-visibility.js.map +1 -0
- package/dist/fill-bridge-anchor.d.ts +32 -0
- package/dist/fill-bridge-anchor.d.ts.map +1 -0
- package/dist/fill-bridge-anchor.js +82 -0
- package/dist/fill-bridge-anchor.js.map +1 -0
- package/dist/fill-predicates.d.ts +44 -0
- package/dist/fill-predicates.d.ts.map +1 -0
- package/dist/fill-predicates.js +65 -0
- package/dist/fill-predicates.js.map +1 -0
- package/dist/fill-triangulate.d.ts +102 -0
- package/dist/fill-triangulate.d.ts.map +1 -0
- package/dist/fill-triangulate.js +333 -0
- package/dist/fill-triangulate.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -6
- package/dist/index.js.map +1 -1
- package/dist/scene.d.ts +25 -0
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +71 -15
- package/dist/scene.js.map +1 -1
- package/dist/section-2d-lift.d.ts +10 -6
- package/dist/section-2d-lift.d.ts.map +1 -1
- package/dist/section-2d-lift.js +16 -10
- package/dist/section-2d-lift.js.map +1 -1
- package/dist/symbolic-overlay-pipelines.d.ts +10 -39
- package/dist/symbolic-overlay-pipelines.d.ts.map +1 -1
- package/dist/symbolic-overlay-pipelines.js +20 -163
- package/dist/symbolic-overlay-pipelines.js.map +1 -1
- package/package.json +2 -2
package/dist/scene.js
CHANGED
|
@@ -10,6 +10,7 @@ import { simplifyIndicesByClustering, lodCellSizeForBounds, LOD_MIN_TRIANGLES }
|
|
|
10
10
|
import { quantizeInterleaved } from './quantize.js';
|
|
11
11
|
import { bucketBaseKeyFor } from './chunk-grid.js';
|
|
12
12
|
import { VisibilityEpochTracker } from './visibility-epoch.js';
|
|
13
|
+
import { isEntityVisible } from './entity-visibility.js';
|
|
13
14
|
import { selectEvictions } from './residency.js';
|
|
14
15
|
import { OPAQUE_ALPHA_CUTOFF } from './overlay-routing.js';
|
|
15
16
|
import { prepareInstancedRender, foldOccurrenceWorldBox, INSTANCE_STRIDE_BYTES, INSTANCE_COLOR_OFFSET, INSTANCE_FLAGS_OFFSET, INSTANCE_FLAG_SELECTED, INSTANCE_FLAG_HIDDEN, } from './instanced-render.js';
|
|
@@ -23,6 +24,55 @@ function destroyGpuResources(m) {
|
|
|
23
24
|
}
|
|
24
25
|
/** Shared empty result for getInstancedTemplates() when the instanced pass is hidden. */
|
|
25
26
|
const EMPTY_INSTANCED_TEMPLATES = [];
|
|
27
|
+
/**
|
|
28
|
+
* Pure helper: compute the exclusive end index of the next flushPending()
|
|
29
|
+
* append chunk, starting at `readIndex` and bounded by BOTH mesh count
|
|
30
|
+
* (`hardEnd`, computed by the caller) and index volume (`maxIndicesPerAppend`).
|
|
31
|
+
* Always takes at least one mesh past `readIndex` -- a single oversize mesh is
|
|
32
|
+
* split upstream by splitMeshForStreaming, so the volume cap never blocks the
|
|
33
|
+
* first mesh of a chunk.
|
|
34
|
+
*
|
|
35
|
+
* Non-finite-safe by construction: every non-finite `next` (a malformed mesh
|
|
36
|
+
* reporting NaN, +Infinity, or -Infinity for `indices.length`) closes the
|
|
37
|
+
* chunk explicitly instead of being folded into the running `chunkIndices`
|
|
38
|
+
* total. Only NaN would have made a naive cap check `chunkIndices + next >
|
|
39
|
+
* maxIndicesPerAppend` silently `false` forever (`NaN > cap` is always
|
|
40
|
+
* `false`); +Infinity actually made that same check fire immediately
|
|
41
|
+
* (`chunkIndices + Infinity > cap` is `true`), closing the chunk after a
|
|
42
|
+
* single oversize mesh instead of growing it unbounded. The current
|
|
43
|
+
* `!(chunkIndices + next <= maxIndicesPerAppend)` form below rejects both
|
|
44
|
+
* NaN and +Infinity explicitly rather than relying on that asymmetry.
|
|
45
|
+
* -Infinity needed a separate, explicit check: `-Infinity <= cap` is always
|
|
46
|
+
* `true`, so `!(... <= cap)` lets it straight through, and folding it into
|
|
47
|
+
* `chunkIndices` would poison the running total to -Infinity permanently,
|
|
48
|
+
* keeping the cap vacuous for every mesh after it, not just the malformed
|
|
49
|
+
* one.
|
|
50
|
+
*/
|
|
51
|
+
export function computeFlushChunkEnd(getIndicesLength, readIndex, hardEnd, maxIndicesPerAppend) {
|
|
52
|
+
let chunkEnd = readIndex;
|
|
53
|
+
let chunkIndices = 0;
|
|
54
|
+
while (chunkEnd < hardEnd) {
|
|
55
|
+
const next = getIndicesLength(chunkEnd);
|
|
56
|
+
if (!Number.isFinite(next)) {
|
|
57
|
+
// A malformed mesh reporting a non-finite indices.length (NaN, +/-Infinity)
|
|
58
|
+
// must close the chunk here rather than being folded into chunkIndices:
|
|
59
|
+
// `chunkIndices += -Infinity` would poison the running total to -Infinity
|
|
60
|
+
// permanently, making `!(chunkIndices + next <= maxIndicesPerAppend)`
|
|
61
|
+
// false forever and letting the volume cap never fire again for the
|
|
62
|
+
// rest of this chunk. Always take at least the first mesh past
|
|
63
|
+
// readIndex (same progress guarantee as the NaN case below).
|
|
64
|
+
if (chunkEnd === readIndex)
|
|
65
|
+
chunkEnd++;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
if (chunkEnd > readIndex && !(chunkIndices + next <= maxIndicesPerAppend)) {
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
chunkIndices += next;
|
|
72
|
+
chunkEnd++;
|
|
73
|
+
}
|
|
74
|
+
return chunkEnd;
|
|
75
|
+
}
|
|
26
76
|
export class Scene {
|
|
27
77
|
meshes = [];
|
|
28
78
|
batchedMeshes = []; // flat render array (rebuilt from buckets)
|
|
@@ -1637,18 +1687,26 @@ export class Scene {
|
|
|
1637
1687
|
break;
|
|
1638
1688
|
}
|
|
1639
1689
|
const hardEnd = Math.min(this.meshQueue.length, this.meshQueueReadIndex + MESHES_PER_APPEND, this.meshQueueReadIndex + (MAX_MESHES_PER_FLUSH - processed));
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1690
|
+
const chunkEnd = computeFlushChunkEnd((i) => this.meshQueue[i].indices.length, this.meshQueueReadIndex, hardEnd, MAX_INDICES_PER_APPEND);
|
|
1691
|
+
// Defensive, not reachable today: chunkEnd is provably > meshQueueReadIndex
|
|
1692
|
+
// here because hardEnd is provably > meshQueueReadIndex whenever this outer
|
|
1693
|
+
// loop iterates, via three invariants that hold simultaneously above:
|
|
1694
|
+
// (1) this.meshQueue.length > this.meshQueueReadIndex -- the outer while
|
|
1695
|
+
// condition that got us into this iteration;
|
|
1696
|
+
// (2) this.meshQueueReadIndex + MESHES_PER_APPEND, and MESHES_PER_APPEND
|
|
1697
|
+
// (512) is a positive constant;
|
|
1698
|
+
// (3) this.meshQueueReadIndex + (MAX_MESHES_PER_FLUSH - processed), and
|
|
1699
|
+
// processed < MAX_MESHES_PER_FLUSH -- the other half of the outer
|
|
1700
|
+
// while condition -- so that term is >= readIndex + 1 too.
|
|
1701
|
+
// hardEnd is the min of all three, so hardEnd >= readIndex + 1, and
|
|
1702
|
+
// computeFlushChunkEnd always advances by at least one past readIndex.
|
|
1703
|
+
// If a future change breaks any one of those three invariants, hardEnd
|
|
1704
|
+
// could collapse to readIndex and the loop would spin the main thread at
|
|
1705
|
+
// 100% CPU doing zero allocation -- the exact signature that made #2379
|
|
1706
|
+
// expensive to diagnose. This break turns that failure mode into "flush
|
|
1707
|
+
// stops early" instead.
|
|
1708
|
+
if (chunkEnd === this.meshQueueReadIndex)
|
|
1709
|
+
break;
|
|
1652
1710
|
const chunk = this.meshQueue.slice(this.meshQueueReadIndex, chunkEnd);
|
|
1653
1711
|
this.meshQueueReadIndex = chunkEnd;
|
|
1654
1712
|
this.appendToBatches(chunk, device, pipeline, true);
|
|
@@ -3204,13 +3262,11 @@ export class Scene {
|
|
|
3204
3262
|
}
|
|
3205
3263
|
this.instancedVisibilityDirty = false;
|
|
3206
3264
|
this.lastInstancedVisibilityVersion = visibilityVersion;
|
|
3207
|
-
const isHidden = (eid) => (hiddenIds != null && hiddenIds.has(eid)) ||
|
|
3208
|
-
(isolatedIds != null && !isolatedIds.has(eid));
|
|
3209
3265
|
// Recompute the effective hidden set over all instanced occurrences and diff vs
|
|
3210
3266
|
// the current one; only flips touch the GPU buffer.
|
|
3211
3267
|
const next = new Set();
|
|
3212
3268
|
for (const eid of this.instancedEntityMap.keys()) {
|
|
3213
|
-
if (
|
|
3269
|
+
if (!isEntityVisible(eid, hiddenIds, isolatedIds))
|
|
3214
3270
|
next.add(eid);
|
|
3215
3271
|
}
|
|
3216
3272
|
// Fast-path: unchanged hidden set → nothing to write.
|