@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.
- package/dist/xeokit-sdk.cjs.js +1173 -130
- package/dist/xeokit-sdk.es.js +1172 -131
- package/dist/xeokit-sdk.es5.js +735 -419
- package/dist/xeokit-sdk.min.cjs.js +7 -7
- package/dist/xeokit-sdk.min.es.js +7 -7
- package/dist/xeokit-sdk.min.es5.js +6 -6
- package/package.json +1 -1
- package/src/plugins/IFCOpenShellLoaderPlugin/IFCOpenShellDefaultDataSource.js +74 -0
- package/src/plugins/IFCOpenShellLoaderPlugin/IFCOpenShellLoaderPlugin.js +900 -0
- package/src/plugins/IFCOpenShellLoaderPlugin/index.js +2 -0
- package/src/plugins/index.js +1 -0
- package/src/viewer/scene/CameraControl/CameraControl.js +2 -2
- package/src/viewer/scene/math/math.js +171 -106
- package/src/viewer/scene/model/SceneModelMesh.js +2 -0
- package/src/viewer/scene/model/compression.js +6 -6
- package/src/viewer/scene/model/layer/DTXLayer.js +5 -1
- package/src/viewer/scene/model/layer/Layer.js +1 -4
- package/src/viewer/scene/model/layer/VBOLayer.js +7 -3
- package/src/viewer/scene/sectionCaps/SectionCaps.js +5 -4
- package/types/plugins/IFCOpenShellLoaderPlugin/IFCOpenShellLoaderPlugin.d.ts +107 -0
- package/types/plugins/IFCOpenShellLoaderPlugin/index.d.ts +1 -0
- package/types/plugins/index.d.ts +1 -0
- package/types/viewer/scene/CameraControl/CameraControl.d.ts +5 -5
package/dist/xeokit-sdk.es.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* xeokit-sdk v2.6.
|
|
3
|
-
* Commit:
|
|
4
|
-
* Built: 2025-
|
|
2
|
+
* xeokit-sdk v2.6.96
|
|
3
|
+
* Commit: c76eb4c08555d55fdd54313f5fb25481d28d1150
|
|
4
|
+
* Built: 2025-12-02T12:22:54.826Z
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
if (typeof window !== 'undefined') {
|
|
8
|
-
window.__XEOKIT__ = { version: '2.6.
|
|
8
|
+
window.__XEOKIT__ = { version: '2.6.96', commit: 'c76eb4c08555d55fdd54313f5fb25481d28d1150', built: '2025-12-02T12:22:54.826Z' };
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/** @private */
|
|
@@ -7433,9 +7433,12 @@ math.makeSectionPlaneSlicer = (function() {
|
|
|
7433
7433
|
math.vec3();
|
|
7434
7434
|
|
|
7435
7435
|
const triangle = [ math.vec3(), math.vec3(), math.vec3() ];
|
|
7436
|
+
const tmpIntersections = [ null, null, null ];
|
|
7437
|
+
const tmpPositions = [ null, null, null ];
|
|
7438
|
+
const tmpReferences = [ null, null, null ];
|
|
7436
7439
|
|
|
7437
|
-
const worldUp = [0, 1, 0];
|
|
7438
7440
|
const worldRight = [1, 0, 0];
|
|
7441
|
+
const worldUp = [0, 1, 0];
|
|
7439
7442
|
const worldForward = [0, 0, 1];
|
|
7440
7443
|
|
|
7441
7444
|
const sqDistVec3 = (function() {
|
|
@@ -7450,84 +7453,113 @@ math.makeSectionPlaneSlicer = (function() {
|
|
|
7450
7453
|
return ret;
|
|
7451
7454
|
};
|
|
7452
7455
|
|
|
7453
|
-
const setCoord2D = (p, dst) => { dst[0] = p.coord2Dx; dst[1] = p.coord2Dy; return dst; };
|
|
7454
|
-
|
|
7455
7456
|
const ccw = (a, b, c) => ((c[1] - a[1]) * (b[0] - a[0])) > ((b[1] - a[1]) * (c[0] - a[0]));
|
|
7456
7457
|
|
|
7457
|
-
|
|
7458
|
-
const
|
|
7459
|
-
const
|
|
7460
|
-
|
|
7461
|
-
return false;
|
|
7462
|
-
}
|
|
7458
|
+
return function(plane) {
|
|
7459
|
+
const planeU = math.vec3ApplyQuaternion(plane.quaternion, worldRight, math.vec3());
|
|
7460
|
+
const planeV = math.vec3ApplyQuaternion(plane.quaternion, worldUp, math.vec3());
|
|
7461
|
+
const planeN = math.vec3ApplyQuaternion(plane.quaternion, worldForward, math.vec3());
|
|
7463
7462
|
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
const [
|
|
7469
|
-
const
|
|
7463
|
+
return function(mesh, cfg = { }) {
|
|
7464
|
+
const planeToMesh = math.subVec3(mesh.origin, plane.pos, math.vec3());
|
|
7465
|
+
const planeDist = math.dotVec3(planeN, planeToMesh);
|
|
7466
|
+
|
|
7467
|
+
const unsortedSegment = [ ];
|
|
7468
|
+
const indexedPositions = [ null ]; // to never return 0 from addPosition, so its result can be used as a predicate
|
|
7469
|
+
const addPosition = (p, addUV) => {
|
|
7470
|
+
const idx = indexedPositions.length;
|
|
7471
|
+
const P = new Float64Array(5);
|
|
7472
|
+
P.set(p);
|
|
7473
|
+
if (addUV) {
|
|
7474
|
+
math.addVec3(planeToMesh, p, tempVec3a);
|
|
7475
|
+
P[3] = -math.dotVec3(planeU, tempVec3a);
|
|
7476
|
+
P[4] = math.dotVec3(planeV, tempVec3a);
|
|
7477
|
+
}
|
|
7478
|
+
indexedPositions.push(P);
|
|
7479
|
+
return idx;
|
|
7480
|
+
};
|
|
7470
7481
|
|
|
7471
|
-
|
|
7472
|
-
for (let i = 0, j = outerEndpoints.length - 1; i < outerEndpoints.length; j = i++) {
|
|
7473
|
-
const o0 = setCoord2D(outerEndpoints[i], tempVec2c);
|
|
7474
|
-
const o1 = setCoord2D(outerEndpoints[j], tempVec2d);
|
|
7482
|
+
const getCoord2D = (idx, dst) => { const P = indexedPositions[idx]; dst[0] = P[3]; dst[1] = P[4]; return dst; };
|
|
7475
7483
|
|
|
7476
|
-
|
|
7477
|
-
|
|
7484
|
+
const isLoopInside = (inner, outer) => {
|
|
7485
|
+
const bbI = inner.boundingBox;
|
|
7486
|
+
const bbO = outer.boundingBox;
|
|
7487
|
+
if ((bbI[0] < bbO[0]) || (bbI[1] < bbO[1]) || (bbI[2] > bbO[2]) || (bbI[3] > bbO[3])) {
|
|
7488
|
+
return false;
|
|
7478
7489
|
}
|
|
7479
7490
|
|
|
7480
|
-
const
|
|
7481
|
-
const
|
|
7491
|
+
const innerEndpoints = inner.endPoints;
|
|
7492
|
+
const outerEndpoints = outer.endPoints;
|
|
7493
|
+
for (let ii = 0, ij = innerEndpoints.length - 1; ii < innerEndpoints.length; ij = ii++) {
|
|
7494
|
+
const i0 = getCoord2D(innerEndpoints[ii], tempVec2a);
|
|
7495
|
+
const [i0x, i0y] = i0;
|
|
7496
|
+
const i1 = getCoord2D(innerEndpoints[ij], tempVec2b);
|
|
7482
7497
|
|
|
7483
|
-
|
|
7484
|
-
|
|
7498
|
+
let inside = false;
|
|
7499
|
+
for (let i = 0, j = outerEndpoints.length - 1; i < outerEndpoints.length; j = i++) {
|
|
7500
|
+
const o0 = getCoord2D(outerEndpoints[i], tempVec2c);
|
|
7501
|
+
const o1 = getCoord2D(outerEndpoints[j], tempVec2d);
|
|
7485
7502
|
|
|
7486
|
-
|
|
7487
|
-
|
|
7503
|
+
if ((ccw(i0, o0, o1) !== ccw(i1, o0, o1)) && (ccw(i0, i1, o0) !== ccw(i0, i1, o1))) {
|
|
7504
|
+
return false; // segments intersect
|
|
7505
|
+
}
|
|
7488
7506
|
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7507
|
+
const [o0x, o0y] = o0;
|
|
7508
|
+
const [o1x, o1y] = o1;
|
|
7509
|
+
|
|
7510
|
+
const dx = i0x - o0x;
|
|
7511
|
+
const dy = i0y - o0y;
|
|
7493
7512
|
|
|
7494
|
-
|
|
7495
|
-
|
|
7513
|
+
const oDx = o1x - o0x;
|
|
7514
|
+
const oDy = o1y - o0y;
|
|
7515
|
+
|
|
7516
|
+
const dot = (((oDx !== 0) || (oDy !== 0)) && (Math.abs(oDx * dy - oDy * dx) < 1e-10)) ? (dx * oDx + dy * oDy) : -1;
|
|
7517
|
+
if ((dot >= 0) && (dot <= (Math.pow(oDx, 2) + Math.pow(oDy, 2)))) {
|
|
7518
|
+
return false; // on edge
|
|
7519
|
+
}
|
|
7520
|
+
|
|
7521
|
+
if (((o0y > i0y) !== (o1y > i0y)) && (dx < (dy * oDx / oDy))) {
|
|
7522
|
+
inside = !inside;
|
|
7523
|
+
}
|
|
7524
|
+
}
|
|
7525
|
+
if (! inside) {
|
|
7526
|
+
return false;
|
|
7527
|
+
}
|
|
7496
7528
|
}
|
|
7497
|
-
}
|
|
7498
|
-
if (! inside) {
|
|
7499
|
-
return false;
|
|
7500
|
-
}
|
|
7501
|
-
}
|
|
7502
7529
|
|
|
7503
|
-
|
|
7504
|
-
|
|
7530
|
+
return true;
|
|
7531
|
+
};
|
|
7505
7532
|
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
const planeV = math.vec3ApplyQuaternion(planeRot, worldUp, math.vec3());
|
|
7509
|
-
const planeN = math.vec3ApplyQuaternion(planeRot, worldForward, math.vec3());
|
|
7533
|
+
const pos = { indices: [ ], normals: [ ] };
|
|
7534
|
+
const neg = (! cfg.onlyPosSliceWithUV) && { indices: [ ], normals: [ ] };
|
|
7510
7535
|
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7536
|
+
const appendOnSide = (dst, indices, normal) => {
|
|
7537
|
+
if (dst) {
|
|
7538
|
+
const p0 = indexedPositions[indices[0]];
|
|
7539
|
+
normal ||= math.normalizeVec3(math.cross3Vec3(math.subVec3(indexedPositions[indices[1]], p0, tempVec3a),
|
|
7540
|
+
math.subVec3(indexedPositions[indices[2]], p0, tempVec3b),
|
|
7541
|
+
tempVec3a),
|
|
7542
|
+
tempVec3a);
|
|
7514
7543
|
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7544
|
+
for (let i = 0; i < 3; ++i) {
|
|
7545
|
+
dst.indices.push(indices[i]);
|
|
7546
|
+
dst.normals.push(normal[0], normal[1], normal[2]);
|
|
7547
|
+
}
|
|
7548
|
+
}
|
|
7549
|
+
};
|
|
7518
7550
|
|
|
7519
7551
|
const setVertex = (i, dst) => {
|
|
7520
|
-
const idx =
|
|
7521
|
-
dst[0] =
|
|
7522
|
-
dst[1] =
|
|
7523
|
-
dst[2] =
|
|
7552
|
+
const idx = mesh.indices[i] * 3;
|
|
7553
|
+
dst[0] = mesh.positions[idx + 0];
|
|
7554
|
+
dst[1] = mesh.positions[idx + 1];
|
|
7555
|
+
dst[2] = mesh.positions[idx + 2];
|
|
7524
7556
|
return dst;
|
|
7525
7557
|
};
|
|
7526
7558
|
|
|
7527
|
-
for (let
|
|
7528
|
-
const p0 = setVertex(
|
|
7529
|
-
const p1 = setVertex(
|
|
7530
|
-
const p2 = setVertex(
|
|
7559
|
+
for (let faceIdx = 0; faceIdx < mesh.indices.length; faceIdx += 3) {
|
|
7560
|
+
const p0 = setVertex(faceIdx + 0, triangle[0]);
|
|
7561
|
+
const p1 = setVertex(faceIdx + 1, triangle[1]);
|
|
7562
|
+
const p2 = setVertex(faceIdx + 2, triangle[2]);
|
|
7531
7563
|
|
|
7532
7564
|
if (math.compareVec3(p0, p1) || math.compareVec3(p1, p2) || math.compareVec3(p2, p0)) {
|
|
7533
7565
|
continue; // skip degenerate triangle
|
|
@@ -7538,12 +7570,60 @@ math.makeSectionPlaneSlicer = (function() {
|
|
|
7538
7570
|
const d2 = planeDist + math.dotVec3(planeN, p2);
|
|
7539
7571
|
|
|
7540
7572
|
if ((d0 !== 0) || (d1 !== 0) || (d2 !== 0)) {
|
|
7541
|
-
const i0 = (d0 * d1 <= 0) && addPosition(math.lerpVec3(d0 / (d0 - d1), 0, 1, p0, p1, tempVec3a));
|
|
7542
|
-
const i1 = (d1 * d2 <= 0) && addPosition(math.lerpVec3(d1 / (d1 - d2), 0, 1, p1, p2, tempVec3a));
|
|
7543
|
-
const i2 = (d2 * d0 <= 0) && addPosition(math.lerpVec3(d2 / (d2 - d0), 0, 1, p2, p0, tempVec3a));
|
|
7573
|
+
const i0 = (d0 * d1 <= 0) && addPosition(math.lerpVec3(d0 / (d0 - d1), 0, 1, p0, p1, tempVec3a), true);
|
|
7574
|
+
const i1 = (d1 * d2 <= 0) && addPosition(math.lerpVec3(d1 / (d1 - d2), 0, 1, p1, p2, tempVec3a), true);
|
|
7575
|
+
const i2 = (d2 * d0 <= 0) && addPosition(math.lerpVec3(d2 / (d2 - d0), 0, 1, p2, p0, tempVec3a), true);
|
|
7576
|
+
|
|
7577
|
+
const p = (! cfg.onlyPosSliceWithUV) && tmpPositions;
|
|
7578
|
+
if (p) {
|
|
7579
|
+
p[0] = addPosition(p0);
|
|
7580
|
+
p[1] = addPosition(p1);
|
|
7581
|
+
p[2] = addPosition(p2);
|
|
7582
|
+
}
|
|
7544
7583
|
|
|
7545
7584
|
if (i0 ? (i1 || i2) : (i1 && i2)) { // triangle intersected by the section plane
|
|
7546
7585
|
unsortedSegment.push(i0 ? [ i0, i1 || i2 ] : [ i1, i2 ]);
|
|
7586
|
+
|
|
7587
|
+
if (p) {
|
|
7588
|
+
if ((d0 === 0) && (d1 === 0)) {
|
|
7589
|
+
appendOnSide((d2 > 0) ? pos : neg, p);
|
|
7590
|
+
} else if ((d0 === 0) && (d2 === 0)) {
|
|
7591
|
+
appendOnSide((d1 > 0) ? pos : neg, p);
|
|
7592
|
+
} else if ((d1 === 0) && (d2 === 0)) {
|
|
7593
|
+
appendOnSide((d0 > 0) ? pos : neg, p);
|
|
7594
|
+
} else {
|
|
7595
|
+
const isPos = (i0 ? d0 : d1) > 0;
|
|
7596
|
+
const dst0 = isPos ? pos : neg;
|
|
7597
|
+
const dst1 = isPos ? neg : pos;
|
|
7598
|
+
const i = tmpIntersections;
|
|
7599
|
+
i[0] = i0;
|
|
7600
|
+
i[1] = i1;
|
|
7601
|
+
i[2] = i2;
|
|
7602
|
+
const ref = tmpReferences;
|
|
7603
|
+
if (i0) {
|
|
7604
|
+
ref[0] = 0; ref[1] = 1; ref[2] = 2;
|
|
7605
|
+
} else {
|
|
7606
|
+
ref[0] = 1; ref[1] = 2; ref[2] = 0;
|
|
7607
|
+
}
|
|
7608
|
+
if (i[ref[1]]) {
|
|
7609
|
+
appendOnSide(dst0, [ p[ref[0]], i[ref[0]], p[ref[2]] ]);
|
|
7610
|
+
appendOnSide(dst0, [ i[ref[0]], i[ref[1]], p[ref[2]] ]);
|
|
7611
|
+
appendOnSide(dst1, [ i[ref[0]], p[ref[1]], i[ref[1]] ]);
|
|
7612
|
+
} else {
|
|
7613
|
+
appendOnSide(dst0, [ p[ref[0]], i[ref[0]], i[ref[2]] ]);
|
|
7614
|
+
appendOnSide(dst1, [ i[ref[0]], p[ref[1]], p[ref[2]] ]);
|
|
7615
|
+
appendOnSide(dst1, [ i[ref[0]], p[ref[2]], i[ref[2]] ]);
|
|
7616
|
+
}
|
|
7617
|
+
}
|
|
7618
|
+
}
|
|
7619
|
+
} else if (p) {
|
|
7620
|
+
if ((d0 >= 0) && (d1 >= 0) && (d2 >= 0)) {
|
|
7621
|
+
appendOnSide(pos, p);
|
|
7622
|
+
} else if ((d0 <= 0) && (d1 <= 0) && (d2 <= 0)) {
|
|
7623
|
+
appendOnSide(neg, p);
|
|
7624
|
+
} else {
|
|
7625
|
+
debugger;
|
|
7626
|
+
}
|
|
7547
7627
|
}
|
|
7548
7628
|
}
|
|
7549
7629
|
}
|
|
@@ -7589,37 +7669,30 @@ math.makeSectionPlaneSlicer = (function() {
|
|
|
7589
7669
|
}
|
|
7590
7670
|
|
|
7591
7671
|
const loops = endpointLoops.filter(endPoints => endPoints.length > 2).map((endPoints, idx) => {
|
|
7592
|
-
const planeEndpoints = endPoints.map(posIdx => {
|
|
7593
|
-
const P = math.addVec3(planeToMesh, indexedPositions[posIdx], tempVec3a);
|
|
7594
|
-
return { posIdx: posIdx, coord2Dx: math.dotVec3(planeU, P), coord2Dy: math.dotVec3(planeV, P) };
|
|
7595
|
-
});
|
|
7596
7672
|
let doubleArea = 0;
|
|
7597
7673
|
const aabb = math.collapseAABB2(math.AABB2());
|
|
7598
|
-
for (let i = 0; i <
|
|
7599
|
-
|
|
7600
|
-
tempVec2a[0] = p0.coord2Dx;
|
|
7601
|
-
tempVec2a[1] = p0.coord2Dy;
|
|
7674
|
+
for (let i = 0; i < endPoints.length; i++) {
|
|
7675
|
+
getCoord2D(endPoints[i], tempVec2a);
|
|
7602
7676
|
math.expandAABB2Point2(aabb, tempVec2a);
|
|
7603
|
-
|
|
7604
|
-
doubleArea += (
|
|
7677
|
+
getCoord2D(endPoints[(i + 1) % endPoints.length], tempVec2b);
|
|
7678
|
+
doubleArea += (tempVec2a[0] * tempVec2b[1] - tempVec2b[0] * tempVec2a[1]);
|
|
7605
7679
|
}
|
|
7606
7680
|
return {
|
|
7607
7681
|
boundingBox: aabb,
|
|
7608
7682
|
doubleArea: Math.abs(doubleArea),
|
|
7609
|
-
endPoints:
|
|
7683
|
+
endPoints: endPoints
|
|
7610
7684
|
};
|
|
7611
7685
|
}).sort((a, b) => b.doubleArea - a.doubleArea);
|
|
7612
7686
|
|
|
7613
|
-
const sliceGeometries = [ ];
|
|
7614
|
-
|
|
7615
7687
|
while (loops.length > 0) {
|
|
7616
7688
|
const vertices2D = [ ];
|
|
7617
7689
|
const vertices3D = [ ];
|
|
7618
7690
|
|
|
7619
7691
|
const appendLoopVertices = loop => loop.endPoints.forEach(endpoint2D => {
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7692
|
+
getCoord2D(endpoint2D, tempVec2a);
|
|
7693
|
+
vertices2D.push(tempVec2a[0], tempVec2a[1]);
|
|
7694
|
+
vertices3D.push(endpoint2D);
|
|
7695
|
+
});
|
|
7623
7696
|
|
|
7624
7697
|
const outerLoop = loops.shift();
|
|
7625
7698
|
appendLoopVertices(outerLoop);
|
|
@@ -7641,41 +7714,33 @@ math.makeSectionPlaneSlicer = (function() {
|
|
|
7641
7714
|
// Triangulate
|
|
7642
7715
|
const triangles = earcut(vertices2D, innerLoops.map(loop => loop.index));
|
|
7643
7716
|
|
|
7644
|
-
const positions = [ ];
|
|
7645
|
-
const normals = [ ];
|
|
7646
|
-
const uv = [ ];
|
|
7647
7717
|
for (let i = 0; i < triangles.length; i += 3) {
|
|
7648
|
-
const
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7718
|
+
const ti = tmpIntersections;
|
|
7719
|
+
ti[0] = vertices3D[triangles[i + 0]];
|
|
7720
|
+
ti[1] = vertices3D[triangles[i + 1]];
|
|
7721
|
+
ti[2] = vertices3D[triangles[i + 2]];
|
|
7722
|
+
const v0 = indexedPositions[ti[0]];
|
|
7723
|
+
math.subVec3(indexedPositions[ti[1]], v0, tempVec3b);
|
|
7724
|
+
math.subVec3(indexedPositions[ti[2]], v0, tempVec3c);
|
|
7653
7725
|
math.normalizeVec3(math.cross3Vec3(tempVec3b, tempVec3c, tempVec3c), tempVec3c);
|
|
7654
7726
|
const facedPositively = math.dotVec3(tempVec3c, planeN) <= 0;
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
for (let j = 0; j < 3; ++j) {
|
|
7659
|
-
const vIdx = triangles[i + (facedPositively ? j : (2 - j))];
|
|
7660
|
-
const v = indexedPositions[vertices3D[vIdx]];
|
|
7661
|
-
positions.push(v[0], v[1], v[2]);
|
|
7662
|
-
normals.push(tempVec3c[0], tempVec3c[1], tempVec3c[2]);
|
|
7663
|
-
const uvOff = 2 * vIdx;
|
|
7664
|
-
uv.push(-vertices2D[uvOff], vertices2D[uvOff + 1]);
|
|
7665
|
-
}
|
|
7666
|
-
}
|
|
7667
|
-
|
|
7668
|
-
if (positions.length > 0) {
|
|
7669
|
-
sliceGeometries.push({
|
|
7670
|
-
indices: iota(positions.length / 3),
|
|
7671
|
-
positions: positions,
|
|
7672
|
-
normals: normals,
|
|
7673
|
-
uv: uv
|
|
7674
|
-
});
|
|
7727
|
+
appendOnSide(facedPositively ? pos : neg, ti, tempVec3c);
|
|
7728
|
+
const tmp = ti[0]; ti[0] = ti[2]; ti[2] = tmp;
|
|
7729
|
+
appendOnSide(facedPositively ? neg : pos, ti, math.negateVec3(tempVec3c, tempVec3c));
|
|
7675
7730
|
}
|
|
7676
7731
|
}
|
|
7677
7732
|
|
|
7678
|
-
|
|
7733
|
+
const side = src => src && (src.indices.length > 0) && (function() {
|
|
7734
|
+
const positions = [ ];
|
|
7735
|
+
const uv = cfg.onlyPosSliceWithUV && [ ];
|
|
7736
|
+
src.indices.forEach(idx => {
|
|
7737
|
+
const P = indexedPositions[idx];
|
|
7738
|
+
positions.push(P[0], P[1], P[2]);
|
|
7739
|
+
uv && uv.push(P[3], P[4]);
|
|
7740
|
+
});
|
|
7741
|
+
return { indices: iota(src.indices.length), positions: positions, normals: src.normals, uv: uv };
|
|
7742
|
+
})();
|
|
7743
|
+
return { pos: side(pos), neg: side(neg) };
|
|
7679
7744
|
};
|
|
7680
7745
|
};
|
|
7681
7746
|
})();
|
|
@@ -31327,6 +31392,8 @@ class SceneModelMesh {
|
|
|
31327
31392
|
*/
|
|
31328
31393
|
_destroy() {
|
|
31329
31394
|
this.model.scene._renderer.putPickID(this.pickId);
|
|
31395
|
+
|
|
31396
|
+
this.layer = null;
|
|
31330
31397
|
}
|
|
31331
31398
|
}
|
|
31332
31399
|
|
|
@@ -31939,10 +32006,7 @@ const getRenderers = (function() {
|
|
|
31939
32006
|
appendFragmentOutputs: programSetup.appendFragmentOutputs,
|
|
31940
32007
|
cleanerEdges: programSetup.cleanerEdges,
|
|
31941
32008
|
clipPos: clipPos,
|
|
31942
|
-
clippableTest:
|
|
31943
|
-
const vClippable = programVariables.createVarying("float", "vClippable", () => `${attributes.clippable} ? 1.0 : 0.0`, "flat");
|
|
31944
|
-
return () => `${vClippable} > 0.0`;
|
|
31945
|
-
})(),
|
|
32009
|
+
clippableTest: renderingAttributes.clippableTest,
|
|
31946
32010
|
clippingCaps: programSetup.clippingCaps,
|
|
31947
32011
|
crossSections: scene.crossSections,
|
|
31948
32012
|
discardPoints: setupPoints && pointsMaterial.roundPoints,
|
|
@@ -33499,9 +33563,13 @@ const makeDTXRenderingAttributes = function(programVariables, isTriangle) {
|
|
|
33499
33563
|
const colorsAndFlags = (offset) => perObjColsFlags(`ivec2(objectIndexCoords.x*8+${offset}, objectIndexCoords.y)`);
|
|
33500
33564
|
|
|
33501
33565
|
return {
|
|
33566
|
+
clippableTest: (function() {
|
|
33567
|
+
const vClippable = programVariables.createVarying("uint", "vClippable", () => "flags2.r", "flat");
|
|
33568
|
+
return () => `${vClippable} > 0u`;
|
|
33569
|
+
})(),
|
|
33570
|
+
|
|
33502
33571
|
geometryParameters: {
|
|
33503
33572
|
attributes: {
|
|
33504
|
-
clippable: "(flags2.r > 0u)",
|
|
33505
33573
|
color: colorA,
|
|
33506
33574
|
flags: iota$2(4).map(i => `int(flags[${i}])`),
|
|
33507
33575
|
metallicRoughness: null,
|
|
@@ -33638,9 +33706,9 @@ function quantizePositions(positions, aabb, positionsDecodeMatrix) { // http://c
|
|
|
33638
33706
|
const xmin = aabb[0];
|
|
33639
33707
|
const ymin = aabb[1];
|
|
33640
33708
|
const zmin = aabb[2];
|
|
33641
|
-
const xwid = aabb[3] - xmin;
|
|
33642
|
-
const ywid = aabb[4] - ymin;
|
|
33643
|
-
const zwid = aabb[5] - zmin;
|
|
33709
|
+
const xwid = (aabb[3] - xmin) || 1;
|
|
33710
|
+
const ywid = (aabb[4] - ymin) || 1;
|
|
33711
|
+
const zwid = (aabb[5] - zmin) || 1;
|
|
33644
33712
|
const maxInt = 65525;
|
|
33645
33713
|
const xMultiplier = maxInt / xwid;
|
|
33646
33714
|
const yMultiplier = maxInt / ywid;
|
|
@@ -33669,9 +33737,9 @@ function createPositionsDecodeMatrix(aabb, positionsDecodeMatrix) { // http://cg
|
|
|
33669
33737
|
const xmin = aabb[0];
|
|
33670
33738
|
const ymin = aabb[1];
|
|
33671
33739
|
const zmin = aabb[2];
|
|
33672
|
-
const xwid = aabb[3] - xmin;
|
|
33673
|
-
const ywid = aabb[4] - ymin;
|
|
33674
|
-
const zwid = aabb[5] - zmin;
|
|
33740
|
+
const xwid = (aabb[3] - xmin) || 1;
|
|
33741
|
+
const ywid = (aabb[4] - ymin) || 1;
|
|
33742
|
+
const zwid = (aabb[5] - zmin) || 1;
|
|
33675
33743
|
const maxInt = 65525;
|
|
33676
33744
|
math.identityMat4(translate$1);
|
|
33677
33745
|
math.translationMat4v(aabb, translate$1);
|
|
@@ -34659,8 +34727,8 @@ class VBOLayer extends Layer {
|
|
|
34659
34727
|
} else { // triangles
|
|
34660
34728
|
if (subGeometry && subGeometry.vertices) {
|
|
34661
34729
|
return drawPoints;
|
|
34662
|
-
} else if (subGeometry
|
|
34663
|
-
return elementsDrawer(gl.LINES, edgeIndicesBuf);
|
|
34730
|
+
} else if (subGeometry) {
|
|
34731
|
+
return edgeIndicesBuf ? elementsDrawer(gl.LINES, edgeIndicesBuf) : (() => { });
|
|
34664
34732
|
} else {
|
|
34665
34733
|
return elementsDrawer(gl.TRIANGLES, indicesBuf);
|
|
34666
34734
|
}
|
|
@@ -34742,9 +34810,13 @@ const makeVBORenderingAttributes = function(programVariables, instancing, entity
|
|
|
34742
34810
|
return {
|
|
34743
34811
|
dontCullOnAlphaZero: true,
|
|
34744
34812
|
|
|
34813
|
+
clippableTest: (function() {
|
|
34814
|
+
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
|
|
34815
|
+
return () => `${vClippable} != 0.0`;
|
|
34816
|
+
})(),
|
|
34817
|
+
|
|
34745
34818
|
geometryParameters: {
|
|
34746
34819
|
attributes: {
|
|
34747
|
-
clippable: `((int(${attributes.flags}) >> 16 & 0xF) == 1)`,
|
|
34748
34820
|
color: attributes.color,
|
|
34749
34821
|
flags: iota$1(4).map(i => ({ toString: () => `(int(${attributes.flags}) >> ${i * 4} & 0xF)` })),
|
|
34750
34822
|
metallicRoughness: attributes.metallicRoughness,
|
|
@@ -54010,7 +54082,7 @@ class SectionCaps {
|
|
|
54010
54082
|
const visibleSceneModels = Object.values(scene.models).filter(sceneModel => (sceneModel.id in modelCaches) && sceneModel.visible);
|
|
54011
54083
|
sectionPlanes.forEach((plane) => {
|
|
54012
54084
|
if (plane.active) {
|
|
54013
|
-
const sliceMesh = math.makeSectionPlaneSlicer(plane
|
|
54085
|
+
const sliceMesh = math.makeSectionPlaneSlicer(plane);
|
|
54014
54086
|
visibleSceneModels.forEach(sceneModel => {
|
|
54015
54087
|
const modelAABB = sceneModel.aabb;
|
|
54016
54088
|
if (math.planeIntersectsAABB3(plane, modelAABB)) {
|
|
@@ -54030,10 +54102,11 @@ class SectionCaps {
|
|
|
54030
54102
|
});
|
|
54031
54103
|
|
|
54032
54104
|
entityCache.meshCaches.filter(meshCache => math.planeIntersectsAABB3(plane, meshCache.mesh.aabb)).forEach((meshCache, meshIdx) => {
|
|
54033
|
-
sliceMesh(modelCenter, meshCache.meshIndices, meshCache.meshVertices
|
|
54105
|
+
const geo = sliceMesh({ origin: modelCenter, indices: meshCache.meshIndices, positions: meshCache.meshVertices }, { onlyPosSliceWithUV: true }).pos;
|
|
54106
|
+
if (geo) {
|
|
54034
54107
|
entityCache.capMeshes.push(new Mesh(scene, {
|
|
54035
54108
|
isObject: true,
|
|
54036
|
-
id: `${plane.id}-${entityId}-${meshIdx}
|
|
54109
|
+
id: `${plane.id}-${entityId}-${meshIdx}`,
|
|
54037
54110
|
material: entity.capMaterial,
|
|
54038
54111
|
origin: math.addVec3(modelCenter, math.mulVec3Scalar(plane.dir, 0.001, tempVec3a$a), tempVec3a$a),
|
|
54039
54112
|
geometry: new ReadableGeometry(scene, {
|
|
@@ -54044,7 +54117,7 @@ class SectionCaps {
|
|
|
54044
54117
|
uv: geo.uv
|
|
54045
54118
|
})
|
|
54046
54119
|
}));
|
|
54047
|
-
}
|
|
54120
|
+
}
|
|
54048
54121
|
});
|
|
54049
54122
|
}
|
|
54050
54123
|
});
|
|
@@ -60456,7 +60529,7 @@ class CameraControl extends Component {
|
|
|
60456
60529
|
*
|
|
60457
60530
|
* See class docs for usage.
|
|
60458
60531
|
*
|
|
60459
|
-
* @param {{Number:Number}|String} value Either a set of new key mappings, or a string to select a keyboard layout,
|
|
60532
|
+
* @param {{Number:(Number | Number[])[]} | String} value Either a set of new key mappings, or a string to select a keyboard layout,
|
|
60460
60533
|
* which causes ````CameraControl```` to use the default key mappings for that layout.
|
|
60461
60534
|
*/
|
|
60462
60535
|
set keyMap(value) {
|
|
@@ -60535,7 +60608,7 @@ class CameraControl extends Component {
|
|
|
60535
60608
|
/**
|
|
60536
60609
|
* Gets custom mappings of keys to {@link CameraControl} actions.
|
|
60537
60610
|
*
|
|
60538
|
-
* @returns {{Number:Number}} Current key mappings.
|
|
60611
|
+
* @returns {{Number:(Number | Number[])[]}} Current key mappings.
|
|
60539
60612
|
*/
|
|
60540
60613
|
get keyMap() {
|
|
60541
60614
|
return this._keyMap;
|
|
@@ -131072,6 +131145,974 @@ class CxConverterIFCLoaderPlugin extends Plugin {
|
|
|
131072
131145
|
}
|
|
131073
131146
|
}
|
|
131074
131147
|
|
|
131148
|
+
/**
|
|
131149
|
+
* Default data access strategy for {@link IFCOpenShellLoaderPlugin}.
|
|
131150
|
+
*
|
|
131151
|
+
* This just loads assets using XMLHttpRequest.
|
|
131152
|
+
*/
|
|
131153
|
+
class IFCOpenShellDefaultDataSource {
|
|
131154
|
+
|
|
131155
|
+
constructor(cfg = {}) {
|
|
131156
|
+
this.cacheBuster = (cfg.cacheBuster !== false);
|
|
131157
|
+
}
|
|
131158
|
+
|
|
131159
|
+
_cacheBusterURL(url) {
|
|
131160
|
+
if (!this.cacheBuster) {
|
|
131161
|
+
return url;
|
|
131162
|
+
}
|
|
131163
|
+
const timestamp = new Date().getTime();
|
|
131164
|
+
if (url.indexOf('?') > -1) {
|
|
131165
|
+
return url + '&_=' + timestamp;
|
|
131166
|
+
} else {
|
|
131167
|
+
return url + '?_=' + timestamp;
|
|
131168
|
+
}
|
|
131169
|
+
}
|
|
131170
|
+
|
|
131171
|
+
/**
|
|
131172
|
+
* Gets the contents of the given IFC file in an arraybuffer.
|
|
131173
|
+
*
|
|
131174
|
+
* @param {String|Number} src Path or ID of an IFC file.
|
|
131175
|
+
* @param {Function} ok Callback fired on success, argument is the IFC file in an arraybuffer.
|
|
131176
|
+
* @param {Function} error Callback fired on error.
|
|
131177
|
+
*/
|
|
131178
|
+
getIFC(src, ok, error) {
|
|
131179
|
+
src = this._cacheBusterURL(src);
|
|
131180
|
+
|
|
131181
|
+
var defaultCallback = () => {
|
|
131182
|
+
};
|
|
131183
|
+
ok = ok || defaultCallback;
|
|
131184
|
+
error = error || defaultCallback;
|
|
131185
|
+
const dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/;
|
|
131186
|
+
const dataUriRegexResult = src.match(dataUriRegex);
|
|
131187
|
+
if (dataUriRegexResult) { // Safari can't handle data URIs through XMLHttpRequest
|
|
131188
|
+
const isBase64 = !!dataUriRegexResult[2];
|
|
131189
|
+
var data = dataUriRegexResult[3];
|
|
131190
|
+
data = window.decodeURIComponent(data);
|
|
131191
|
+
if (isBase64) {
|
|
131192
|
+
data = window.atob(data);
|
|
131193
|
+
}
|
|
131194
|
+
try {
|
|
131195
|
+
const buffer = new ArrayBuffer(data.length);
|
|
131196
|
+
const view = new Uint8Array(buffer);
|
|
131197
|
+
for (var i = 0; i < data.length; i++) {
|
|
131198
|
+
view[i] = data.charCodeAt(i);
|
|
131199
|
+
}
|
|
131200
|
+
ok(buffer);
|
|
131201
|
+
} catch (errMsg) {
|
|
131202
|
+
error(errMsg);
|
|
131203
|
+
}
|
|
131204
|
+
} else {
|
|
131205
|
+
const request = new XMLHttpRequest();
|
|
131206
|
+
request.open('GET', src, true);
|
|
131207
|
+
request.responseType = 'text';
|
|
131208
|
+
request.onreadystatechange = function () {
|
|
131209
|
+
if (request.readyState === 4) {
|
|
131210
|
+
if (request.status === 200) {
|
|
131211
|
+
ok(request.response);
|
|
131212
|
+
} else {
|
|
131213
|
+
error('getIFC error : ' + request.response);
|
|
131214
|
+
}
|
|
131215
|
+
}
|
|
131216
|
+
};
|
|
131217
|
+
request.send(null);
|
|
131218
|
+
}
|
|
131219
|
+
}
|
|
131220
|
+
}
|
|
131221
|
+
|
|
131222
|
+
/**
|
|
131223
|
+
* {@link Viewer} plugin that uses [IfcOpenShell](https://ifcopenshell.org/) to load BIM models directly from IFC files.
|
|
131224
|
+
*
|
|
131225
|
+
* <a href="https://xeokit.github.io/xeokit-sdk/examples/index.html#BIMOffline_IFCOpenShellLoaderPlugin_Duplex"><img src="https://xeokit.io/img/docs/IFCOpenShellLoaderPlugin/IFCOpenShellLoaderPlugin.png"></a>
|
|
131226
|
+
*
|
|
131227
|
+
* [[Run this example](https://xeokit.github.io/xeokit-sdk/examples/index.html#BIMOffline_IFCOpenShellLoaderPlugin_Duplex)]
|
|
131228
|
+
*
|
|
131229
|
+
* ## Overview
|
|
131230
|
+
*
|
|
131231
|
+
* * Loads small-to-medium sized BIM models directly from IFC files.
|
|
131232
|
+
* * Uses [IfcOpenShell API](https://ifcopenshell.org/) to parse IFC files in the browser.
|
|
131233
|
+
* * Loads IFC geometry, element structure metadata, and property sets.
|
|
131234
|
+
* * Not for large models. For best performance with large models, we recommend using {@link XKTLoaderPlugin}.
|
|
131235
|
+
* * Loads double-precision coordinates, enabling models to be viewed at global coordinates without accuracy loss.
|
|
131236
|
+
* * Filter which IFC types don't get loaded.
|
|
131237
|
+
* * Configure initial appearances of specified IFC types.
|
|
131238
|
+
* * Set a custom data source for IFC files.
|
|
131239
|
+
*
|
|
131240
|
+
* ## Limitations
|
|
131241
|
+
*
|
|
131242
|
+
* Loading and parsing huge IFC STEP files can be slow, and can overwhelm the browser, however. To view your
|
|
131243
|
+
* largest IFC models, we recommend instead pre-converting those to xeokit's compressed native .XKT format, then
|
|
131244
|
+
* loading them with {@link XKTLoaderPlugin} instead.</p>
|
|
131245
|
+
*
|
|
131246
|
+
* ## Scene representation
|
|
131247
|
+
*
|
|
131248
|
+
* When loading a model, IFCOpenShellLoaderPlugin creates an {@link Entity} that represents the model, which
|
|
131249
|
+
* will have {@link Entity#isModel} set ````true```` and will be registered by {@link Entity#id}
|
|
131250
|
+
* in {@link Scene#models}. The IFCOpenShellLoaderPlugin also creates an {@link Entity} for each object within the
|
|
131251
|
+
* model. Those Entities will have {@link Entity#isObject} set ````true```` and will be registered
|
|
131252
|
+
* by {@link Entity#id} in {@link Scene#objects}.
|
|
131253
|
+
*
|
|
131254
|
+
* ## Metadata
|
|
131255
|
+
*
|
|
131256
|
+
* When loading a model, IFCOpenShellLoaderPlugin also creates a {@link MetaModel} that represents the model, which contains
|
|
131257
|
+
* a {@link MetaObject} for each IFC element, plus a {@link PropertySet} for each IFC property set. Loading metadata
|
|
131258
|
+
* can be very slow, so we can also optionally disable it if we don't need it.
|
|
131259
|
+
*
|
|
131260
|
+
* ## Usage
|
|
131261
|
+
*
|
|
131262
|
+
* In the example below we'll load the Duplex BIM model from
|
|
131263
|
+
* an [IFC file](https://github.com/xeokit/xeokit-sdk/tree/master/assets/models/ifc). Within our {@link Viewer}, this
|
|
131264
|
+
* will create a bunch of {@link Entity}s that represents the model and its objects, along with a {@link MetaModel},
|
|
131265
|
+
* {@link MetaObject}s and {@link PropertySet}s that hold their metadata.
|
|
131266
|
+
*
|
|
131267
|
+
* ````javascript
|
|
131268
|
+
* import {Viewer, IFCOpenShellLoaderPlugin, NavCubePlugin, TreeViewPlugin} from "../../dist/xeokit-sdk.es.js";
|
|
131269
|
+
*
|
|
131270
|
+
* //------------------------------------------------------------------------------------------------------------------
|
|
131271
|
+
* // 1. Create a Viewer,
|
|
131272
|
+
* // 2. Arrange the camera
|
|
131273
|
+
* //------------------------------------------------------------------------------------------------------------------
|
|
131274
|
+
*
|
|
131275
|
+
* // 1
|
|
131276
|
+
* const viewer = new Viewer({
|
|
131277
|
+
* canvasId: "myCanvas",
|
|
131278
|
+
* transparent: true
|
|
131279
|
+
* });
|
|
131280
|
+
*
|
|
131281
|
+
* // 2
|
|
131282
|
+
* viewer.camera.eye = [-3.933, 2.855, 27.018];
|
|
131283
|
+
* viewer.camera.look = [4.400, 3.724, 8.899];
|
|
131284
|
+
* viewer.camera.up = [-0.018, 0.999, 0.039];
|
|
131285
|
+
*
|
|
131286
|
+
* //------------------------------------------------------------------------------------------------------------------
|
|
131287
|
+
* // 1. Create the IFCOpenShellLoaderPlugin,
|
|
131288
|
+
* // 2. Load an IFC model
|
|
131289
|
+
* //------------------------------------------------------------------------------------------------------------------
|
|
131290
|
+
*
|
|
131291
|
+
* // 1
|
|
131292
|
+
*
|
|
131293
|
+
* const ifcLoader = new IFCOpenShellLoaderPlugin(viewer, {
|
|
131294
|
+
* workerSrc: "./my/directory/IFCOpenShellWorker.js",
|
|
131295
|
+
* ifcOpenShellURL: "./my/directory/ifcopenshell-0.8.3+34a1bc6-cp313-cp313-emscripten_4_0_9_wasm32.whl"
|
|
131296
|
+
* });
|
|
131297
|
+
*
|
|
131298
|
+
* // 2
|
|
131299
|
+
* const model = ifcLoader.load({ // Returns an Entity that represents the model
|
|
131300
|
+
* id: "myModel",
|
|
131301
|
+
* src: "../assets/models/ifc/Duplex.ifc",
|
|
131302
|
+
* excludeTypes: ["IfcSpace"],
|
|
131303
|
+
* edges: true
|
|
131304
|
+
* });
|
|
131305
|
+
*
|
|
131306
|
+
* model.on("loaded", () => {
|
|
131307
|
+
*
|
|
131308
|
+
* //----------------------------------------------------------------------------------------------------------
|
|
131309
|
+
* // 1. Find metadata on the bottom storey
|
|
131310
|
+
* // 2. X-ray all the objects except for the bottom storey
|
|
131311
|
+
* // 3. Fit the bottom storey in view
|
|
131312
|
+
* //----------------------------------------------------------------------------------------------------------
|
|
131313
|
+
*
|
|
131314
|
+
* // 1
|
|
131315
|
+
* const metaModel = viewer.metaScene.metaModels["myModel"]; // MetaModel with ID "myModel"
|
|
131316
|
+
* const metaObject
|
|
131317
|
+
* = viewer.metaScene.metaObjects["1xS3BCk291UvhgP2dvNsgp"]; // MetaObject with ID "1xS3BCk291UvhgP2dvNsgp"
|
|
131318
|
+
*
|
|
131319
|
+
* const name = metaObject.name; // "01 eerste verdieping"
|
|
131320
|
+
* const type = metaObject.type; // "IfcBuildingStorey"
|
|
131321
|
+
* const parent = metaObject.parent; // MetaObject with type "IfcBuilding"
|
|
131322
|
+
* const children = metaObject.children; // Array of child MetaObjects
|
|
131323
|
+
* const objectId = metaObject.id; // "1xS3BCk291UvhgP2dvNsgp"
|
|
131324
|
+
* const objectIds = viewer.metaScene.getObjectIDsInSubtree(objectId); // IDs of leaf sub-objects
|
|
131325
|
+
* const aabb = viewer.scene.getAABB(objectIds); // Axis-aligned boundary of the leaf sub-objects
|
|
131326
|
+
*
|
|
131327
|
+
* // 2
|
|
131328
|
+
* viewer.scene.setObjectsXRayed(viewer.scene.objectIds, true);
|
|
131329
|
+
* viewer.scene.setObjectsXRayed(objectIds, false);
|
|
131330
|
+
*
|
|
131331
|
+
* // 3
|
|
131332
|
+
* viewer.cameraFlight.flyTo(aabb);
|
|
131333
|
+
*
|
|
131334
|
+
* // Find the model Entity by ID
|
|
131335
|
+
* model = viewer.scene.models["myModel"];
|
|
131336
|
+
*
|
|
131337
|
+
* // Destroy the model
|
|
131338
|
+
* model.destroy();
|
|
131339
|
+
* });
|
|
131340
|
+
* ````
|
|
131341
|
+
*
|
|
131342
|
+
* ## Configuring a custom data source
|
|
131343
|
+
*
|
|
131344
|
+
* By default, IFCOpenShellLoaderPlugin will load IFC files over HTTP.
|
|
131345
|
+
*
|
|
131346
|
+
* In the example below, we'll customize the way IFCOpenShellLoaderPlugin loads the files by configuring it with our own data source
|
|
131347
|
+
* object. For simplicity, our custom data source example also uses HTTP, using a couple of xeokit utility functions.
|
|
131348
|
+
*
|
|
131349
|
+
* ````javascript
|
|
131350
|
+
* import {utils} from "xeokit-sdk.es.js";
|
|
131351
|
+
*
|
|
131352
|
+
* class MyDataSource {
|
|
131353
|
+
*
|
|
131354
|
+
* constructor() {
|
|
131355
|
+
* }
|
|
131356
|
+
*
|
|
131357
|
+
* // Gets the contents of the given IFC file in an arraybuffer
|
|
131358
|
+
* getIFC(src, ok, error) {
|
|
131359
|
+
* console.log("MyDataSource#getIFC(" + IFCSrc + ", ... )");
|
|
131360
|
+
* utils.loadArraybuffer(src,
|
|
131361
|
+
* (arraybuffer) => {
|
|
131362
|
+
* ok(arraybuffer);
|
|
131363
|
+
* },
|
|
131364
|
+
* function (errMsg) {
|
|
131365
|
+
* error(errMsg);
|
|
131366
|
+
* });
|
|
131367
|
+
* }
|
|
131368
|
+
* }
|
|
131369
|
+
*
|
|
131370
|
+
* const ifcLoader2 = new IFCOpenShellLoaderPlugin(viewer, {
|
|
131371
|
+
* dataSource: new MyDataSource()
|
|
131372
|
+
* });
|
|
131373
|
+
*
|
|
131374
|
+
* const model5 = ifcLoader2.load({
|
|
131375
|
+
* id: "myModel5",
|
|
131376
|
+
* src: "../assets/models/ifc/Duplex.ifc"
|
|
131377
|
+
* });
|
|
131378
|
+
* ````
|
|
131379
|
+
*
|
|
131380
|
+
* ## Loading multiple copies of a model, without object ID clashes
|
|
131381
|
+
*
|
|
131382
|
+
* Sometimes we need to load two or more instances of the same model, without having clashes
|
|
131383
|
+
* between the IDs of the equivalent objects in the model instances.
|
|
131384
|
+
*
|
|
131385
|
+
* As shown in the example below, we do this by setting {@link IFCOpenShellLoaderPlugin#globalizeObjectIds} ````true```` before we load our models.
|
|
131386
|
+
*
|
|
131387
|
+
* ````javascript
|
|
131388
|
+
* ifcLoader.globalizeObjectIds = true;
|
|
131389
|
+
*
|
|
131390
|
+
* const model = ifcLoader.load({
|
|
131391
|
+
* id: "model1",
|
|
131392
|
+
* src: "../assets/models/ifc/Duplex.ifc"
|
|
131393
|
+
* });
|
|
131394
|
+
*
|
|
131395
|
+
* const model2 = ifcLoader.load({
|
|
131396
|
+
* id: "model2",
|
|
131397
|
+
* src: "../assets/models/ifc/Duplex.ifc"
|
|
131398
|
+
* });
|
|
131399
|
+
* ````
|
|
131400
|
+
*
|
|
131401
|
+
* For each {@link Entity} loaded by these two calls, {@link Entity#id} and {@link MetaObject#id} will get prefixed by
|
|
131402
|
+
* the ID of their model, in order to avoid ID clashes between the two models.
|
|
131403
|
+
*
|
|
131404
|
+
* An Entity belonging to the first model will get an ID like this:
|
|
131405
|
+
*
|
|
131406
|
+
* ````
|
|
131407
|
+
* myModel1#0BTBFw6f90Nfh9rP1dlXrb
|
|
131408
|
+
* ````
|
|
131409
|
+
*
|
|
131410
|
+
* The equivalent Entity in the second model will get an ID like this:
|
|
131411
|
+
*
|
|
131412
|
+
* ````
|
|
131413
|
+
* myModel2#0BTBFw6f90Nfh9rP1dlXrb
|
|
131414
|
+
* ````
|
|
131415
|
+
*
|
|
131416
|
+
* Now, to update the visibility of both of those Entities collectively, using {@link Scene#setObjectsVisible}, we can
|
|
131417
|
+
* supply just the IFC product ID part to that method:
|
|
131418
|
+
*
|
|
131419
|
+
* ````javascript
|
|
131420
|
+
* myViewer.scene.setObjectVisibilities("0BTBFw6f90Nfh9rP1dlXrb", true);
|
|
131421
|
+
* ````
|
|
131422
|
+
*
|
|
131423
|
+
* The method, along with {@link Scene#setObjectsXRayed}, {@link Scene#setObjectsHighlighted} etc, will internally expand
|
|
131424
|
+
* the given ID to refer to the instances of that Entity in both models.
|
|
131425
|
+
*
|
|
131426
|
+
* We can also, of course, reference each Entity directly, using its globalized ID:
|
|
131427
|
+
*
|
|
131428
|
+
* ````javascript
|
|
131429
|
+
* myViewer.scene.setObjectVisibilities("myModel1#0BTBFw6f90Nfh9rP1dlXrb", true);
|
|
131430
|
+
*````
|
|
131431
|
+
*
|
|
131432
|
+
* @class IFCOpenShellLoaderPlugin
|
|
131433
|
+
* @since 2.6.90
|
|
131434
|
+
*/
|
|
131435
|
+
class IFCOpenShellLoaderPlugin extends Plugin {
|
|
131436
|
+
|
|
131437
|
+
/**
|
|
131438
|
+
* @param {Viewer} viewer The {@link Viewer} that will own this plugin.
|
|
131439
|
+
* @param {Object} cfg Plugin configuration.
|
|
131440
|
+
* @param {String} [cfg.id="IFCOpenShellLoader"] Optional ID for this plugin instance.
|
|
131441
|
+
* @param {Object} [cfg.dataSource] Custom data source (defaults to {@link IFCOpenShellDefaultDataSource}).
|
|
131442
|
+
* @param {Object} cfg.ifcopenshell IfcOpenShell API object.
|
|
131443
|
+
* @param {Object} cfg.ifcopenshell_geom IfcOpenShell geometry API object.
|
|
131444
|
+
*/
|
|
131445
|
+
constructor(viewer, cfg) {
|
|
131446
|
+
|
|
131447
|
+
super("IFCOpenShellLoader", viewer, cfg);
|
|
131448
|
+
|
|
131449
|
+
if (!cfg) {
|
|
131450
|
+
throw new Error("IFCOpenShellLoaderPlugin: No configuration given");
|
|
131451
|
+
}
|
|
131452
|
+
|
|
131453
|
+
if (!cfg.ifcopenshell) {
|
|
131454
|
+
throw new Error("IFCOpenShellLoaderPlugin: No ifcopenshell given");
|
|
131455
|
+
}
|
|
131456
|
+
|
|
131457
|
+
if (!cfg.ifcopenshell_geom) {
|
|
131458
|
+
throw new Error("IFCOpenShellLoaderPlugin: No ifcopenshell_geom given");
|
|
131459
|
+
}
|
|
131460
|
+
|
|
131461
|
+
this.ifcopenshell = cfg.ifcopenshell;
|
|
131462
|
+
this.ifcopenshell_geom = cfg.ifcopenshell_geom;
|
|
131463
|
+
|
|
131464
|
+
this.dataSource = cfg.dataSource;
|
|
131465
|
+
}
|
|
131466
|
+
|
|
131467
|
+
/**
|
|
131468
|
+
* Sets a custom data source for IFC files.
|
|
131469
|
+
* @param value
|
|
131470
|
+
*/
|
|
131471
|
+
set dataSource(value) {
|
|
131472
|
+
this._dataSource = value || new IFCOpenShellDefaultDataSource();
|
|
131473
|
+
}
|
|
131474
|
+
|
|
131475
|
+
/**
|
|
131476
|
+
* Gets the data source for IFC files.
|
|
131477
|
+
* @returns {*|IFCOpenShellDefaultDataSource}
|
|
131478
|
+
*/
|
|
131479
|
+
get dataSource() {
|
|
131480
|
+
return this._dataSource;
|
|
131481
|
+
}
|
|
131482
|
+
|
|
131483
|
+
/**
|
|
131484
|
+
* Gets whether IFCOpenShellLoaderPlugin globalizes each {@link Entity#id} and {@link MetaObject#id} as it loads a model.
|
|
131485
|
+
*
|
|
131486
|
+
* Default value is ````false````.
|
|
131487
|
+
*
|
|
131488
|
+
* @type {Boolean}
|
|
131489
|
+
*/
|
|
131490
|
+
get globalizeObjectIds() {
|
|
131491
|
+
return this._globalizeObjectIds;
|
|
131492
|
+
}
|
|
131493
|
+
|
|
131494
|
+
/**
|
|
131495
|
+
* Sets whether IFCOpenShellLoaderPlugin globalizes each {@link Entity#id} and {@link MetaObject#id} as it loads a model.
|
|
131496
|
+
*
|
|
131497
|
+
* Set this ````true```` when you need to load multiple instances of the same model, to avoid ID clashes
|
|
131498
|
+
* between the objects in the different instances.
|
|
131499
|
+
*
|
|
131500
|
+
* When we load a model with this set ````true````, then each {@link Entity#id} and {@link MetaObject#id} will be
|
|
131501
|
+
* prefixed by the ID of the model, ie. ````<modelId>#<objectId>````.
|
|
131502
|
+
*
|
|
131503
|
+
* {@link Entity#originalSystemId} and {@link MetaObject#originalSystemId} will always hold the original, un-prefixed, ID values.
|
|
131504
|
+
*
|
|
131505
|
+
* Default value is ````false````.
|
|
131506
|
+
*
|
|
131507
|
+
* See the main {@link IFCOpenShellLoaderPlugin} class documentation for usage info.
|
|
131508
|
+
*
|
|
131509
|
+
* @type {Boolean}
|
|
131510
|
+
*/
|
|
131511
|
+
set globalizeObjectIds(value) {
|
|
131512
|
+
this._globalizeObjectIds = !!value;
|
|
131513
|
+
}
|
|
131514
|
+
|
|
131515
|
+
/**
|
|
131516
|
+
* Loads an IFC model from a file or text into the {@link Viewer}.
|
|
131517
|
+
*
|
|
131518
|
+
* @param {Object} params
|
|
131519
|
+
* @param {String} [params.id] Optional root Entity ID.
|
|
131520
|
+
* @param {String} [params.src] IFC file path (alternative to `text`).
|
|
131521
|
+
* @param {String} [params.text] IFC text (alternative to `src`).
|
|
131522
|
+
* @param {{String:Object}} [params.objectDefaults]
|
|
131523
|
+
* @param {String[]} [params.excludeTypes] Array of IFC types to exclude.
|
|
131524
|
+
* @param {Number[]} [params.origin=[0,0,0]] Optional World-coordinate origin to apply to the model.
|
|
131525
|
+
* @param {Number[]} [params.position=[0,0,0]] Optional position offset to apply to the model.
|
|
131526
|
+
* @param {Number[]} [params.rotation=[0,0,0]] Optional XYZ Euler rotation (degrees) to apply to the model.
|
|
131527
|
+
* @param {Boolean} [params.backfaces=true] Whether to render backfaces.
|
|
131528
|
+
* @param {Boolean} [params.dtxEnabled=true] Whether to enable data texture storage for geometry buffers.
|
|
131529
|
+
* @param {Boolean} [params.loadMetadata=true] Whether to load metadata.
|
|
131530
|
+
* @param {Boolean} [params.loadMetadataPropertySets=true] Whether to load property sets within the metadata. Only works when `loadMetadata` is true.
|
|
131531
|
+
* @param {Boolean} [params.edges=false] Whether to generate edge lines for the model.
|
|
131532
|
+
* @param {Boolean} [params.saoEnabled=false] Whether to enable SAO for the model.
|
|
131533
|
+
* @param {Boolean} [params.globalizeObjectIds=false] Whether to globalize each {@link Entity#id} and {@link MetaObject#id} as it loads the model.
|
|
131534
|
+
* @returns {Entity}
|
|
131535
|
+
*/
|
|
131536
|
+
async load(params = {}) {
|
|
131537
|
+
|
|
131538
|
+
let {
|
|
131539
|
+
id,
|
|
131540
|
+
backfaces = true,
|
|
131541
|
+
dtxEnabled = true,
|
|
131542
|
+
position,
|
|
131543
|
+
rotation,
|
|
131544
|
+
origin,
|
|
131545
|
+
loadMetadata,
|
|
131546
|
+
loadMetadataPropertySets,
|
|
131547
|
+
edges,
|
|
131548
|
+
saoEnabled,
|
|
131549
|
+
globalizeObjectIds,
|
|
131550
|
+
excludeTypes
|
|
131551
|
+
} = params;
|
|
131552
|
+
|
|
131553
|
+
if (id && this.viewer.scene.components[id]) {
|
|
131554
|
+
this.error(`Component with this ID already exists: ${id} - autogenerating SceneModel ID`);
|
|
131555
|
+
id = null;
|
|
131556
|
+
}
|
|
131557
|
+
|
|
131558
|
+
const sceneModel = new SceneModel(this.viewer.scene, {
|
|
131559
|
+
id,
|
|
131560
|
+
isModel: true,
|
|
131561
|
+
globalizeObjectIds,
|
|
131562
|
+
backfaces,
|
|
131563
|
+
dtxEnabled,
|
|
131564
|
+
position,
|
|
131565
|
+
rotation,
|
|
131566
|
+
origin,
|
|
131567
|
+
edges,
|
|
131568
|
+
saoEnabled
|
|
131569
|
+
});
|
|
131570
|
+
|
|
131571
|
+
const modelId = sceneModel.id;
|
|
131572
|
+
|
|
131573
|
+
if (!params.src && !params.text) {
|
|
131574
|
+
this.error("load() expected 'src' or 'text'");
|
|
131575
|
+
return sceneModel; // Return empty model
|
|
131576
|
+
}
|
|
131577
|
+
|
|
131578
|
+
const spinner = this.viewer.scene.canvas.spinner;
|
|
131579
|
+
spinner.processes++;
|
|
131580
|
+
|
|
131581
|
+
const loadIFC = (fileData) => {
|
|
131582
|
+
const ifc = this.ifcopenshell.file.from_string(fileData);
|
|
131583
|
+
const ctx = {
|
|
131584
|
+
loadMetadataPropertySets: (loadMetadataPropertySets !== false),
|
|
131585
|
+
globalizeObjectIds: globalizeObjectIds || this._globalizeObjectIds,
|
|
131586
|
+
geometryCache: new Map(),
|
|
131587
|
+
ifc,
|
|
131588
|
+
sceneModel
|
|
131589
|
+
};
|
|
131590
|
+
if (excludeTypes) {
|
|
131591
|
+
ctx.excludeTypes = excludeTypes;
|
|
131592
|
+
}
|
|
131593
|
+
this._loadIFCGeometry(ctx);
|
|
131594
|
+
if (loadMetadata !== false) {
|
|
131595
|
+
const metaModelData = this._loadIFCMetaModel(ctx, ifc);
|
|
131596
|
+
this.viewer.metaScene.createMetaModel(modelId, metaModelData);
|
|
131597
|
+
}
|
|
131598
|
+
this.viewer.scene.canvas.spinner.processes--;
|
|
131599
|
+
};
|
|
131600
|
+
|
|
131601
|
+
if (params.src) {
|
|
131602
|
+
this.viewer.scene.canvas.spinner.processes++;
|
|
131603
|
+
this._dataSource.getIFC(
|
|
131604
|
+
params.src,
|
|
131605
|
+
(fileData) => {
|
|
131606
|
+
loadIFC(fileData);
|
|
131607
|
+
this.viewer.scene.canvas.spinner.processes--;
|
|
131608
|
+
},
|
|
131609
|
+
(err) => {
|
|
131610
|
+
this.viewer.scene.canvas.spinner.processes--;
|
|
131611
|
+
this.error(err);
|
|
131612
|
+
}
|
|
131613
|
+
);
|
|
131614
|
+
} else {
|
|
131615
|
+
loadIFC(params.text);
|
|
131616
|
+
}
|
|
131617
|
+
|
|
131618
|
+
sceneModel.once("destroyed", () => {
|
|
131619
|
+
this.viewer.metaScene.destroyMetaModel(modelId);
|
|
131620
|
+
});
|
|
131621
|
+
|
|
131622
|
+
return sceneModel;
|
|
131623
|
+
}
|
|
131624
|
+
|
|
131625
|
+
_loadIFCGeometry(ctx) {
|
|
131626
|
+
const {ifc, sceneModel} = ctx;
|
|
131627
|
+
const {ifcopenshell_geom} = this;
|
|
131628
|
+
|
|
131629
|
+
const settings = ifcopenshell_geom.settings();
|
|
131630
|
+
settings.set(settings.WELD_VERTICES, false);
|
|
131631
|
+
|
|
131632
|
+
const iterator = ifcopenshell_geom.iterator.callKwargs({
|
|
131633
|
+
settings,
|
|
131634
|
+
file_or_filename: ifc,
|
|
131635
|
+
exclude: ctx.excludeTypes,
|
|
131636
|
+
geometry_library: "hybrid-cgal-simple-opencascade"
|
|
131637
|
+
});
|
|
131638
|
+
|
|
131639
|
+
if (iterator.initialize()) {
|
|
131640
|
+
do {
|
|
131641
|
+
const obj = iterator.get();
|
|
131642
|
+
if (obj) {
|
|
131643
|
+
const entity = ifc.by_id(obj.id);
|
|
131644
|
+
this._parseIFCEntity(ctx, obj, entity);
|
|
131645
|
+
}
|
|
131646
|
+
} while (iterator.next());
|
|
131647
|
+
}
|
|
131648
|
+
|
|
131649
|
+
sceneModel.finalize();
|
|
131650
|
+
|
|
131651
|
+
sceneModel.scene.once("tick", () => {
|
|
131652
|
+
if (!sceneModel.destroyed) {
|
|
131653
|
+
sceneModel.scene.fire("modelLoaded", sceneModel.id);
|
|
131654
|
+
sceneModel.fire("loaded", true, false);
|
|
131655
|
+
}
|
|
131656
|
+
});
|
|
131657
|
+
}
|
|
131658
|
+
|
|
131659
|
+
_parseIFCEntity(ctx, obj, ifcEntity) {
|
|
131660
|
+
const {sceneModel, geometryCache} = ctx;
|
|
131661
|
+
const geometry_id = obj.geometry.id;
|
|
131662
|
+
|
|
131663
|
+
const M = obj.transformation.data().components.toJs();
|
|
131664
|
+
const {origin, matrix} = extractRTCTransform(M);
|
|
131665
|
+
|
|
131666
|
+
if (!geometryCache.get(geometry_id)) {
|
|
131667
|
+
|
|
131668
|
+
const srcMaterials = obj.geometry.materials.toJs();
|
|
131669
|
+
const materials = srcMaterials.map((m) => ({
|
|
131670
|
+
diffuse: m.diffuse.components.toJs(),
|
|
131671
|
+
transparency: (m.transparency
|
|
131672
|
+
&& !isNaN(m.transparency)) ? m.transparency : 0.0,
|
|
131673
|
+
}));
|
|
131674
|
+
|
|
131675
|
+
const materialIds = new Int32Array(obj.geometry.material_ids.toJs());
|
|
131676
|
+
|
|
131677
|
+
// Build mapping: materialIndex -> [faceIdx...]
|
|
131678
|
+
const mapping = buildMaterialMapping(materialIds);
|
|
131679
|
+
|
|
131680
|
+
// Create sub-geometry per materialIndex, once
|
|
131681
|
+
const subGeoms = new Map();
|
|
131682
|
+
|
|
131683
|
+
for (const [matIndexStr, faceList] of Object.entries(mapping)) {
|
|
131684
|
+
|
|
131685
|
+
if (!faceList || faceList.length === 0) {
|
|
131686
|
+
continue;
|
|
131687
|
+
}
|
|
131688
|
+
|
|
131689
|
+
// xeokit auto-generates normals on the GPU side
|
|
131690
|
+
|
|
131691
|
+
const positions = new Float32Array(obj.geometry.verts.toJs());
|
|
131692
|
+
const edgeIndices = new Uint32Array(obj.geometry.edges.toJs());
|
|
131693
|
+
const faces = new Uint32Array(obj.geometry.faces.toJs());
|
|
131694
|
+
const matIndex = Number(matIndexStr);
|
|
131695
|
+
const indices = buildIndicesForFaces(faceList, faces);
|
|
131696
|
+
const sceneGeometryId = makeSubGeometryId(geometry_id, matIndex); // deterministic
|
|
131697
|
+
|
|
131698
|
+
sceneModel.createGeometry({
|
|
131699
|
+
id: sceneGeometryId,
|
|
131700
|
+
primitive: "triangles",
|
|
131701
|
+
positions,
|
|
131702
|
+
indices,
|
|
131703
|
+
edgeIndices
|
|
131704
|
+
});
|
|
131705
|
+
|
|
131706
|
+
subGeoms.set(matIndex, sceneGeometryId);
|
|
131707
|
+
}
|
|
131708
|
+
|
|
131709
|
+
geometryCache.set(geometry_id, {
|
|
131710
|
+
subGeoms,
|
|
131711
|
+
materials,
|
|
131712
|
+
// store mapping as Map<number, Uint32Array> to avoid recomputing
|
|
131713
|
+
mapping: new Map(Object.entries(mapping).map(
|
|
131714
|
+
([k, v]) => [Number(k), new Uint32Array(v)]
|
|
131715
|
+
))
|
|
131716
|
+
});
|
|
131717
|
+
}
|
|
131718
|
+
|
|
131719
|
+
// Reuse cached sub-geometries to create per-object meshes
|
|
131720
|
+
|
|
131721
|
+
const cached = geometryCache.get(geometry_id);
|
|
131722
|
+
const meshIds = [];
|
|
131723
|
+
|
|
131724
|
+
for (const [matIndex, sceneGeometryId] of cached.subGeoms.entries()) {
|
|
131725
|
+
const material = cached.materials[matIndex] || {diffuse: [0.6, 0.6, 0.6], transparency: 0.0};
|
|
131726
|
+
const meshId = generateUUID();
|
|
131727
|
+
const diffuse = material.diffuse;
|
|
131728
|
+
sceneModel.createMesh({
|
|
131729
|
+
id: meshId,
|
|
131730
|
+
geometryId: sceneGeometryId,
|
|
131731
|
+
origin,
|
|
131732
|
+
matrix,
|
|
131733
|
+
color: [diffuse[0], diffuse[1], diffuse[2]],
|
|
131734
|
+
opacity: 1.0 - material.transparency
|
|
131735
|
+
});
|
|
131736
|
+
meshIds.push(meshId);
|
|
131737
|
+
}
|
|
131738
|
+
|
|
131739
|
+
sceneModel.createEntity({
|
|
131740
|
+
id: ctx.globalizeObjectIds ? math.globalizeObjectId(ctx.sceneModel.id, ifcEntity.GlobalId) : ifcEntity.GlobalId,
|
|
131741
|
+
isObject: true,
|
|
131742
|
+
meshIds
|
|
131743
|
+
});
|
|
131744
|
+
}
|
|
131745
|
+
|
|
131746
|
+
_loadIFCMetaModel(ctx, ifc) {
|
|
131747
|
+
|
|
131748
|
+
const visited = new Set();
|
|
131749
|
+
const metaObjects = [];
|
|
131750
|
+
const propertySets = [];
|
|
131751
|
+
|
|
131752
|
+
// ---- helpers -----------------------------------------------------------
|
|
131753
|
+
const toStr = (v) => (v === undefined || v === null) ? "" : String(v);
|
|
131754
|
+
|
|
131755
|
+
function getGlobalId(entity) {
|
|
131756
|
+
try {
|
|
131757
|
+
return String(entity.GlobalId);
|
|
131758
|
+
} catch {
|
|
131759
|
+
return null;
|
|
131760
|
+
}
|
|
131761
|
+
}
|
|
131762
|
+
|
|
131763
|
+
function addNode(entity, parent) {
|
|
131764
|
+
const id = getGlobalId(entity);
|
|
131765
|
+
if (!id || visited.has(id)) return false;
|
|
131766
|
+
visited.add(id);
|
|
131767
|
+
const globalizeObjectIds = ctx.globalizeObjectIds;
|
|
131768
|
+
const modelId = ctx.sceneModel.id;
|
|
131769
|
+
const propertySetIds = [];
|
|
131770
|
+
if (ctx.loadMetadataPropertySets) {
|
|
131771
|
+
// Try all possible association fields
|
|
131772
|
+
const associationFields = ["HasAssociations", "IsDefinedBy", "IsDecomposedBy", "ContainsElements"];
|
|
131773
|
+
for (const field of associationFields) {
|
|
131774
|
+
const associations = entity[field];
|
|
131775
|
+
if (associations && associations.length > 0) {
|
|
131776
|
+
for (let j = 0; j < associations.length; j++) {
|
|
131777
|
+
const rel = associations.get(j);
|
|
131778
|
+
if (rel.is_a && rel.is_a() === "IfcRelDefinesByProperties") {
|
|
131779
|
+
const propSet = rel.RelatingPropertyDefinition;
|
|
131780
|
+
if (propSet && propSet.is_a) {
|
|
131781
|
+
// Accept both IfcPropertySet and IfcElementQuantity
|
|
131782
|
+
if (["IfcPropertySet", "IfcElementQuantity"].includes(propSet.is_a())) {
|
|
131783
|
+
const propSetId = propSet.GlobalId ? String(propSet.GlobalId) : null;
|
|
131784
|
+
const propSetName = propSet.Name ? String(propSet.Name) : "";
|
|
131785
|
+
const propSetType = propSet.is_a ? String(propSet.is_a()) : "";
|
|
131786
|
+
const properties = [];
|
|
131787
|
+
const props = propSet.HasProperties || propSet.Quantities;
|
|
131788
|
+
if (props && props.length > 0) {
|
|
131789
|
+
for (let k = 0; k < props.length; k++) {
|
|
131790
|
+
const p = props.get(k);
|
|
131791
|
+
const propName = p.Name ? String(p.Name) : "";
|
|
131792
|
+
let propValue = "";
|
|
131793
|
+
let propType = p.is_a ? String(p.is_a()) : "";
|
|
131794
|
+
if (p.is_a && p.is_a() === "IfcPropertySingleValue") {
|
|
131795
|
+
try {
|
|
131796
|
+
propValue = p.NominalValue ? String(p.NominalValue.wrappedValue) : "";
|
|
131797
|
+
} catch {
|
|
131798
|
+
propValue = "";
|
|
131799
|
+
}
|
|
131800
|
+
} else if (p.is_a && p.is_a() === "IfcPropertyEnumeratedValue") {
|
|
131801
|
+
try {
|
|
131802
|
+
const values = p.EnumerationValues;
|
|
131803
|
+
if (values && values.length > 0) {
|
|
131804
|
+
const arr = [];
|
|
131805
|
+
for (let vi = 0; vi < values.length; vi++) {
|
|
131806
|
+
arr.push(String(values.get(vi).wrappedValue));
|
|
131807
|
+
}
|
|
131808
|
+
propValue = arr.join(", ");
|
|
131809
|
+
}
|
|
131810
|
+
} catch {
|
|
131811
|
+
propValue = "";
|
|
131812
|
+
}
|
|
131813
|
+
} else if (p.is_a && p.is_a() === "IfcQuantityArea") {
|
|
131814
|
+
propValue = p.AreaValue ? String(p.AreaValue) : "";
|
|
131815
|
+
} else if (p.is_a && p.is_a() === "IfcQuantityLength") {
|
|
131816
|
+
propValue = p.LengthValue ? String(p.LengthValue) : "";
|
|
131817
|
+
} else if (p.is_a && p.is_a() === "IfcQuantityVolume") {
|
|
131818
|
+
propValue = p.VolumeValue ? String(p.VolumeValue) : "";
|
|
131819
|
+
} else {
|
|
131820
|
+
try {
|
|
131821
|
+
propValue = p.NominalValue ? String(p.NominalValue) : "";
|
|
131822
|
+
} catch {
|
|
131823
|
+
propValue = "";
|
|
131824
|
+
}
|
|
131825
|
+
}
|
|
131826
|
+
properties.push({
|
|
131827
|
+
name: propName,
|
|
131828
|
+
value: propValue,
|
|
131829
|
+
type: propType
|
|
131830
|
+
});
|
|
131831
|
+
p.destroy?.();
|
|
131832
|
+
}
|
|
131833
|
+
props.destroy?.();
|
|
131834
|
+
}
|
|
131835
|
+
propertySets.push({
|
|
131836
|
+
id: propSetId,
|
|
131837
|
+
// objectId: ctx.globalizeObjectIds ? math.globalizeObjectId(ctx.sceneModel.id, objectId) : objectId,
|
|
131838
|
+
name: propSetName,
|
|
131839
|
+
type: propSetType,
|
|
131840
|
+
properties
|
|
131841
|
+
});
|
|
131842
|
+
propertySetIds.push(propSetId);
|
|
131843
|
+
propSet.destroy?.();
|
|
131844
|
+
}
|
|
131845
|
+
}
|
|
131846
|
+
rel.destroy?.();
|
|
131847
|
+
}
|
|
131848
|
+
}
|
|
131849
|
+
associations.destroy?.();
|
|
131850
|
+
}
|
|
131851
|
+
}
|
|
131852
|
+
}
|
|
131853
|
+
metaObjects.push({
|
|
131854
|
+
id: globalizeObjectIds ? math.globalizeObjectId(modelId, id) : id,
|
|
131855
|
+
type: String(entity.is_a()),
|
|
131856
|
+
parent: parent
|
|
131857
|
+
? (globalizeObjectIds
|
|
131858
|
+
? math.globalizeObjectId(modelId, getGlobalId(parent))
|
|
131859
|
+
: getGlobalId(parent))
|
|
131860
|
+
: null,
|
|
131861
|
+
propertySetIds
|
|
131862
|
+
});
|
|
131863
|
+
return true;
|
|
131864
|
+
}
|
|
131865
|
+
|
|
131866
|
+
function walk(entity, parent = null) {
|
|
131867
|
+
if (!entity) return;
|
|
131868
|
+
|
|
131869
|
+
// add current node (skip children if already visited)
|
|
131870
|
+
if (!addNode(entity, parent)) {
|
|
131871
|
+
entity.destroy?.();
|
|
131872
|
+
return;
|
|
131873
|
+
}
|
|
131874
|
+
|
|
131875
|
+
// 1) Decomposition (IfcRelAggregates / IfcRelNests)
|
|
131876
|
+
const decos = entity.IsDecomposedBy;
|
|
131877
|
+
if (decos) {
|
|
131878
|
+
for (let i = 0; i < decos.length; i++) {
|
|
131879
|
+
const rel = decos.get(i);
|
|
131880
|
+
const children = rel.RelatedObjects;
|
|
131881
|
+
if (children) {
|
|
131882
|
+
for (let j = 0; j < children.length; j++) {
|
|
131883
|
+
const child = children.get(j);
|
|
131884
|
+
walk(child, entity);
|
|
131885
|
+
child.destroy?.();
|
|
131886
|
+
}
|
|
131887
|
+
children.destroy?.();
|
|
131888
|
+
}
|
|
131889
|
+
rel.destroy?.();
|
|
131890
|
+
}
|
|
131891
|
+
}
|
|
131892
|
+
|
|
131893
|
+
// 2) Spatial containment (IfcRelContainedInSpatialStructure)
|
|
131894
|
+
const contains = entity.ContainsElements;
|
|
131895
|
+
if (contains) {
|
|
131896
|
+
for (let i = 0; i < contains.length; i++) {
|
|
131897
|
+
const rel = contains.get(i);
|
|
131898
|
+
const elems = rel.RelatedElements;
|
|
131899
|
+
if (elems) {
|
|
131900
|
+
for (let j = 0; j < elems.length; j++) {
|
|
131901
|
+
const elem = elems.get(j);
|
|
131902
|
+
walk(elem, entity);
|
|
131903
|
+
elem.destroy?.();
|
|
131904
|
+
}
|
|
131905
|
+
elems.destroy?.();
|
|
131906
|
+
}
|
|
131907
|
+
rel.destroy?.();
|
|
131908
|
+
}
|
|
131909
|
+
}
|
|
131910
|
+
|
|
131911
|
+
entity.destroy?.();
|
|
131912
|
+
}
|
|
131913
|
+
|
|
131914
|
+
// ---- metadata extraction ----------------------------------------------
|
|
131915
|
+
let projectId = "";
|
|
131916
|
+
let author = "";
|
|
131917
|
+
let createdAt = ""; // ISO string
|
|
131918
|
+
let schema = "";
|
|
131919
|
+
let creatingApplication = "";
|
|
131920
|
+
|
|
131921
|
+
// Schema (primary)
|
|
131922
|
+
try {
|
|
131923
|
+
// ifcopenshell.file usually exposes a `schema` string property
|
|
131924
|
+
schema = toStr(ifc.schema);
|
|
131925
|
+
} catch {
|
|
131926
|
+
}
|
|
131927
|
+
if (!schema) {
|
|
131928
|
+
// Fallback to STEP header
|
|
131929
|
+
try {
|
|
131930
|
+
const ids = ifc.wrapped_data.header.file_schema.schema_identifiers;
|
|
131931
|
+
if (ids && ids.length > 0) schema = toStr(ids.get(0));
|
|
131932
|
+
} catch {
|
|
131933
|
+
}
|
|
131934
|
+
}
|
|
131935
|
+
|
|
131936
|
+
// Project (also gives us OwnerHistory on many files)
|
|
131937
|
+
const projects = ifc.by_type("IfcProject");
|
|
131938
|
+
if (projects && projects.length > 0) {
|
|
131939
|
+
const project = projects.get(0);
|
|
131940
|
+
projectId = toStr(project.GlobalId);
|
|
131941
|
+
|
|
131942
|
+
// OwnerHistory path (preferred when present)
|
|
131943
|
+
try {
|
|
131944
|
+
const oh = project.OwnerHistory; // deprecated in newer IFC4.x, but present in many files
|
|
131945
|
+
if (oh) {
|
|
131946
|
+
// Author: IfcPersonAndOrganization → ThePerson (GivenName/FamilyName) and TheOrganization.Name
|
|
131947
|
+
try {
|
|
131948
|
+
const user = oh.OwningUser;
|
|
131949
|
+
const person = user?.ThePerson;
|
|
131950
|
+
const org = user?.TheOrganization;
|
|
131951
|
+
const gn = person?.GivenName ? toStr(person.GivenName) : "";
|
|
131952
|
+
const fn = person?.FamilyName ? toStr(person.FamilyName) : "";
|
|
131953
|
+
const personName = (gn || fn) ? [gn, fn].filter(Boolean).join(" ") : "";
|
|
131954
|
+
const orgName = org?.Name ? toStr(org.Name) : "";
|
|
131955
|
+
author = [personName, orgName].filter(Boolean).join(" / ");
|
|
131956
|
+
} catch {
|
|
131957
|
+
}
|
|
131958
|
+
|
|
131959
|
+
// Creation time (UNIX seconds)
|
|
131960
|
+
try {
|
|
131961
|
+
const ts = oh?.CreationDate;
|
|
131962
|
+
if (typeof ts === "number" && isFinite(ts) && ts > 0) {
|
|
131963
|
+
createdAt = new Date(ts * 1000).toISOString();
|
|
131964
|
+
}
|
|
131965
|
+
} catch {
|
|
131966
|
+
}
|
|
131967
|
+
|
|
131968
|
+
// Creating application
|
|
131969
|
+
try {
|
|
131970
|
+
const app = oh?.OwningApplication;
|
|
131971
|
+
const appName =
|
|
131972
|
+
app?.ApplicationFullName ? toStr(app.ApplicationFullName) :
|
|
131973
|
+
app?.ApplicationIdentifier ? toStr(app.ApplicationIdentifier) : "";
|
|
131974
|
+
const appVer = app?.Version ? toStr(app.Version) : "";
|
|
131975
|
+
creatingApplication = [appName, appVer].filter(Boolean).join(" ");
|
|
131976
|
+
} catch {
|
|
131977
|
+
}
|
|
131978
|
+
}
|
|
131979
|
+
} catch {
|
|
131980
|
+
}
|
|
131981
|
+
|
|
131982
|
+
// Clean first project (we’ll traverse below with a fresh pointer anyway)
|
|
131983
|
+
project.destroy?.();
|
|
131984
|
+
}
|
|
131985
|
+
|
|
131986
|
+
// Fallbacks via STEP header if OwnerHistory wasn’t there / incomplete
|
|
131987
|
+
try {
|
|
131988
|
+
const fileName = ifc.wrapped_data.header.file_name;
|
|
131989
|
+
if (!author) {
|
|
131990
|
+
try {
|
|
131991
|
+
const authors = fileName.author;
|
|
131992
|
+
if (authors && authors.length > 0) {
|
|
131993
|
+
// `author` is a LIST in the STEP header; join if multiple
|
|
131994
|
+
const parts = [];
|
|
131995
|
+
for (let i = 0; i < authors.length; i++) parts.push(toStr(authors.get(i)));
|
|
131996
|
+
author = parts.filter(Boolean).join(", ");
|
|
131997
|
+
}
|
|
131998
|
+
} catch {
|
|
131999
|
+
}
|
|
132000
|
+
}
|
|
132001
|
+
if (!createdAt) {
|
|
132002
|
+
const ts = toStr(fileName.time_stamp); // already a string like "2023-08-10T12:34:56"
|
|
132003
|
+
if (ts) {
|
|
132004
|
+
// normalize to ISO if possible
|
|
132005
|
+
const maybe = new Date(ts);
|
|
132006
|
+
if (!isNaN(maybe.getTime())) createdAt = maybe.toISOString();
|
|
132007
|
+
}
|
|
132008
|
+
}
|
|
132009
|
+
if (!creatingApplication) {
|
|
132010
|
+
// STEP header carries "originating_system" and "preprocessor_version"
|
|
132011
|
+
const orig = toStr(fileName.originating_system);
|
|
132012
|
+
const prep = toStr(fileName.preprocessor_version);
|
|
132013
|
+
creatingApplication = [orig, prep].filter(Boolean).join(" / ");
|
|
132014
|
+
}
|
|
132015
|
+
} catch {
|
|
132016
|
+
}
|
|
132017
|
+
|
|
132018
|
+
// If createdAt still missing, sweep for earliest OwnerHistory timestamp across roots
|
|
132019
|
+
if (!createdAt) {
|
|
132020
|
+
try {
|
|
132021
|
+
let minTs = Infinity;
|
|
132022
|
+
const roots = ifc.by_type("IfcRoot");
|
|
132023
|
+
for (let i = 0; i < roots.length; i++) {
|
|
132024
|
+
const r = roots.get(i);
|
|
132025
|
+
const oh = r?.OwnerHistory;
|
|
132026
|
+
const ts = oh?.CreationDate;
|
|
132027
|
+
if (typeof ts === "number" && isFinite(ts) && ts > 0 && ts < minTs) {
|
|
132028
|
+
minTs = ts;
|
|
132029
|
+
}
|
|
132030
|
+
r.destroy?.();
|
|
132031
|
+
}
|
|
132032
|
+
roots.destroy?.();
|
|
132033
|
+
if (isFinite(minTs)) createdAt = new Date(minTs * 1000).toISOString();
|
|
132034
|
+
} catch {
|
|
132035
|
+
}
|
|
132036
|
+
}
|
|
132037
|
+
|
|
132038
|
+
// ---- hierarchy walk ----------------------------------------------------
|
|
132039
|
+
// Re-query projects since we destroyed the first pointer above
|
|
132040
|
+
const projects2 = ifc.by_type("IfcProject");
|
|
132041
|
+
for (let i = 0; i < projects2.length; i++) {
|
|
132042
|
+
const project = projects2.get(i);
|
|
132043
|
+
walk(project, null);
|
|
132044
|
+
project.destroy?.();
|
|
132045
|
+
}
|
|
132046
|
+
projects2.destroy?.();
|
|
132047
|
+
|
|
132048
|
+
return {
|
|
132049
|
+
id: "",
|
|
132050
|
+
projectId,
|
|
132051
|
+
author,
|
|
132052
|
+
createdAt,
|
|
132053
|
+
schema,
|
|
132054
|
+
creatingApplication,
|
|
132055
|
+
metaObjects,
|
|
132056
|
+
propertySets
|
|
132057
|
+
};
|
|
132058
|
+
}
|
|
132059
|
+
|
|
132060
|
+
/**
|
|
132061
|
+
* Destroys this IFCOpenShellLoaderPlugin instance.
|
|
132062
|
+
*/
|
|
132063
|
+
destroy() {
|
|
132064
|
+
super.destroy();
|
|
132065
|
+
}
|
|
132066
|
+
}
|
|
132067
|
+
|
|
132068
|
+
function makeSubGeometryId(geometry_id, matIndex) {
|
|
132069
|
+
return `${geometry_id}:${matIndex}#geom`;
|
|
132070
|
+
}
|
|
132071
|
+
|
|
132072
|
+
function buildMaterialMapping(materialIds) {
|
|
132073
|
+
const mapping = {};
|
|
132074
|
+
for (let faceIdx = 0; faceIdx < materialIds.length; faceIdx++) {
|
|
132075
|
+
const materialId = materialIds[faceIdx];
|
|
132076
|
+
if (materialId == null || materialId < 0) continue; // skip invalid / missing
|
|
132077
|
+
(mapping[materialId] ||= []).push(faceIdx);
|
|
132078
|
+
}
|
|
132079
|
+
return mapping;
|
|
132080
|
+
}
|
|
132081
|
+
|
|
132082
|
+
function buildIndicesForFaces(faceList, faceIndices) {
|
|
132083
|
+
const indices = new Uint32Array(faceList.length * 3);
|
|
132084
|
+
let k = 0;
|
|
132085
|
+
for (const faceIdx of faceList) {
|
|
132086
|
+
const base = faceIdx * 3;
|
|
132087
|
+
indices[k++] = faceIndices[base + 0];
|
|
132088
|
+
indices[k++] = faceIndices[base + 1];
|
|
132089
|
+
indices[k++] = faceIndices[base + 2];
|
|
132090
|
+
}
|
|
132091
|
+
return indices;
|
|
132092
|
+
}
|
|
132093
|
+
|
|
132094
|
+
function generateUUID() {
|
|
132095
|
+
return Math.random().toString(36).substr(2, 9);
|
|
132096
|
+
}
|
|
132097
|
+
|
|
132098
|
+
function extractRTCTransform(transform) {
|
|
132099
|
+
const matrix = flattenMatrixArray(transform);
|
|
132100
|
+
const origin = [];
|
|
132101
|
+
const worldOrigin = matrix.slice(12, 15); // translation xyz
|
|
132102
|
+
worldToRTCPositions(worldOrigin, worldOrigin, origin);
|
|
132103
|
+
matrix.set(worldOrigin, 12);
|
|
132104
|
+
return {origin, matrix};
|
|
132105
|
+
}
|
|
132106
|
+
|
|
132107
|
+
function flattenMatrixArray(m) {
|
|
132108
|
+
return new Float64Array([
|
|
132109
|
+
m[0][0], m[2][0], -m[1][0], m[3][0],
|
|
132110
|
+
m[0][1], m[2][1], -m[1][1], m[3][1],
|
|
132111
|
+
m[0][2], m[2][2], -m[1][2], m[3][2],
|
|
132112
|
+
m[0][3], m[2][3], -m[1][3], m[3][3]
|
|
132113
|
+
]);
|
|
132114
|
+
}
|
|
132115
|
+
|
|
131075
132116
|
/**
|
|
131076
132117
|
* Default data access strategy for {@link LASLoaderPlugin}.
|
|
131077
132118
|
*/
|
|
@@ -135111,4 +136152,4 @@ class ZoneTranslateTouchControl extends ZoneTranslateControl {
|
|
|
135111
136152
|
}
|
|
135112
136153
|
}
|
|
135113
136154
|
|
|
135114
|
-
export { AlphaFormat, AmbientLight, AngleMeasurementEditMouseControl, AngleMeasurementEditTouchControl, AngleMeasurementsControl, AngleMeasurementsMouseControl, AngleMeasurementsPlugin, AngleMeasurementsTouchControl, AnnotationsPlugin, AxisGizmoPlugin, BCFViewpointsPlugin, Bitmap, ByteType, CameraMemento, CameraPath, CameraPathAnimation, CityJSONLoaderPlugin, ClampToEdgeWrapping, Component, CompressedMediaType, Configs, ContextMenu, CubicBezierCurve, Curve, CxConverterIFCLoaderPlugin, DefaultLoadingManager, DepthFormat, DepthStencilFormat, DirLight, DistanceMeasurementEditControl, DistanceMeasurementEditMouseControl, DistanceMeasurementEditTouchControl, DistanceMeasurementsControl, DistanceMeasurementsMouseControl, DistanceMeasurementsPlugin, DistanceMeasurementsTouchControl, Dot3D, DotBIMDefaultDataSource, DotBIMLoaderPlugin, EdgeMaterial, EmphasisMaterial, FaceAlignedSectionPlanesPlugin, FastNavPlugin, FloatType, Fresnel, Frustum$1 as Frustum, FrustumPlane, GIFMediaType, GLTFDefaultDataSource, GLTFLoaderPlugin, HalfFloatType, ImagePlane, IntType, JPEGMediaType, KTX2TextureTranscoder, LASLoaderPlugin, Label3D, LambertMaterial, LightMap, LineSet, LinearEncoding, LinearFilter, LinearMipMapLinearFilter, LinearMipMapNearestFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, Loader, LoadingManager, LocaleService, LuminanceAlphaFormat, LuminanceFormat, Map$1 as Map, Marker, MarqueePicker, MarqueePickerMouseControl, Mesh, MeshSurfaceArea, MeshVolume, MetallicMaterial, MirroredRepeatWrapping, ModelMemento, NavCubePlugin, NearestFilter, NearestMipMapLinearFilter, NearestMipMapNearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, Node$1 as Node, OBJLoaderPlugin, ObjectsKdTree3, ObjectsMemento, PNGMediaType, Path, PerformanceModel, PhongMaterial, PickResult, Plugin, PointLight, PointerCircle, PointerLens, QuadraticBezierCurve, Queue, RGBAFormat, RGBAIntegerFormat, RGBA_ASTC_10x10_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_BPTC_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGBFormat, RGB_ETC1_Format, RGB_ETC2_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGB_S3TC_DXT1_Format, RGFormat, RGIntegerFormat, ReadableGeometry, RedFormat, RedIntegerFormat, ReflectionMap, RepeatWrapping, STLDefaultDataSource, STLLoaderPlugin, SceneModel, SceneModelMesh, SceneModelTransform, SectionPlane, SectionPlanesPlugin, ShortType, SpecularMaterial, SplineCurve, SpriteMarker, StoreyViewsPlugin, Texture, TextureTranscoder, TransformControl, TreeViewPlugin, UnsignedByteType, UnsignedInt248Type, UnsignedIntType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedShortType, VBOGeometry, ViewCullPlugin, Viewer, WebIFCLoaderPlugin, Wire3D, WorkerPool$1 as WorkerPool, XKTDefaultDataSource, XKTLoaderPlugin, XML3DLoaderPlugin, ZoneEditControl, ZoneEditMouseControl, ZoneEditTouchControl, ZoneTranslateControl, ZoneTranslateMouseControl, ZoneTranslateTouchControl, ZonesMouseControl, ZonesPlugin, ZonesPolysurfaceMouseControl, ZonesPolysurfaceTouchControl, ZonesTouchControl, activateDraggableDot, activateDraggableDots, addMousePressListener, addTouchPressListener, buildBoxGeometry, buildBoxLinesGeometry, buildBoxLinesGeometryFromAABB, buildCylinderGeometry, buildGridGeometry, buildLineGeometry, buildPlaneGeometry, buildPolylineGeometry, buildPolylineGeometryFromCurve, buildSphereGeometry, buildTorusGeometry, buildVectorTextGeometry, createCombinedTexture, createRTCViewMat, createSkyboxMesh, createSphereMapMesh, frustumIntersectsAABB3, getKTX2TextureTranscoder, isTriangleMeshSolid, load3DSGeometry, loadOBJGeometry, marker3D, math, meshSurfaceArea, meshVolume, os, sRGBEncoding, setFrustum, startPolygonCreate, stats, touchPointSelector, transformToNode, triangulateEarClipping, utils, wire3D, worldToRTCPos, worldToRTCPositions };
|
|
136155
|
+
export { AlphaFormat, AmbientLight, AngleMeasurementEditMouseControl, AngleMeasurementEditTouchControl, AngleMeasurementsControl, AngleMeasurementsMouseControl, AngleMeasurementsPlugin, AngleMeasurementsTouchControl, AnnotationsPlugin, AxisGizmoPlugin, BCFViewpointsPlugin, Bitmap, ByteType, CameraMemento, CameraPath, CameraPathAnimation, CityJSONLoaderPlugin, ClampToEdgeWrapping, Component, CompressedMediaType, Configs, ContextMenu, CubicBezierCurve, Curve, CxConverterIFCLoaderPlugin, DefaultLoadingManager, DepthFormat, DepthStencilFormat, DirLight, DistanceMeasurementEditControl, DistanceMeasurementEditMouseControl, DistanceMeasurementEditTouchControl, DistanceMeasurementsControl, DistanceMeasurementsMouseControl, DistanceMeasurementsPlugin, DistanceMeasurementsTouchControl, Dot3D, DotBIMDefaultDataSource, DotBIMLoaderPlugin, EdgeMaterial, EmphasisMaterial, FaceAlignedSectionPlanesPlugin, FastNavPlugin, FloatType, Fresnel, Frustum$1 as Frustum, FrustumPlane, GIFMediaType, GLTFDefaultDataSource, GLTFLoaderPlugin, HalfFloatType, IFCOpenShellDefaultDataSource, IFCOpenShellLoaderPlugin, ImagePlane, IntType, JPEGMediaType, KTX2TextureTranscoder, LASLoaderPlugin, Label3D, LambertMaterial, LightMap, LineSet, LinearEncoding, LinearFilter, LinearMipMapLinearFilter, LinearMipMapNearestFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, Loader, LoadingManager, LocaleService, LuminanceAlphaFormat, LuminanceFormat, Map$1 as Map, Marker, MarqueePicker, MarqueePickerMouseControl, Mesh, MeshSurfaceArea, MeshVolume, MetallicMaterial, MirroredRepeatWrapping, ModelMemento, NavCubePlugin, NearestFilter, NearestMipMapLinearFilter, NearestMipMapNearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, Node$1 as Node, OBJLoaderPlugin, ObjectsKdTree3, ObjectsMemento, PNGMediaType, Path, PerformanceModel, PhongMaterial, PickResult, Plugin, PointLight, PointerCircle, PointerLens, QuadraticBezierCurve, Queue, RGBAFormat, RGBAIntegerFormat, RGBA_ASTC_10x10_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_BPTC_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGBFormat, RGB_ETC1_Format, RGB_ETC2_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGB_S3TC_DXT1_Format, RGFormat, RGIntegerFormat, ReadableGeometry, RedFormat, RedIntegerFormat, ReflectionMap, RepeatWrapping, STLDefaultDataSource, STLLoaderPlugin, SceneModel, SceneModelMesh, SceneModelTransform, SectionPlane, SectionPlanesPlugin, ShortType, SpecularMaterial, SplineCurve, SpriteMarker, StoreyViewsPlugin, Texture, TextureTranscoder, TransformControl, TreeViewPlugin, UnsignedByteType, UnsignedInt248Type, UnsignedIntType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedShortType, VBOGeometry, ViewCullPlugin, Viewer, WebIFCLoaderPlugin, Wire3D, WorkerPool$1 as WorkerPool, XKTDefaultDataSource, XKTLoaderPlugin, XML3DLoaderPlugin, ZoneEditControl, ZoneEditMouseControl, ZoneEditTouchControl, ZoneTranslateControl, ZoneTranslateMouseControl, ZoneTranslateTouchControl, ZonesMouseControl, ZonesPlugin, ZonesPolysurfaceMouseControl, ZonesPolysurfaceTouchControl, ZonesTouchControl, activateDraggableDot, activateDraggableDots, addMousePressListener, addTouchPressListener, buildBoxGeometry, buildBoxLinesGeometry, buildBoxLinesGeometryFromAABB, buildCylinderGeometry, buildGridGeometry, buildLineGeometry, buildPlaneGeometry, buildPolylineGeometry, buildPolylineGeometryFromCurve, buildSphereGeometry, buildTorusGeometry, buildVectorTextGeometry, createCombinedTexture, createRTCViewMat, createSkyboxMesh, createSphereMapMesh, frustumIntersectsAABB3, getKTX2TextureTranscoder, isTriangleMeshSolid, load3DSGeometry, loadOBJGeometry, marker3D, math, meshSurfaceArea, meshVolume, os, sRGBEncoding, setFrustum, startPolygonCreate, stats, touchPointSelector, transformToNode, triangulateEarClipping, utils, wire3D, worldToRTCPos, worldToRTCPositions };
|