@xeokit/xeokit-sdk 2.6.95 → 2.6.96

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.
@@ -0,0 +1,2 @@
1
+ export * from "./IFCOpenShellDefaultDataSource.js";
2
+ export * from "./IFCOpenShellLoaderPlugin.js";
@@ -18,6 +18,7 @@ export * from "./XKTLoaderPlugin/index.js";
18
18
  export * from "./XML3DLoaderPlugin/index.js";
19
19
  export * from "./WebIFCLoaderPlugin/index.js";
20
20
  export * from "./CxConverterIFCLoaderPlugin/index.js";
21
+ export * from "./IFCOpenShellLoaderPlugin/index.js";
21
22
  export * from "./LASLoaderPlugin/index.js";
22
23
  export * from "./CityJSONLoaderPlugin/index.js";
23
24
  export * from "./DotBIMLoaderPlugin/index.js";
@@ -867,7 +867,7 @@ class CameraControl extends Component {
867
867
  *
868
868
  * See class docs for usage.
869
869
  *
870
- * @param {{Number:Number}|String} value Either a set of new key mappings, or a string to select a keyboard layout,
870
+ * @param {{Number:(Number | Number[])[]} | String} value Either a set of new key mappings, or a string to select a keyboard layout,
871
871
  * which causes ````CameraControl```` to use the default key mappings for that layout.
872
872
  */
873
873
  set keyMap(value) {
@@ -946,7 +946,7 @@ class CameraControl extends Component {
946
946
  /**
947
947
  * Gets custom mappings of keys to {@link CameraControl} actions.
948
948
  *
949
- * @returns {{Number:Number}} Current key mappings.
949
+ * @returns {{Number:(Number | Number[])[]}} Current key mappings.
950
950
  */
951
951
  get keyMap() {
952
952
  return this._keyMap;
@@ -5475,9 +5475,12 @@ math.makeSectionPlaneSlicer = (function() {
5475
5475
  const tempVec3d = math.vec3();
5476
5476
 
5477
5477
  const triangle = [ math.vec3(), math.vec3(), math.vec3() ];
5478
+ const tmpIntersections = [ null, null, null ];
5479
+ const tmpPositions = [ null, null, null ];
5480
+ const tmpReferences = [ null, null, null ];
5478
5481
 
5479
- const worldUp = [0, 1, 0];
5480
5482
  const worldRight = [1, 0, 0];
5483
+ const worldUp = [0, 1, 0];
5481
5484
  const worldForward = [0, 0, 1];
5482
5485
 
5483
5486
  const sqDistVec3 = (function() {
@@ -5492,84 +5495,113 @@ math.makeSectionPlaneSlicer = (function() {
5492
5495
  return ret;
5493
5496
  };
5494
5497
 
5495
- const setCoord2D = (p, dst) => { dst[0] = p.coord2Dx; dst[1] = p.coord2Dy; return dst; };
5496
-
5497
5498
  const ccw = (a, b, c) => ((c[1] - a[1]) * (b[0] - a[0])) > ((b[1] - a[1]) * (c[0] - a[0]));
5498
5499
 
5499
- const isLoopInside = (inner, outer) => {
5500
- const bbI = inner.boundingBox;
5501
- const bbO = outer.boundingBox;
5502
- if ((bbI[0] < bbO[0]) || (bbI[1] < bbO[1]) || (bbI[2] > bbO[2]) || (bbI[3] > bbO[3])) {
5503
- return false;
5504
- }
5500
+ return function(plane) {
5501
+ const planeU = math.vec3ApplyQuaternion(plane.quaternion, worldRight, math.vec3());
5502
+ const planeV = math.vec3ApplyQuaternion(plane.quaternion, worldUp, math.vec3());
5503
+ const planeN = math.vec3ApplyQuaternion(plane.quaternion, worldForward, math.vec3());
5505
5504
 
5506
- const innerEndpoints = inner.endPoints;
5507
- const outerEndpoints = outer.endPoints;
5508
- for (let ii = 0, ij = innerEndpoints.length - 1; ii < innerEndpoints.length; ij = ii++) {
5509
- const i0 = setCoord2D(innerEndpoints[ii], tempVec2a);
5510
- const [i0x, i0y] = i0;
5511
- const i1 = setCoord2D(innerEndpoints[ij], tempVec2b);
5505
+ return function(mesh, cfg = { }) {
5506
+ const planeToMesh = math.subVec3(mesh.origin, plane.pos, math.vec3());
5507
+ const planeDist = math.dotVec3(planeN, planeToMesh);
5512
5508
 
5513
- let inside = false;
5514
- for (let i = 0, j = outerEndpoints.length - 1; i < outerEndpoints.length; j = i++) {
5515
- const o0 = setCoord2D(outerEndpoints[i], tempVec2c);
5516
- const o1 = setCoord2D(outerEndpoints[j], tempVec2d);
5509
+ const unsortedSegment = [ ];
5510
+ const indexedPositions = [ null ]; // to never return 0 from addPosition, so its result can be used as a predicate
5511
+ const addPosition = (p, addUV) => {
5512
+ const idx = indexedPositions.length;
5513
+ const P = new Float64Array(5);
5514
+ P.set(p);
5515
+ if (addUV) {
5516
+ math.addVec3(planeToMesh, p, tempVec3a);
5517
+ P[3] = -math.dotVec3(planeU, tempVec3a);
5518
+ P[4] = math.dotVec3(planeV, tempVec3a);
5519
+ }
5520
+ indexedPositions.push(P);
5521
+ return idx;
5522
+ };
5517
5523
 
5518
- if ((ccw(i0, o0, o1) !== ccw(i1, o0, o1)) && (ccw(i0, i1, o0) !== ccw(i0, i1, o1))) {
5519
- return false; // segments intersect
5524
+ const getCoord2D = (idx, dst) => { const P = indexedPositions[idx]; dst[0] = P[3]; dst[1] = P[4]; return dst; };
5525
+
5526
+ const isLoopInside = (inner, outer) => {
5527
+ const bbI = inner.boundingBox;
5528
+ const bbO = outer.boundingBox;
5529
+ if ((bbI[0] < bbO[0]) || (bbI[1] < bbO[1]) || (bbI[2] > bbO[2]) || (bbI[3] > bbO[3])) {
5530
+ return false;
5520
5531
  }
5521
5532
 
5522
- const [o0x, o0y] = o0;
5523
- const [o1x, o1y] = o1;
5533
+ const innerEndpoints = inner.endPoints;
5534
+ const outerEndpoints = outer.endPoints;
5535
+ for (let ii = 0, ij = innerEndpoints.length - 1; ii < innerEndpoints.length; ij = ii++) {
5536
+ const i0 = getCoord2D(innerEndpoints[ii], tempVec2a);
5537
+ const [i0x, i0y] = i0;
5538
+ const i1 = getCoord2D(innerEndpoints[ij], tempVec2b);
5524
5539
 
5525
- const dx = i0x - o0x;
5526
- const dy = i0y - o0y;
5540
+ let inside = false;
5541
+ for (let i = 0, j = outerEndpoints.length - 1; i < outerEndpoints.length; j = i++) {
5542
+ const o0 = getCoord2D(outerEndpoints[i], tempVec2c);
5543
+ const o1 = getCoord2D(outerEndpoints[j], tempVec2d);
5527
5544
 
5528
- const oDx = o1x - o0x;
5529
- const oDy = o1y - o0y;
5545
+ if ((ccw(i0, o0, o1) !== ccw(i1, o0, o1)) && (ccw(i0, i1, o0) !== ccw(i0, i1, o1))) {
5546
+ return false; // segments intersect
5547
+ }
5530
5548
 
5531
- const dot = (((oDx !== 0) || (oDy !== 0)) && (Math.abs(oDx * dy - oDy * dx) < 1e-10)) ? (dx * oDx + dy * oDy) : -1;
5532
- if ((dot >= 0) && (dot <= (Math.pow(oDx, 2) + Math.pow(oDy, 2)))) {
5533
- return false; // on edge
5534
- }
5549
+ const [o0x, o0y] = o0;
5550
+ const [o1x, o1y] = o1;
5535
5551
 
5536
- if (((o0y > i0y) !== (o1y > i0y)) && (dx < (dy * oDx / oDy))) {
5537
- inside = !inside;
5552
+ const dx = i0x - o0x;
5553
+ const dy = i0y - o0y;
5554
+
5555
+ const oDx = o1x - o0x;
5556
+ const oDy = o1y - o0y;
5557
+
5558
+ const dot = (((oDx !== 0) || (oDy !== 0)) && (Math.abs(oDx * dy - oDy * dx) < 1e-10)) ? (dx * oDx + dy * oDy) : -1;
5559
+ if ((dot >= 0) && (dot <= (Math.pow(oDx, 2) + Math.pow(oDy, 2)))) {
5560
+ return false; // on edge
5561
+ }
5562
+
5563
+ if (((o0y > i0y) !== (o1y > i0y)) && (dx < (dy * oDx / oDy))) {
5564
+ inside = !inside;
5565
+ }
5566
+ }
5567
+ if (! inside) {
5568
+ return false;
5569
+ }
5538
5570
  }
5539
- }
5540
- if (! inside) {
5541
- return false;
5542
- }
5543
- }
5544
5571
 
5545
- return true;
5546
- };
5572
+ return true;
5573
+ };
5547
5574
 
5548
- return function(planePos, planeRot) {
5549
- const planeU = math.vec3ApplyQuaternion(planeRot, worldRight, math.vec3());
5550
- const planeV = math.vec3ApplyQuaternion(planeRot, worldUp, math.vec3());
5551
- const planeN = math.vec3ApplyQuaternion(planeRot, worldForward, math.vec3());
5575
+ const pos = { indices: [ ], normals: [ ] };
5576
+ const neg = (! cfg.onlyPosSliceWithUV) && { indices: [ ], normals: [ ] };
5552
5577
 
5553
- return function(meshCenter, meshIndices, meshPositions) {
5554
- const planeToMesh = math.subVec3(meshCenter, planePos, math.vec3());
5555
- const planeDist = math.dotVec3(planeN, planeToMesh);
5578
+ const appendOnSide = (dst, indices, normal) => {
5579
+ if (dst) {
5580
+ const p0 = indexedPositions[indices[0]];
5581
+ normal ||= math.normalizeVec3(math.cross3Vec3(math.subVec3(indexedPositions[indices[1]], p0, tempVec3a),
5582
+ math.subVec3(indexedPositions[indices[2]], p0, tempVec3b),
5583
+ tempVec3a),
5584
+ tempVec3a);
5556
5585
 
5557
- const unsortedSegment = [ ];
5558
- const indexedPositions = [ null ]; // to never return 0 from addPosition, so its result can be used as a predicate
5559
- const addPosition = p => { const idx = indexedPositions.length; indexedPositions.push(math.vec3(p)); return idx; };
5586
+ for (let i = 0; i < 3; ++i) {
5587
+ dst.indices.push(indices[i]);
5588
+ dst.normals.push(normal[0], normal[1], normal[2]);
5589
+ }
5590
+ }
5591
+ };
5560
5592
 
5561
5593
  const setVertex = (i, dst) => {
5562
- const idx = meshIndices[i] * 3;
5563
- dst[0] = meshPositions[idx + 0];
5564
- dst[1] = meshPositions[idx + 1];
5565
- dst[2] = meshPositions[idx + 2];
5594
+ const idx = mesh.indices[i] * 3;
5595
+ dst[0] = mesh.positions[idx + 0];
5596
+ dst[1] = mesh.positions[idx + 1];
5597
+ dst[2] = mesh.positions[idx + 2];
5566
5598
  return dst;
5567
5599
  };
5568
5600
 
5569
- for (let meshIdx = 0; meshIdx < meshIndices.length; meshIdx += 3) {
5570
- const p0 = setVertex(meshIdx + 0, triangle[0]);
5571
- const p1 = setVertex(meshIdx + 1, triangle[1]);
5572
- const p2 = setVertex(meshIdx + 2, triangle[2]);
5601
+ for (let faceIdx = 0; faceIdx < mesh.indices.length; faceIdx += 3) {
5602
+ const p0 = setVertex(faceIdx + 0, triangle[0]);
5603
+ const p1 = setVertex(faceIdx + 1, triangle[1]);
5604
+ const p2 = setVertex(faceIdx + 2, triangle[2]);
5573
5605
 
5574
5606
  if (math.compareVec3(p0, p1) || math.compareVec3(p1, p2) || math.compareVec3(p2, p0)) {
5575
5607
  continue; // skip degenerate triangle
@@ -5580,12 +5612,60 @@ math.makeSectionPlaneSlicer = (function() {
5580
5612
  const d2 = planeDist + math.dotVec3(planeN, p2);
5581
5613
 
5582
5614
  if ((d0 !== 0) || (d1 !== 0) || (d2 !== 0)) {
5583
- const i0 = (d0 * d1 <= 0) && addPosition(math.lerpVec3(d0 / (d0 - d1), 0, 1, p0, p1, tempVec3a));
5584
- const i1 = (d1 * d2 <= 0) && addPosition(math.lerpVec3(d1 / (d1 - d2), 0, 1, p1, p2, tempVec3a));
5585
- const i2 = (d2 * d0 <= 0) && addPosition(math.lerpVec3(d2 / (d2 - d0), 0, 1, p2, p0, tempVec3a));
5615
+ const i0 = (d0 * d1 <= 0) && addPosition(math.lerpVec3(d0 / (d0 - d1), 0, 1, p0, p1, tempVec3a), true);
5616
+ const i1 = (d1 * d2 <= 0) && addPosition(math.lerpVec3(d1 / (d1 - d2), 0, 1, p1, p2, tempVec3a), true);
5617
+ const i2 = (d2 * d0 <= 0) && addPosition(math.lerpVec3(d2 / (d2 - d0), 0, 1, p2, p0, tempVec3a), true);
5618
+
5619
+ const p = (! cfg.onlyPosSliceWithUV) && tmpPositions;
5620
+ if (p) {
5621
+ p[0] = addPosition(p0);
5622
+ p[1] = addPosition(p1);
5623
+ p[2] = addPosition(p2);
5624
+ }
5586
5625
 
5587
5626
  if (i0 ? (i1 || i2) : (i1 && i2)) { // triangle intersected by the section plane
5588
5627
  unsortedSegment.push(i0 ? [ i0, i1 || i2 ] : [ i1, i2 ]);
5628
+
5629
+ if (p) {
5630
+ if ((d0 === 0) && (d1 === 0)) {
5631
+ appendOnSide((d2 > 0) ? pos : neg, p);
5632
+ } else if ((d0 === 0) && (d2 === 0)) {
5633
+ appendOnSide((d1 > 0) ? pos : neg, p);
5634
+ } else if ((d1 === 0) && (d2 === 0)) {
5635
+ appendOnSide((d0 > 0) ? pos : neg, p);
5636
+ } else {
5637
+ const isPos = (i0 ? d0 : d1) > 0;
5638
+ const dst0 = isPos ? pos : neg;
5639
+ const dst1 = isPos ? neg : pos;
5640
+ const i = tmpIntersections;
5641
+ i[0] = i0;
5642
+ i[1] = i1;
5643
+ i[2] = i2;
5644
+ const ref = tmpReferences;
5645
+ if (i0) {
5646
+ ref[0] = 0; ref[1] = 1; ref[2] = 2;
5647
+ } else {
5648
+ ref[0] = 1; ref[1] = 2; ref[2] = 0;
5649
+ }
5650
+ if (i[ref[1]]) {
5651
+ appendOnSide(dst0, [ p[ref[0]], i[ref[0]], p[ref[2]] ]);
5652
+ appendOnSide(dst0, [ i[ref[0]], i[ref[1]], p[ref[2]] ]);
5653
+ appendOnSide(dst1, [ i[ref[0]], p[ref[1]], i[ref[1]] ]);
5654
+ } else {
5655
+ appendOnSide(dst0, [ p[ref[0]], i[ref[0]], i[ref[2]] ]);
5656
+ appendOnSide(dst1, [ i[ref[0]], p[ref[1]], p[ref[2]] ]);
5657
+ appendOnSide(dst1, [ i[ref[0]], p[ref[2]], i[ref[2]] ]);
5658
+ }
5659
+ }
5660
+ }
5661
+ } else if (p) {
5662
+ if ((d0 >= 0) && (d1 >= 0) && (d2 >= 0)) {
5663
+ appendOnSide(pos, p);
5664
+ } else if ((d0 <= 0) && (d1 <= 0) && (d2 <= 0)) {
5665
+ appendOnSide(neg, p);
5666
+ } else {
5667
+ debugger;
5668
+ }
5589
5669
  }
5590
5670
  }
5591
5671
  }
@@ -5631,37 +5711,30 @@ math.makeSectionPlaneSlicer = (function() {
5631
5711
  }
5632
5712
 
5633
5713
  const loops = endpointLoops.filter(endPoints => endPoints.length > 2).map((endPoints, idx) => {
5634
- const planeEndpoints = endPoints.map(posIdx => {
5635
- const P = math.addVec3(planeToMesh, indexedPositions[posIdx], tempVec3a);
5636
- return { posIdx: posIdx, coord2Dx: math.dotVec3(planeU, P), coord2Dy: math.dotVec3(planeV, P) };
5637
- });
5638
5714
  let doubleArea = 0;
5639
5715
  const aabb = math.collapseAABB2(math.AABB2());
5640
- for (let i = 0; i < planeEndpoints.length; i++) {
5641
- const p0 = planeEndpoints[i];
5642
- tempVec2a[0] = p0.coord2Dx;
5643
- tempVec2a[1] = p0.coord2Dy;
5716
+ for (let i = 0; i < endPoints.length; i++) {
5717
+ getCoord2D(endPoints[i], tempVec2a);
5644
5718
  math.expandAABB2Point2(aabb, tempVec2a);
5645
- const p1 = planeEndpoints[(i + 1) % planeEndpoints.length];
5646
- doubleArea += (p0.coord2Dx * p1.coord2Dy - p1.coord2Dx * p0.coord2Dy);
5719
+ getCoord2D(endPoints[(i + 1) % endPoints.length], tempVec2b);
5720
+ doubleArea += (tempVec2a[0] * tempVec2b[1] - tempVec2b[0] * tempVec2a[1]);
5647
5721
  }
5648
5722
  return {
5649
5723
  boundingBox: aabb,
5650
5724
  doubleArea: Math.abs(doubleArea),
5651
- endPoints: planeEndpoints
5725
+ endPoints: endPoints
5652
5726
  };
5653
5727
  }).sort((a, b) => b.doubleArea - a.doubleArea);
5654
5728
 
5655
- const sliceGeometries = [ ];
5656
-
5657
5729
  while (loops.length > 0) {
5658
5730
  const vertices2D = [ ];
5659
5731
  const vertices3D = [ ];
5660
5732
 
5661
5733
  const appendLoopVertices = loop => loop.endPoints.forEach(endpoint2D => {
5662
- vertices2D.push(endpoint2D.coord2Dx, endpoint2D.coord2Dy);
5663
- vertices3D.push(endpoint2D.posIdx);
5664
- });
5734
+ getCoord2D(endpoint2D, tempVec2a);
5735
+ vertices2D.push(tempVec2a[0], tempVec2a[1]);
5736
+ vertices3D.push(endpoint2D);
5737
+ });
5665
5738
 
5666
5739
  const outerLoop = loops.shift();
5667
5740
  appendLoopVertices(outerLoop);
@@ -5683,41 +5756,33 @@ math.makeSectionPlaneSlicer = (function() {
5683
5756
  // Triangulate
5684
5757
  const triangles = earcut(vertices2D, innerLoops.map(loop => loop.index));
5685
5758
 
5686
- const positions = [ ];
5687
- const normals = [ ];
5688
- const uv = [ ];
5689
5759
  for (let i = 0; i < triangles.length; i += 3) {
5690
- const v0 = indexedPositions[vertices3D[triangles[i + 0]]];
5691
- const v1 = indexedPositions[vertices3D[triangles[i + 1]]];
5692
- const v2 = indexedPositions[vertices3D[triangles[i + 2]]];
5693
- math.subVec3(v1, v0, tempVec3b);
5694
- math.subVec3(v2, v0, tempVec3c);
5760
+ const ti = tmpIntersections;
5761
+ ti[0] = vertices3D[triangles[i + 0]];
5762
+ ti[1] = vertices3D[triangles[i + 1]];
5763
+ ti[2] = vertices3D[triangles[i + 2]];
5764
+ const v0 = indexedPositions[ti[0]];
5765
+ math.subVec3(indexedPositions[ti[1]], v0, tempVec3b);
5766
+ math.subVec3(indexedPositions[ti[2]], v0, tempVec3c);
5695
5767
  math.normalizeVec3(math.cross3Vec3(tempVec3b, tempVec3c, tempVec3c), tempVec3c);
5696
5768
  const facedPositively = math.dotVec3(tempVec3c, planeN) <= 0;
5697
- if (! facedPositively) {
5698
- math.negateVec3(tempVec3c, tempVec3c);
5699
- }
5700
- for (let j = 0; j < 3; ++j) {
5701
- const vIdx = triangles[i + (facedPositively ? j : (2 - j))];
5702
- const v = indexedPositions[vertices3D[vIdx]];
5703
- positions.push(v[0], v[1], v[2]);
5704
- normals.push(tempVec3c[0], tempVec3c[1], tempVec3c[2]);
5705
- const uvOff = 2 * vIdx;
5706
- uv.push(-vertices2D[uvOff], vertices2D[uvOff + 1]);
5707
- }
5708
- }
5709
-
5710
- if (positions.length > 0) {
5711
- sliceGeometries.push({
5712
- indices: iota(positions.length / 3),
5713
- positions: positions,
5714
- normals: normals,
5715
- uv: uv
5716
- });
5769
+ appendOnSide(facedPositively ? pos : neg, ti, tempVec3c);
5770
+ const tmp = ti[0]; ti[0] = ti[2]; ti[2] = tmp;
5771
+ appendOnSide(facedPositively ? neg : pos, ti, math.negateVec3(tempVec3c, tempVec3c));
5717
5772
  }
5718
5773
  }
5719
5774
 
5720
- return sliceGeometries;
5775
+ const side = src => src && (src.indices.length > 0) && (function() {
5776
+ const positions = [ ];
5777
+ const uv = cfg.onlyPosSliceWithUV && [ ];
5778
+ src.indices.forEach(idx => {
5779
+ const P = indexedPositions[idx];
5780
+ positions.push(P[0], P[1], P[2]);
5781
+ uv && uv.push(P[3], P[4]);
5782
+ });
5783
+ return { indices: iota(src.indices.length), positions: positions, normals: src.normals, uv: uv };
5784
+ })();
5785
+ return { pos: side(pos), neg: side(neg) };
5721
5786
  };
5722
5787
  };
5723
5788
  })();
@@ -415,5 +415,7 @@ export class SceneModelMesh {
415
415
  */
416
416
  _destroy() {
417
417
  this.model.scene._renderer.putPickID(this.pickId);
418
+
419
+ this.layer = null;
418
420
  }
419
421
  }
@@ -12,9 +12,9 @@ function quantizePositions(positions, aabb, positionsDecodeMatrix) { // http://c
12
12
  const xmin = aabb[0];
13
13
  const ymin = aabb[1];
14
14
  const zmin = aabb[2];
15
- const xwid = aabb[3] - xmin;
16
- const ywid = aabb[4] - ymin;
17
- const zwid = aabb[5] - zmin;
15
+ const xwid = (aabb[3] - xmin) || 1;
16
+ const ywid = (aabb[4] - ymin) || 1;
17
+ const zwid = (aabb[5] - zmin) || 1;
18
18
  const maxInt = 65525;
19
19
  const xMultiplier = maxInt / xwid;
20
20
  const yMultiplier = maxInt / ywid;
@@ -43,9 +43,9 @@ function createPositionsDecodeMatrix(aabb, positionsDecodeMatrix) { // http://cg
43
43
  const xmin = aabb[0];
44
44
  const ymin = aabb[1];
45
45
  const zmin = aabb[2];
46
- const xwid = aabb[3] - xmin;
47
- const ywid = aabb[4] - ymin;
48
- const zwid = aabb[5] - zmin;
46
+ const xwid = (aabb[3] - xmin) || 1;
47
+ const ywid = (aabb[4] - ymin) || 1;
48
+ const zwid = (aabb[5] - zmin) || 1;
49
49
  const maxInt = 65525;
50
50
  math.identityMat4(translate);
51
51
  math.translationMat4v(aabb, translate);
@@ -812,9 +812,13 @@ const makeDTXRenderingAttributes = function(programVariables, isTriangle) {
812
812
  const colorsAndFlags = (offset) => perObjColsFlags(`ivec2(objectIndexCoords.x*8+${offset}, objectIndexCoords.y)`);
813
813
 
814
814
  return {
815
+ clippableTest: (function() {
816
+ const vClippable = programVariables.createVarying("uint", "vClippable", () => "flags2.r", "flat");
817
+ return () => `${vClippable} > 0u`;
818
+ })(),
819
+
815
820
  geometryParameters: {
816
821
  attributes: {
817
- clippable: "(flags2.r > 0u)",
818
822
  color: colorA,
819
823
  flags: iota(4).map(i => `int(flags[${i}])`),
820
824
  metallicRoughness: null,
@@ -156,10 +156,7 @@ export const getRenderers = (function() {
156
156
  appendFragmentOutputs: programSetup.appendFragmentOutputs,
157
157
  cleanerEdges: programSetup.cleanerEdges,
158
158
  clipPos: clipPos,
159
- clippableTest: (function() {
160
- const vClippable = programVariables.createVarying("float", "vClippable", () => `${attributes.clippable} ? 1.0 : 0.0`, "flat");
161
- return () => `${vClippable} > 0.0`;
162
- })(),
159
+ clippableTest: renderingAttributes.clippableTest,
163
160
  clippingCaps: programSetup.clippingCaps,
164
161
  crossSections: scene.crossSections,
165
162
  discardPoints: setupPoints && pointsMaterial.roundPoints,
@@ -887,8 +887,8 @@ export class VBOLayer extends Layer {
887
887
  } else { // triangles
888
888
  if (subGeometry && subGeometry.vertices) {
889
889
  return drawPoints;
890
- } else if (subGeometry && edgeIndicesBuf) {
891
- return elementsDrawer(gl.LINES, edgeIndicesBuf);
890
+ } else if (subGeometry) {
891
+ return edgeIndicesBuf ? elementsDrawer(gl.LINES, edgeIndicesBuf) : (() => { });
892
892
  } else {
893
893
  return elementsDrawer(gl.TRIANGLES, indicesBuf);
894
894
  }
@@ -970,9 +970,13 @@ const makeVBORenderingAttributes = function(programVariables, instancing, entity
970
970
  return {
971
971
  dontCullOnAlphaZero: true,
972
972
 
973
+ clippableTest: (function() {
974
+ const vClippable = programVariables.createVarying("float", "vClippable", () => `${`((int(${attributes.flags}) >> 16 & 0xF) == 1)`} ? 1.0 : 0.0`); // Using `flat uint` for vClippable causes an instability - see XEOK-385
975
+ return () => `${vClippable} != 0.0`;
976
+ })(),
977
+
973
978
  geometryParameters: {
974
979
  attributes: {
975
- clippable: `((int(${attributes.flags}) >> 16 & 0xF) == 1)`,
976
980
  color: attributes.color,
977
981
  flags: iota(4).map(i => ({ toString: () => `(int(${attributes.flags}) >> ${i * 4} & 0xF)` })),
978
982
  metallicRoughness: attributes.metallicRoughness,
@@ -118,7 +118,7 @@ class SectionCaps {
118
118
  const visibleSceneModels = Object.values(scene.models).filter(sceneModel => (sceneModel.id in modelCaches) && sceneModel.visible);
119
119
  sectionPlanes.forEach((plane) => {
120
120
  if (plane.active) {
121
- const sliceMesh = math.makeSectionPlaneSlicer(plane.pos, plane.quaternion);
121
+ const sliceMesh = math.makeSectionPlaneSlicer(plane);
122
122
  visibleSceneModels.forEach(sceneModel => {
123
123
  const modelAABB = sceneModel.aabb;
124
124
  if (math.planeIntersectsAABB3(plane, modelAABB)) {
@@ -138,10 +138,11 @@ class SectionCaps {
138
138
  });
139
139
 
140
140
  entityCache.meshCaches.filter(meshCache => math.planeIntersectsAABB3(plane, meshCache.mesh.aabb)).forEach((meshCache, meshIdx) => {
141
- sliceMesh(modelCenter, meshCache.meshIndices, meshCache.meshVertices).forEach((geo, geoIdx) => {
141
+ const geo = sliceMesh({ origin: modelCenter, indices: meshCache.meshIndices, positions: meshCache.meshVertices }, { onlyPosSliceWithUV: true }).pos;
142
+ if (geo) {
142
143
  entityCache.capMeshes.push(new Mesh(scene, {
143
144
  isObject: true,
144
- id: `${plane.id}-${entityId}-${meshIdx}-${geoIdx}`,
145
+ id: `${plane.id}-${entityId}-${meshIdx}`,
145
146
  material: entity.capMaterial,
146
147
  origin: math.addVec3(modelCenter, math.mulVec3Scalar(plane.dir, 0.001, tempVec3a), tempVec3a),
147
148
  geometry: new ReadableGeometry(scene, {
@@ -152,7 +153,7 @@ class SectionCaps {
152
153
  uv: geo.uv
153
154
  })
154
155
  }));
155
- });
156
+ }
156
157
  });
157
158
  }
158
159
  });
@@ -0,0 +1,107 @@
1
+ import { Plugin, Viewer, SceneModel } from "../../viewer";
2
+ import { ModelStats } from "../index";
3
+
4
+ /**
5
+ * Default data source for loading IFC files in IFCOpenShellLoaderPlugin.
6
+ * Loads files via HTTP by default.
7
+ */
8
+ export declare interface IFCOpenShellDefaultDataSource {
9
+ /**
10
+ * Loads the contents of the given IFC file as an ArrayBuffer.
11
+ * @param src Path or ID of the IFC file.
12
+ * @param ok Callback on success, receives the IFC file as an ArrayBuffer.
13
+ * @param error Callback on error, receives an Error.
14
+ */
15
+ getIFC(src: string | number, ok: (buffer: ArrayBuffer) => void, error: (e: Error) => void): void;
16
+
17
+ /** Whether to append a cache-busting query parameter to requests. */
18
+ get cacheBuster(): boolean;
19
+ set cacheBuster(value: boolean);
20
+ }
21
+
22
+ /**
23
+ * Configuration options for IFCOpenShellLoaderPlugin.
24
+ */
25
+ export declare type IFCOpenShellLoaderPluginConfigs = {
26
+ /** Optional plugin ID. */
27
+ id?: string;
28
+ /** Custom data source for loading IFC files. */
29
+ dataSource?: IFCOpenShellDefaultDataSource;
30
+ /** Pyodide proxy for the ifcopenshell module. */
31
+ ifcopenshell: any;
32
+ /** Pyodide proxy for the ifcopenshell.geom module. */
33
+ ifcopenshell_geom: any;
34
+ };
35
+
36
+ /**
37
+ * Parameters for loading an IFC model with IFCOpenShellLoaderPlugin.
38
+ */
39
+ export declare type LoadIFCOpenShellModel = {
40
+ /** Unique ID for the loaded SceneModel. */
41
+ id?: string;
42
+ /** Path to the IFC file. */
43
+ src?: string;
44
+ /** Whether to load IFC metadata (metaobject). */
45
+ loadMetadata?: boolean;
46
+ /** Whether to load IFC metadata property sets. Only works when `loadMetadata` is `true`. */
47
+ loadMetadataPropertySets?: boolean;
48
+ /** Exclude objects with types in this list. */
49
+ excludeTypes?: string[];
50
+ /** Render the model with edges emphasized. */
51
+ edges?: boolean;
52
+ /** World-space double-precision origin for the model. */
53
+ origin?: number[];
54
+ /** Single-precision position offset, relative to origin. */
55
+ position?: number[];
56
+ /** Model scale. */
57
+ scale?: number[];
58
+ /** Model orientation as Euler angles (degrees) for X, Y, Z. */
59
+ rotation?: number[];
60
+ /** Enable Scalable Ambient Obscurance (SAO) for the model. */
61
+ saoEnabled?: boolean;
62
+ /** Force rendering of backfaces for the model. */
63
+ backfaces?: boolean;
64
+ /** Globalize Entity and MetaObject IDs to avoid clashes. */
65
+ globalizeObjectIds?: boolean;
66
+ /** Collect model statistics. */
67
+ stats?: ModelStats;
68
+ /** Indicates if compact data texture scene representation is enabled for this model. */
69
+ dtxEnabled?: boolean;
70
+ };
71
+
72
+ /**
73
+ * Viewer plugin that uses IfcOpenShell to load BIM models directly from IFC files.
74
+ */
75
+ export declare class IFCOpenShellLoaderPlugin extends Plugin {
76
+ /**
77
+ * Creates a new IFCOpenShellLoaderPlugin.
78
+ * @param viewer The Viewer instance.
79
+ * @param cfg Plugin configuration.
80
+ */
81
+ constructor(viewer: Viewer, cfg?: IFCOpenShellLoaderPluginConfigs);
82
+
83
+ /**
84
+ * Sets a custom data source for loading IFC files.
85
+ * Default is IFCOpenShellDefaultDataSource, which loads via HTTP.
86
+ */
87
+ set dataSource(arg: IFCOpenShellDefaultDataSource);
88
+
89
+ /**
90
+ * Gets the current data source for loading IFC files.
91
+ * Default is IFCOpenShellDefaultDataSource, which loads via HTTP.
92
+ */
93
+ get dataSource(): IFCOpenShellDefaultDataSource;
94
+
95
+ /**
96
+ * Gets whether object and metaobject IDs are globalized to avoid clashes.
97
+ * Default is false.
98
+ */
99
+ get globalizeObjectIds(): boolean;
100
+
101
+ /**
102
+ * Loads an IFC model into the Viewer's scene.
103
+ * @param params Loading parameters.
104
+ * @returns SceneModel representing the loaded model.
105
+ */
106
+ load(params?: LoadIFCOpenShellModel): SceneModel;
107
+ }
@@ -0,0 +1 @@
1
+ export * from './IFCOpenShellLoaderPlugin';
@@ -8,6 +8,7 @@ export * from "./DistanceMeasurementsPlugin";
8
8
  export * from "./DotBIMLoaderPlugin";
9
9
  export * from "./FastNavPlugin";
10
10
  export * from "./GLTFLoaderPlugin";
11
+ export * from "./IFCOpenShellLoaderPlugin";
11
12
  export * from "./LASLoaderPlugin";
12
13
  export * from "./NavCubePlugin";
13
14
  export * from "./OBJLoaderPlugin";