@xeokit/xeokit-sdk 2.6.95 → 2.6.97
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 +1303 -179
- package/dist/xeokit-sdk.es.js +1302 -180
- package/dist/xeokit-sdk.es5.js +762 -438
- 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/SceneModel.js +26 -23
- 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 +11 -11
- package/src/viewer/scene/model/layer/VBOLayer.js +7 -3
- package/src/viewer/scene/sectionCaps/SectionCaps.js +5 -4
- package/src/viewer/scene/webgl/Renderer.js +79 -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.cjs.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* xeokit-sdk v2.6.
|
|
3
|
-
* Commit:
|
|
4
|
-
* Built: 2025-
|
|
2
|
+
* xeokit-sdk v2.6.97
|
|
3
|
+
* Commit: fd8731b8272e4958c8231a6794a98a1874564a08
|
|
4
|
+
* Built: 2025-12-03T17:04:14.467Z
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
if (typeof window !== 'undefined') {
|
|
8
|
-
window.__XEOKIT__ = { version: '2.6.
|
|
8
|
+
window.__XEOKIT__ = { version: '2.6.97', commit: 'fd8731b8272e4958c8231a6794a98a1874564a08', built: '2025-12-03T17:04:14.467Z' };
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
@@ -1973,7 +1973,7 @@ const tempVec3a$j = new FloatArrayType(3);
|
|
|
1973
1973
|
|
|
1974
1974
|
const tempMat1 = new FloatArrayType(16);
|
|
1975
1975
|
const tempMat2 = new FloatArrayType(16);
|
|
1976
|
-
const tempVec4$
|
|
1976
|
+
const tempVec4$4 = new FloatArrayType(4);
|
|
1977
1977
|
|
|
1978
1978
|
|
|
1979
1979
|
/**
|
|
@@ -5188,7 +5188,7 @@ const math = {
|
|
|
5188
5188
|
},
|
|
5189
5189
|
|
|
5190
5190
|
quaternionToAngleAxis(q, angleAxis = math.vec4()) {
|
|
5191
|
-
q = math.normalizeQuaternion(q, tempVec4$
|
|
5191
|
+
q = math.normalizeQuaternion(q, tempVec4$4);
|
|
5192
5192
|
const q3 = q[3];
|
|
5193
5193
|
const angle = 2 * Math.acos(q3);
|
|
5194
5194
|
const s = Math.sqrt(1 - q3 * q3);
|
|
@@ -7437,9 +7437,12 @@ math.makeSectionPlaneSlicer = (function() {
|
|
|
7437
7437
|
math.vec3();
|
|
7438
7438
|
|
|
7439
7439
|
const triangle = [ math.vec3(), math.vec3(), math.vec3() ];
|
|
7440
|
+
const tmpIntersections = [ null, null, null ];
|
|
7441
|
+
const tmpPositions = [ null, null, null ];
|
|
7442
|
+
const tmpReferences = [ null, null, null ];
|
|
7440
7443
|
|
|
7441
|
-
const worldUp = [0, 1, 0];
|
|
7442
7444
|
const worldRight = [1, 0, 0];
|
|
7445
|
+
const worldUp = [0, 1, 0];
|
|
7443
7446
|
const worldForward = [0, 0, 1];
|
|
7444
7447
|
|
|
7445
7448
|
const sqDistVec3 = (function() {
|
|
@@ -7454,84 +7457,113 @@ math.makeSectionPlaneSlicer = (function() {
|
|
|
7454
7457
|
return ret;
|
|
7455
7458
|
};
|
|
7456
7459
|
|
|
7457
|
-
const setCoord2D = (p, dst) => { dst[0] = p.coord2Dx; dst[1] = p.coord2Dy; return dst; };
|
|
7458
|
-
|
|
7459
7460
|
const ccw = (a, b, c) => ((c[1] - a[1]) * (b[0] - a[0])) > ((b[1] - a[1]) * (c[0] - a[0]));
|
|
7460
7461
|
|
|
7461
|
-
|
|
7462
|
-
const
|
|
7463
|
-
const
|
|
7464
|
-
|
|
7465
|
-
return false;
|
|
7466
|
-
}
|
|
7462
|
+
return function(plane) {
|
|
7463
|
+
const planeU = math.vec3ApplyQuaternion(plane.quaternion, worldRight, math.vec3());
|
|
7464
|
+
const planeV = math.vec3ApplyQuaternion(plane.quaternion, worldUp, math.vec3());
|
|
7465
|
+
const planeN = math.vec3ApplyQuaternion(plane.quaternion, worldForward, math.vec3());
|
|
7467
7466
|
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
const [
|
|
7473
|
-
const
|
|
7467
|
+
return function(mesh, cfg = { }) {
|
|
7468
|
+
const planeToMesh = math.subVec3(mesh.origin, plane.pos, math.vec3());
|
|
7469
|
+
const planeDist = math.dotVec3(planeN, planeToMesh);
|
|
7470
|
+
|
|
7471
|
+
const unsortedSegment = [ ];
|
|
7472
|
+
const indexedPositions = [ null ]; // to never return 0 from addPosition, so its result can be used as a predicate
|
|
7473
|
+
const addPosition = (p, addUV) => {
|
|
7474
|
+
const idx = indexedPositions.length;
|
|
7475
|
+
const P = new Float64Array(5);
|
|
7476
|
+
P.set(p);
|
|
7477
|
+
if (addUV) {
|
|
7478
|
+
math.addVec3(planeToMesh, p, tempVec3a);
|
|
7479
|
+
P[3] = -math.dotVec3(planeU, tempVec3a);
|
|
7480
|
+
P[4] = math.dotVec3(planeV, tempVec3a);
|
|
7481
|
+
}
|
|
7482
|
+
indexedPositions.push(P);
|
|
7483
|
+
return idx;
|
|
7484
|
+
};
|
|
7474
7485
|
|
|
7475
|
-
|
|
7476
|
-
for (let i = 0, j = outerEndpoints.length - 1; i < outerEndpoints.length; j = i++) {
|
|
7477
|
-
const o0 = setCoord2D(outerEndpoints[i], tempVec2c);
|
|
7478
|
-
const o1 = setCoord2D(outerEndpoints[j], tempVec2d);
|
|
7486
|
+
const getCoord2D = (idx, dst) => { const P = indexedPositions[idx]; dst[0] = P[3]; dst[1] = P[4]; return dst; };
|
|
7479
7487
|
|
|
7480
|
-
|
|
7481
|
-
|
|
7488
|
+
const isLoopInside = (inner, outer) => {
|
|
7489
|
+
const bbI = inner.boundingBox;
|
|
7490
|
+
const bbO = outer.boundingBox;
|
|
7491
|
+
if ((bbI[0] < bbO[0]) || (bbI[1] < bbO[1]) || (bbI[2] > bbO[2]) || (bbI[3] > bbO[3])) {
|
|
7492
|
+
return false;
|
|
7482
7493
|
}
|
|
7483
7494
|
|
|
7484
|
-
const
|
|
7485
|
-
const
|
|
7495
|
+
const innerEndpoints = inner.endPoints;
|
|
7496
|
+
const outerEndpoints = outer.endPoints;
|
|
7497
|
+
for (let ii = 0, ij = innerEndpoints.length - 1; ii < innerEndpoints.length; ij = ii++) {
|
|
7498
|
+
const i0 = getCoord2D(innerEndpoints[ii], tempVec2a);
|
|
7499
|
+
const [i0x, i0y] = i0;
|
|
7500
|
+
const i1 = getCoord2D(innerEndpoints[ij], tempVec2b);
|
|
7486
7501
|
|
|
7487
|
-
|
|
7488
|
-
|
|
7502
|
+
let inside = false;
|
|
7503
|
+
for (let i = 0, j = outerEndpoints.length - 1; i < outerEndpoints.length; j = i++) {
|
|
7504
|
+
const o0 = getCoord2D(outerEndpoints[i], tempVec2c);
|
|
7505
|
+
const o1 = getCoord2D(outerEndpoints[j], tempVec2d);
|
|
7489
7506
|
|
|
7490
|
-
|
|
7491
|
-
|
|
7507
|
+
if ((ccw(i0, o0, o1) !== ccw(i1, o0, o1)) && (ccw(i0, i1, o0) !== ccw(i0, i1, o1))) {
|
|
7508
|
+
return false; // segments intersect
|
|
7509
|
+
}
|
|
7492
7510
|
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7511
|
+
const [o0x, o0y] = o0;
|
|
7512
|
+
const [o1x, o1y] = o1;
|
|
7513
|
+
|
|
7514
|
+
const dx = i0x - o0x;
|
|
7515
|
+
const dy = i0y - o0y;
|
|
7516
|
+
|
|
7517
|
+
const oDx = o1x - o0x;
|
|
7518
|
+
const oDy = o1y - o0y;
|
|
7497
7519
|
|
|
7498
|
-
|
|
7499
|
-
|
|
7520
|
+
const dot = (((oDx !== 0) || (oDy !== 0)) && (Math.abs(oDx * dy - oDy * dx) < 1e-10)) ? (dx * oDx + dy * oDy) : -1;
|
|
7521
|
+
if ((dot >= 0) && (dot <= (Math.pow(oDx, 2) + Math.pow(oDy, 2)))) {
|
|
7522
|
+
return false; // on edge
|
|
7523
|
+
}
|
|
7524
|
+
|
|
7525
|
+
if (((o0y > i0y) !== (o1y > i0y)) && (dx < (dy * oDx / oDy))) {
|
|
7526
|
+
inside = !inside;
|
|
7527
|
+
}
|
|
7528
|
+
}
|
|
7529
|
+
if (! inside) {
|
|
7530
|
+
return false;
|
|
7531
|
+
}
|
|
7500
7532
|
}
|
|
7501
|
-
}
|
|
7502
|
-
if (! inside) {
|
|
7503
|
-
return false;
|
|
7504
|
-
}
|
|
7505
|
-
}
|
|
7506
7533
|
|
|
7507
|
-
|
|
7508
|
-
|
|
7534
|
+
return true;
|
|
7535
|
+
};
|
|
7509
7536
|
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
const planeV = math.vec3ApplyQuaternion(planeRot, worldUp, math.vec3());
|
|
7513
|
-
const planeN = math.vec3ApplyQuaternion(planeRot, worldForward, math.vec3());
|
|
7537
|
+
const pos = { indices: [ ], normals: [ ] };
|
|
7538
|
+
const neg = (! cfg.onlyPosSliceWithUV) && { indices: [ ], normals: [ ] };
|
|
7514
7539
|
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7540
|
+
const appendOnSide = (dst, indices, normal) => {
|
|
7541
|
+
if (dst) {
|
|
7542
|
+
const p0 = indexedPositions[indices[0]];
|
|
7543
|
+
normal ||= math.normalizeVec3(math.cross3Vec3(math.subVec3(indexedPositions[indices[1]], p0, tempVec3a),
|
|
7544
|
+
math.subVec3(indexedPositions[indices[2]], p0, tempVec3b),
|
|
7545
|
+
tempVec3a),
|
|
7546
|
+
tempVec3a);
|
|
7518
7547
|
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7548
|
+
for (let i = 0; i < 3; ++i) {
|
|
7549
|
+
dst.indices.push(indices[i]);
|
|
7550
|
+
dst.normals.push(normal[0], normal[1], normal[2]);
|
|
7551
|
+
}
|
|
7552
|
+
}
|
|
7553
|
+
};
|
|
7522
7554
|
|
|
7523
7555
|
const setVertex = (i, dst) => {
|
|
7524
|
-
const idx =
|
|
7525
|
-
dst[0] =
|
|
7526
|
-
dst[1] =
|
|
7527
|
-
dst[2] =
|
|
7556
|
+
const idx = mesh.indices[i] * 3;
|
|
7557
|
+
dst[0] = mesh.positions[idx + 0];
|
|
7558
|
+
dst[1] = mesh.positions[idx + 1];
|
|
7559
|
+
dst[2] = mesh.positions[idx + 2];
|
|
7528
7560
|
return dst;
|
|
7529
7561
|
};
|
|
7530
7562
|
|
|
7531
|
-
for (let
|
|
7532
|
-
const p0 = setVertex(
|
|
7533
|
-
const p1 = setVertex(
|
|
7534
|
-
const p2 = setVertex(
|
|
7563
|
+
for (let faceIdx = 0; faceIdx < mesh.indices.length; faceIdx += 3) {
|
|
7564
|
+
const p0 = setVertex(faceIdx + 0, triangle[0]);
|
|
7565
|
+
const p1 = setVertex(faceIdx + 1, triangle[1]);
|
|
7566
|
+
const p2 = setVertex(faceIdx + 2, triangle[2]);
|
|
7535
7567
|
|
|
7536
7568
|
if (math.compareVec3(p0, p1) || math.compareVec3(p1, p2) || math.compareVec3(p2, p0)) {
|
|
7537
7569
|
continue; // skip degenerate triangle
|
|
@@ -7542,12 +7574,60 @@ math.makeSectionPlaneSlicer = (function() {
|
|
|
7542
7574
|
const d2 = planeDist + math.dotVec3(planeN, p2);
|
|
7543
7575
|
|
|
7544
7576
|
if ((d0 !== 0) || (d1 !== 0) || (d2 !== 0)) {
|
|
7545
|
-
const i0 = (d0 * d1 <= 0) && addPosition(math.lerpVec3(d0 / (d0 - d1), 0, 1, p0, p1, tempVec3a));
|
|
7546
|
-
const i1 = (d1 * d2 <= 0) && addPosition(math.lerpVec3(d1 / (d1 - d2), 0, 1, p1, p2, tempVec3a));
|
|
7547
|
-
const i2 = (d2 * d0 <= 0) && addPosition(math.lerpVec3(d2 / (d2 - d0), 0, 1, p2, p0, tempVec3a));
|
|
7577
|
+
const i0 = (d0 * d1 <= 0) && addPosition(math.lerpVec3(d0 / (d0 - d1), 0, 1, p0, p1, tempVec3a), true);
|
|
7578
|
+
const i1 = (d1 * d2 <= 0) && addPosition(math.lerpVec3(d1 / (d1 - d2), 0, 1, p1, p2, tempVec3a), true);
|
|
7579
|
+
const i2 = (d2 * d0 <= 0) && addPosition(math.lerpVec3(d2 / (d2 - d0), 0, 1, p2, p0, tempVec3a), true);
|
|
7580
|
+
|
|
7581
|
+
const p = (! cfg.onlyPosSliceWithUV) && tmpPositions;
|
|
7582
|
+
if (p) {
|
|
7583
|
+
p[0] = addPosition(p0);
|
|
7584
|
+
p[1] = addPosition(p1);
|
|
7585
|
+
p[2] = addPosition(p2);
|
|
7586
|
+
}
|
|
7548
7587
|
|
|
7549
7588
|
if (i0 ? (i1 || i2) : (i1 && i2)) { // triangle intersected by the section plane
|
|
7550
7589
|
unsortedSegment.push(i0 ? [ i0, i1 || i2 ] : [ i1, i2 ]);
|
|
7590
|
+
|
|
7591
|
+
if (p) {
|
|
7592
|
+
if ((d0 === 0) && (d1 === 0)) {
|
|
7593
|
+
appendOnSide((d2 > 0) ? pos : neg, p);
|
|
7594
|
+
} else if ((d0 === 0) && (d2 === 0)) {
|
|
7595
|
+
appendOnSide((d1 > 0) ? pos : neg, p);
|
|
7596
|
+
} else if ((d1 === 0) && (d2 === 0)) {
|
|
7597
|
+
appendOnSide((d0 > 0) ? pos : neg, p);
|
|
7598
|
+
} else {
|
|
7599
|
+
const isPos = (i0 ? d0 : d1) > 0;
|
|
7600
|
+
const dst0 = isPos ? pos : neg;
|
|
7601
|
+
const dst1 = isPos ? neg : pos;
|
|
7602
|
+
const i = tmpIntersections;
|
|
7603
|
+
i[0] = i0;
|
|
7604
|
+
i[1] = i1;
|
|
7605
|
+
i[2] = i2;
|
|
7606
|
+
const ref = tmpReferences;
|
|
7607
|
+
if (i0) {
|
|
7608
|
+
ref[0] = 0; ref[1] = 1; ref[2] = 2;
|
|
7609
|
+
} else {
|
|
7610
|
+
ref[0] = 1; ref[1] = 2; ref[2] = 0;
|
|
7611
|
+
}
|
|
7612
|
+
if (i[ref[1]]) {
|
|
7613
|
+
appendOnSide(dst0, [ p[ref[0]], i[ref[0]], p[ref[2]] ]);
|
|
7614
|
+
appendOnSide(dst0, [ i[ref[0]], i[ref[1]], p[ref[2]] ]);
|
|
7615
|
+
appendOnSide(dst1, [ i[ref[0]], p[ref[1]], i[ref[1]] ]);
|
|
7616
|
+
} else {
|
|
7617
|
+
appendOnSide(dst0, [ p[ref[0]], i[ref[0]], i[ref[2]] ]);
|
|
7618
|
+
appendOnSide(dst1, [ i[ref[0]], p[ref[1]], p[ref[2]] ]);
|
|
7619
|
+
appendOnSide(dst1, [ i[ref[0]], p[ref[2]], i[ref[2]] ]);
|
|
7620
|
+
}
|
|
7621
|
+
}
|
|
7622
|
+
}
|
|
7623
|
+
} else if (p) {
|
|
7624
|
+
if ((d0 >= 0) && (d1 >= 0) && (d2 >= 0)) {
|
|
7625
|
+
appendOnSide(pos, p);
|
|
7626
|
+
} else if ((d0 <= 0) && (d1 <= 0) && (d2 <= 0)) {
|
|
7627
|
+
appendOnSide(neg, p);
|
|
7628
|
+
} else {
|
|
7629
|
+
debugger;
|
|
7630
|
+
}
|
|
7551
7631
|
}
|
|
7552
7632
|
}
|
|
7553
7633
|
}
|
|
@@ -7593,37 +7673,30 @@ math.makeSectionPlaneSlicer = (function() {
|
|
|
7593
7673
|
}
|
|
7594
7674
|
|
|
7595
7675
|
const loops = endpointLoops.filter(endPoints => endPoints.length > 2).map((endPoints, idx) => {
|
|
7596
|
-
const planeEndpoints = endPoints.map(posIdx => {
|
|
7597
|
-
const P = math.addVec3(planeToMesh, indexedPositions[posIdx], tempVec3a);
|
|
7598
|
-
return { posIdx: posIdx, coord2Dx: math.dotVec3(planeU, P), coord2Dy: math.dotVec3(planeV, P) };
|
|
7599
|
-
});
|
|
7600
7676
|
let doubleArea = 0;
|
|
7601
7677
|
const aabb = math.collapseAABB2(math.AABB2());
|
|
7602
|
-
for (let i = 0; i <
|
|
7603
|
-
|
|
7604
|
-
tempVec2a[0] = p0.coord2Dx;
|
|
7605
|
-
tempVec2a[1] = p0.coord2Dy;
|
|
7678
|
+
for (let i = 0; i < endPoints.length; i++) {
|
|
7679
|
+
getCoord2D(endPoints[i], tempVec2a);
|
|
7606
7680
|
math.expandAABB2Point2(aabb, tempVec2a);
|
|
7607
|
-
|
|
7608
|
-
doubleArea += (
|
|
7681
|
+
getCoord2D(endPoints[(i + 1) % endPoints.length], tempVec2b);
|
|
7682
|
+
doubleArea += (tempVec2a[0] * tempVec2b[1] - tempVec2b[0] * tempVec2a[1]);
|
|
7609
7683
|
}
|
|
7610
7684
|
return {
|
|
7611
7685
|
boundingBox: aabb,
|
|
7612
7686
|
doubleArea: Math.abs(doubleArea),
|
|
7613
|
-
endPoints:
|
|
7687
|
+
endPoints: endPoints
|
|
7614
7688
|
};
|
|
7615
7689
|
}).sort((a, b) => b.doubleArea - a.doubleArea);
|
|
7616
7690
|
|
|
7617
|
-
const sliceGeometries = [ ];
|
|
7618
|
-
|
|
7619
7691
|
while (loops.length > 0) {
|
|
7620
7692
|
const vertices2D = [ ];
|
|
7621
7693
|
const vertices3D = [ ];
|
|
7622
7694
|
|
|
7623
7695
|
const appendLoopVertices = loop => loop.endPoints.forEach(endpoint2D => {
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7696
|
+
getCoord2D(endpoint2D, tempVec2a);
|
|
7697
|
+
vertices2D.push(tempVec2a[0], tempVec2a[1]);
|
|
7698
|
+
vertices3D.push(endpoint2D);
|
|
7699
|
+
});
|
|
7627
7700
|
|
|
7628
7701
|
const outerLoop = loops.shift();
|
|
7629
7702
|
appendLoopVertices(outerLoop);
|
|
@@ -7645,41 +7718,33 @@ math.makeSectionPlaneSlicer = (function() {
|
|
|
7645
7718
|
// Triangulate
|
|
7646
7719
|
const triangles = earcut(vertices2D, innerLoops.map(loop => loop.index));
|
|
7647
7720
|
|
|
7648
|
-
const positions = [ ];
|
|
7649
|
-
const normals = [ ];
|
|
7650
|
-
const uv = [ ];
|
|
7651
7721
|
for (let i = 0; i < triangles.length; i += 3) {
|
|
7652
|
-
const
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7722
|
+
const ti = tmpIntersections;
|
|
7723
|
+
ti[0] = vertices3D[triangles[i + 0]];
|
|
7724
|
+
ti[1] = vertices3D[triangles[i + 1]];
|
|
7725
|
+
ti[2] = vertices3D[triangles[i + 2]];
|
|
7726
|
+
const v0 = indexedPositions[ti[0]];
|
|
7727
|
+
math.subVec3(indexedPositions[ti[1]], v0, tempVec3b);
|
|
7728
|
+
math.subVec3(indexedPositions[ti[2]], v0, tempVec3c);
|
|
7657
7729
|
math.normalizeVec3(math.cross3Vec3(tempVec3b, tempVec3c, tempVec3c), tempVec3c);
|
|
7658
7730
|
const facedPositively = math.dotVec3(tempVec3c, planeN) <= 0;
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
for (let j = 0; j < 3; ++j) {
|
|
7663
|
-
const vIdx = triangles[i + (facedPositively ? j : (2 - j))];
|
|
7664
|
-
const v = indexedPositions[vertices3D[vIdx]];
|
|
7665
|
-
positions.push(v[0], v[1], v[2]);
|
|
7666
|
-
normals.push(tempVec3c[0], tempVec3c[1], tempVec3c[2]);
|
|
7667
|
-
const uvOff = 2 * vIdx;
|
|
7668
|
-
uv.push(-vertices2D[uvOff], vertices2D[uvOff + 1]);
|
|
7669
|
-
}
|
|
7670
|
-
}
|
|
7671
|
-
|
|
7672
|
-
if (positions.length > 0) {
|
|
7673
|
-
sliceGeometries.push({
|
|
7674
|
-
indices: iota(positions.length / 3),
|
|
7675
|
-
positions: positions,
|
|
7676
|
-
normals: normals,
|
|
7677
|
-
uv: uv
|
|
7678
|
-
});
|
|
7731
|
+
appendOnSide(facedPositively ? pos : neg, ti, tempVec3c);
|
|
7732
|
+
const tmp = ti[0]; ti[0] = ti[2]; ti[2] = tmp;
|
|
7733
|
+
appendOnSide(facedPositively ? neg : pos, ti, math.negateVec3(tempVec3c, tempVec3c));
|
|
7679
7734
|
}
|
|
7680
7735
|
}
|
|
7681
7736
|
|
|
7682
|
-
|
|
7737
|
+
const side = src => src && (src.indices.length > 0) && (function() {
|
|
7738
|
+
const positions = [ ];
|
|
7739
|
+
const uv = cfg.onlyPosSliceWithUV && [ ];
|
|
7740
|
+
src.indices.forEach(idx => {
|
|
7741
|
+
const P = indexedPositions[idx];
|
|
7742
|
+
positions.push(P[0], P[1], P[2]);
|
|
7743
|
+
uv && uv.push(P[3], P[4]);
|
|
7744
|
+
});
|
|
7745
|
+
return { indices: iota(src.indices.length), positions: positions, normals: src.normals, uv: uv };
|
|
7746
|
+
})();
|
|
7747
|
+
return { pos: side(pos), neg: side(neg) };
|
|
7683
7748
|
};
|
|
7684
7749
|
};
|
|
7685
7750
|
})();
|
|
@@ -20597,7 +20662,7 @@ const CompressedMediaType = 10003;
|
|
|
20597
20662
|
|
|
20598
20663
|
const ids$1 = new Map$1({});
|
|
20599
20664
|
const tempVec3a$f = math.vec3();
|
|
20600
|
-
const tempVec4$
|
|
20665
|
+
const tempVec4$3 = math.vec4();
|
|
20601
20666
|
const TEXTURE_DECODE_FUNCS = { [sRGBEncoding]: "sRGBToLinear" };
|
|
20602
20667
|
|
|
20603
20668
|
const iota$3 = function(n) {
|
|
@@ -21017,11 +21082,11 @@ const createLightSetup = function(programVariables, lightsState) {
|
|
|
21017
21082
|
const lightUniforms = {
|
|
21018
21083
|
color: programVariables.createUniform("vec4", `lightColor${i}`, (set) => {
|
|
21019
21084
|
const light = lights[i]; // in case it changed
|
|
21020
|
-
tempVec4$
|
|
21021
|
-
tempVec4$
|
|
21022
|
-
tempVec4$
|
|
21023
|
-
tempVec4$
|
|
21024
|
-
set(tempVec4$
|
|
21085
|
+
tempVec4$3[0] = light.color[0];
|
|
21086
|
+
tempVec4$3[1] = light.color[1];
|
|
21087
|
+
tempVec4$3[2] = light.color[2];
|
|
21088
|
+
tempVec4$3[3] = light.intensity;
|
|
21089
|
+
set(tempVec4$3);
|
|
21025
21090
|
}),
|
|
21026
21091
|
position: programVariables.createUniform("vec3", `lightPos${i}`, (set) => set(lights[i].pos)),
|
|
21027
21092
|
direction: programVariables.createUniform("vec3", `lightDir${i}`, (set) => set(lights[i].dir)),
|
|
@@ -21203,7 +21268,7 @@ const makeInputSetters = function(gl, handle) {
|
|
|
21203
21268
|
};
|
|
21204
21269
|
};
|
|
21205
21270
|
|
|
21206
|
-
const tempVec4$
|
|
21271
|
+
const tempVec4$2 = math.vec4();
|
|
21207
21272
|
|
|
21208
21273
|
const DrawShaderSource = function(meshDrawHash, programVariables, geometry, material, scene) {
|
|
21209
21274
|
const materialState = material._state;
|
|
@@ -21294,10 +21359,10 @@ const DrawShaderSource = function(meshDrawHash, programVariables, geometry, mate
|
|
|
21294
21359
|
const materialAlphaModeCutoff = setupUniform("materialAlphaModeCutoff", "vec4", mtl => {
|
|
21295
21360
|
const alpha = mtl.alpha;
|
|
21296
21361
|
if ((alpha !== undefined) && (alpha !== null)) {
|
|
21297
|
-
tempVec4$
|
|
21298
|
-
tempVec4$
|
|
21299
|
-
tempVec4$
|
|
21300
|
-
return tempVec4$
|
|
21362
|
+
tempVec4$2[0] = alpha;
|
|
21363
|
+
tempVec4$2[1] = (mtl.alphaMode === 1 ? 1 : 0);
|
|
21364
|
+
tempVec4$2[2] = mtl.alphaCutoff;
|
|
21365
|
+
return tempVec4$2;
|
|
21301
21366
|
} else {
|
|
21302
21367
|
return null;
|
|
21303
21368
|
}
|
|
@@ -31331,6 +31396,8 @@ class SceneModelMesh {
|
|
|
31331
31396
|
*/
|
|
31332
31397
|
_destroy() {
|
|
31333
31398
|
this.model.scene._renderer.putPickID(this.pickId);
|
|
31399
|
+
|
|
31400
|
+
this.layer = null;
|
|
31334
31401
|
}
|
|
31335
31402
|
}
|
|
31336
31403
|
|
|
@@ -31806,7 +31873,7 @@ const SnapProgram = function(programVariables, geometry, isSnapInit, isPoints) {
|
|
|
31806
31873
|
|
|
31807
31874
|
const tempVec2 = math.vec2();
|
|
31808
31875
|
const tempVec3$6 = math.vec3();
|
|
31809
|
-
const tempVec4 = math.vec4();
|
|
31876
|
+
const tempVec4$1 = math.vec4();
|
|
31810
31877
|
const tempMat4$2 = math.mat4();
|
|
31811
31878
|
const vec3zero = math.vec3([0,0,0]);
|
|
31812
31879
|
|
|
@@ -31943,10 +32010,7 @@ const getRenderers = (function() {
|
|
|
31943
32010
|
appendFragmentOutputs: programSetup.appendFragmentOutputs,
|
|
31944
32011
|
cleanerEdges: programSetup.cleanerEdges,
|
|
31945
32012
|
clipPos: clipPos,
|
|
31946
|
-
clippableTest:
|
|
31947
|
-
const vClippable = programVariables.createVarying("float", "vClippable", () => `${attributes.clippable} ? 1.0 : 0.0`, "flat");
|
|
31948
|
-
return () => `${vClippable} > 0.0`;
|
|
31949
|
-
})(),
|
|
32013
|
+
clippableTest: renderingAttributes.clippableTest,
|
|
31950
32014
|
clippingCaps: programSetup.clippingCaps,
|
|
31951
32015
|
crossSections: scene.crossSections,
|
|
31952
32016
|
discardPoints: setupPoints && pointsMaterial.roundPoints,
|
|
@@ -32292,6 +32356,15 @@ class Layer {
|
|
|
32292
32356
|
|
|
32293
32357
|
aabbChanged() { this._aabbDirty = true; }
|
|
32294
32358
|
|
|
32359
|
+
getAABB() {
|
|
32360
|
+
if (this._aabbDirty) { // Per-layer AABB for best RTC accuracy
|
|
32361
|
+
math.collapseAABB3(this._aabb);
|
|
32362
|
+
this._meshes.forEach(m => math.expandAABB3(this._aabb, m.aabb));
|
|
32363
|
+
this._aabbDirty = false;
|
|
32364
|
+
}
|
|
32365
|
+
return this._aabb;
|
|
32366
|
+
}
|
|
32367
|
+
|
|
32295
32368
|
__drawLayer(renderFlags, frameCtx, renderer, pass) {
|
|
32296
32369
|
if ((this._countsByFlag[ENTITY_FLAGS.CULLED].count < this._portions.length) && (this._countsByFlag[ENTITY_FLAGS.VISIBLE].count > 0)) {
|
|
32297
32370
|
const backfacePasses = (this.primitive !== "points") && (this.primitive !== "lines") && [
|
|
@@ -32364,7 +32437,7 @@ class Layer {
|
|
|
32364
32437
|
// ---------------------- SILHOUETTE RENDERING -----------------------------------
|
|
32365
32438
|
|
|
32366
32439
|
__drawSilhouette(renderFlags, frameCtx, material, renderPass) {
|
|
32367
|
-
frameCtx.programColor = this.__setVec4FromMaterialColorAlpha(material.fillColor, material.fillAlpha, tempVec4);
|
|
32440
|
+
frameCtx.programColor = this.__setVec4FromMaterialColorAlpha(material.fillColor, material.fillAlpha, tempVec4$1);
|
|
32368
32441
|
this.__drawLayer(renderFlags, frameCtx, this._renderers.silhouetteRenderer, renderPass);
|
|
32369
32442
|
}
|
|
32370
32443
|
|
|
@@ -32410,7 +32483,7 @@ class Layer {
|
|
|
32410
32483
|
__drawUniformEdges(renderFlags, frameCtx, material, renderPass) {
|
|
32411
32484
|
const renderer = this._renderers.edgesRenderers && this._renderers.edgesRenderers.uniform;
|
|
32412
32485
|
if (renderer) {
|
|
32413
|
-
frameCtx.programColor = this.__setVec4FromMaterialColorAlpha(material.edgeColor, material.edgeAlpha, tempVec4);
|
|
32486
|
+
frameCtx.programColor = this.__setVec4FromMaterialColorAlpha(material.edgeColor, material.edgeAlpha, tempVec4$1);
|
|
32414
32487
|
this.__drawLayer(renderFlags, frameCtx, renderer, renderPass);
|
|
32415
32488
|
}
|
|
32416
32489
|
}
|
|
@@ -32457,13 +32530,7 @@ class Layer {
|
|
|
32457
32530
|
drawSnap(renderFlags, frameCtx, isSnapInit) {
|
|
32458
32531
|
frameCtx.snapPickOrigin = [0, 0, 0];
|
|
32459
32532
|
|
|
32460
|
-
|
|
32461
|
-
math.collapseAABB3(this._aabb);
|
|
32462
|
-
this._meshes.forEach(m => math.expandAABB3(this._aabb, m.aabb));
|
|
32463
|
-
this._aabbDirty = false;
|
|
32464
|
-
}
|
|
32465
|
-
|
|
32466
|
-
const aabb = this._aabb;
|
|
32533
|
+
const aabb = this.getAABB();
|
|
32467
32534
|
frameCtx.snapPickCoordinateScale = math.mulVec3Scalar(
|
|
32468
32535
|
safeInvVec3([ aabb[3] - aabb[0], aabb[4] - aabb[1], aabb[5] - aabb[2] ]),
|
|
32469
32536
|
math.MAX_INT);
|
|
@@ -33503,9 +33570,13 @@ const makeDTXRenderingAttributes = function(programVariables, isTriangle) {
|
|
|
33503
33570
|
const colorsAndFlags = (offset) => perObjColsFlags(`ivec2(objectIndexCoords.x*8+${offset}, objectIndexCoords.y)`);
|
|
33504
33571
|
|
|
33505
33572
|
return {
|
|
33573
|
+
clippableTest: (function() {
|
|
33574
|
+
const vClippable = programVariables.createVarying("uint", "vClippable", () => "flags2.r", "flat");
|
|
33575
|
+
return () => `${vClippable} > 0u`;
|
|
33576
|
+
})(),
|
|
33577
|
+
|
|
33506
33578
|
geometryParameters: {
|
|
33507
33579
|
attributes: {
|
|
33508
|
-
clippable: "(flags2.r > 0u)",
|
|
33509
33580
|
color: colorA,
|
|
33510
33581
|
flags: iota$2(4).map(i => `int(flags[${i}])`),
|
|
33511
33582
|
metallicRoughness: null,
|
|
@@ -33642,9 +33713,9 @@ function quantizePositions(positions, aabb, positionsDecodeMatrix) { // http://c
|
|
|
33642
33713
|
const xmin = aabb[0];
|
|
33643
33714
|
const ymin = aabb[1];
|
|
33644
33715
|
const zmin = aabb[2];
|
|
33645
|
-
const xwid = aabb[3] - xmin;
|
|
33646
|
-
const ywid = aabb[4] - ymin;
|
|
33647
|
-
const zwid = aabb[5] - zmin;
|
|
33716
|
+
const xwid = (aabb[3] - xmin) || 1;
|
|
33717
|
+
const ywid = (aabb[4] - ymin) || 1;
|
|
33718
|
+
const zwid = (aabb[5] - zmin) || 1;
|
|
33648
33719
|
const maxInt = 65525;
|
|
33649
33720
|
const xMultiplier = maxInt / xwid;
|
|
33650
33721
|
const yMultiplier = maxInt / ywid;
|
|
@@ -33673,9 +33744,9 @@ function createPositionsDecodeMatrix(aabb, positionsDecodeMatrix) { // http://cg
|
|
|
33673
33744
|
const xmin = aabb[0];
|
|
33674
33745
|
const ymin = aabb[1];
|
|
33675
33746
|
const zmin = aabb[2];
|
|
33676
|
-
const xwid = aabb[3] - xmin;
|
|
33677
|
-
const ywid = aabb[4] - ymin;
|
|
33678
|
-
const zwid = aabb[5] - zmin;
|
|
33747
|
+
const xwid = (aabb[3] - xmin) || 1;
|
|
33748
|
+
const ywid = (aabb[4] - ymin) || 1;
|
|
33749
|
+
const zwid = (aabb[5] - zmin) || 1;
|
|
33679
33750
|
const maxInt = 65525;
|
|
33680
33751
|
math.identityMat4(translate$1);
|
|
33681
33752
|
math.translationMat4v(aabb, translate$1);
|
|
@@ -34663,8 +34734,8 @@ class VBOLayer extends Layer {
|
|
|
34663
34734
|
} else { // triangles
|
|
34664
34735
|
if (subGeometry && subGeometry.vertices) {
|
|
34665
34736
|
return drawPoints;
|
|
34666
|
-
} else if (subGeometry
|
|
34667
|
-
return elementsDrawer(gl.LINES, edgeIndicesBuf);
|
|
34737
|
+
} else if (subGeometry) {
|
|
34738
|
+
return edgeIndicesBuf ? elementsDrawer(gl.LINES, edgeIndicesBuf) : (() => { });
|
|
34668
34739
|
} else {
|
|
34669
34740
|
return elementsDrawer(gl.TRIANGLES, indicesBuf);
|
|
34670
34741
|
}
|
|
@@ -34746,9 +34817,13 @@ const makeVBORenderingAttributes = function(programVariables, instancing, entity
|
|
|
34746
34817
|
return {
|
|
34747
34818
|
dontCullOnAlphaZero: true,
|
|
34748
34819
|
|
|
34820
|
+
clippableTest: (function() {
|
|
34821
|
+
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
|
|
34822
|
+
return () => `${vClippable} != 0.0`;
|
|
34823
|
+
})(),
|
|
34824
|
+
|
|
34749
34825
|
geometryParameters: {
|
|
34750
34826
|
attributes: {
|
|
34751
|
-
clippable: `((int(${attributes.flags}) >> 16 & 0xF) == 1)`,
|
|
34752
34827
|
color: attributes.color,
|
|
34753
34828
|
flags: iota$1(4).map(i => ({ toString: () => `(int(${attributes.flags}) >> ${i * 4} & 0xF)` })),
|
|
34754
34829
|
metallicRoughness: attributes.metallicRoughness,
|
|
@@ -40900,34 +40975,37 @@ class SceneModel extends Component {
|
|
|
40900
40975
|
/**
|
|
40901
40976
|
* @private
|
|
40902
40977
|
*/
|
|
40903
|
-
_withEachVisibleLayer(testNumVisibleLayerPortions, cb) {
|
|
40978
|
+
_withEachVisibleLayer(frameCtx, testNumVisibleLayerPortions, cb) {
|
|
40904
40979
|
if (testNumVisibleLayerPortions && (this.numVisibleLayerPortions === 0)) {
|
|
40905
40980
|
return;
|
|
40906
40981
|
}
|
|
40982
|
+
const testLayerCull = frameCtx.testAABB;
|
|
40907
40983
|
const renderFlags = this.renderFlags;
|
|
40908
40984
|
for (let i = 0, len = renderFlags.visibleLayers.length; i < len; i++) {
|
|
40909
|
-
const
|
|
40910
|
-
|
|
40911
|
-
|
|
40912
|
-
|
|
40913
|
-
|
|
40914
|
-
|
|
40915
|
-
|
|
40916
|
-
|
|
40917
|
-
|
|
40918
|
-
|
|
40919
|
-
|
|
40920
|
-
|
|
40921
|
-
|
|
40922
|
-
|
|
40923
|
-
|
|
40924
|
-
|
|
40925
|
-
|
|
40926
|
-
|
|
40927
|
-
|
|
40928
|
-
|
|
40929
|
-
|
|
40930
|
-
|
|
40985
|
+
const layer = this.layerList[renderFlags.visibleLayers[i]];
|
|
40986
|
+
if ((! testLayerCull) || testLayerCull(layer.getAABB())) {
|
|
40987
|
+
cb(layer);
|
|
40988
|
+
}
|
|
40989
|
+
}
|
|
40990
|
+
}
|
|
40991
|
+
|
|
40992
|
+
drawColorOpaque (frameCtx) { this._withEachVisibleLayer(frameCtx, false, layer => layer.drawColorOpaque (this.renderFlags, frameCtx)); }
|
|
40993
|
+
drawColorTransparent (frameCtx) { this._withEachVisibleLayer(frameCtx, false, layer => layer.drawColorTransparent (this.renderFlags, frameCtx)); }
|
|
40994
|
+
drawDepth (frameCtx) { this._withEachVisibleLayer(frameCtx, false, layer => layer.drawDepth (this.renderFlags, frameCtx)); } // Dedicated to SAO because it skips transparent objects
|
|
40995
|
+
drawSilhouetteXRayed (frameCtx) { this._withEachVisibleLayer(frameCtx, false, layer => layer.drawSilhouetteXRayed (this.renderFlags, frameCtx)); }
|
|
40996
|
+
drawSilhouetteHighlighted(frameCtx) { this._withEachVisibleLayer(frameCtx, false, layer => layer.drawSilhouetteHighlighted(this.renderFlags, frameCtx)); }
|
|
40997
|
+
drawSilhouetteSelected (frameCtx) { this._withEachVisibleLayer(frameCtx, false, layer => layer.drawSilhouetteSelected (this.renderFlags, frameCtx)); }
|
|
40998
|
+
drawEdgesColorOpaque (frameCtx) { this._withEachVisibleLayer(frameCtx, false, layer => layer.drawEdgesColorOpaque (this.renderFlags, frameCtx)); }
|
|
40999
|
+
drawEdgesColorTransparent(frameCtx) { this._withEachVisibleLayer(frameCtx, false, layer => layer.drawEdgesColorTransparent(this.renderFlags, frameCtx)); }
|
|
41000
|
+
drawEdgesXRayed (frameCtx) { this._withEachVisibleLayer(frameCtx, false, layer => layer.drawEdgesXRayed (this.renderFlags, frameCtx)); }
|
|
41001
|
+
drawEdgesHighlighted (frameCtx) { this._withEachVisibleLayer(frameCtx, false, layer => layer.drawEdgesHighlighted (this.renderFlags, frameCtx)); }
|
|
41002
|
+
drawEdgesSelected (frameCtx) { this._withEachVisibleLayer(frameCtx, false, layer => layer.drawEdgesSelected (this.renderFlags, frameCtx)); }
|
|
41003
|
+
drawOcclusion (frameCtx) { this._withEachVisibleLayer(frameCtx, true, layer => layer.drawOcclusion (this.renderFlags, frameCtx)); }
|
|
41004
|
+
drawShadow (frameCtx) { this._withEachVisibleLayer(frameCtx, true, layer => layer.drawShadow (this.renderFlags, frameCtx)); }
|
|
41005
|
+
drawPickMesh (frameCtx) { this._withEachVisibleLayer(frameCtx, true, layer => layer.drawPickMesh (this.renderFlags, frameCtx)); }
|
|
41006
|
+
drawPickDepths (frameCtx) { this._withEachVisibleLayer(frameCtx, true, layer => layer.drawPickDepths (this.renderFlags, frameCtx)); }
|
|
41007
|
+
drawPickNormals (frameCtx) { this._withEachVisibleLayer(frameCtx, true, layer => layer.drawPickNormals (this.renderFlags, frameCtx)); }
|
|
41008
|
+
_drawSnap (frameCtx, isSnapInit) { this._withEachVisibleLayer(frameCtx, true, layer => layer.drawSnap (this.renderFlags, frameCtx, isSnapInit)); }
|
|
40931
41009
|
|
|
40932
41010
|
drawSnapInit(frameCtx) { this._drawSnap(frameCtx, true ); }
|
|
40933
41011
|
drawSnap (frameCtx) { this._drawSnap(frameCtx, false); }
|
|
@@ -46969,6 +47047,9 @@ const vec3_0 = math.vec3([0,0,0]);
|
|
|
46969
47047
|
|
|
46970
47048
|
const iota = (n) => { const ret = [ ]; for (let i = 0; i < n; ++i) ret.push(i); return ret; };
|
|
46971
47049
|
|
|
47050
|
+
const tempPlanes = iota(6).map(() => math.vec4());
|
|
47051
|
+
const tempVec4 = math.vec4();
|
|
47052
|
+
|
|
46972
47053
|
const bitShiftScreenZ = math.vec4([1.0 / (256.0 * 256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0]);
|
|
46973
47054
|
|
|
46974
47055
|
const pixelToInt = pix => pix[0] + (pix[1] << 8) + (pix[2] << 16) + (pix[3] << 24);
|
|
@@ -46978,6 +47059,66 @@ const toWorldPos = (p, origin, scale) => math.vec3([ p[0] * scale[0] + origin
|
|
|
46978
47059
|
p[1] * scale[1] + origin[1],
|
|
46979
47060
|
p[2] * scale[2] + origin[2] ]);
|
|
46980
47061
|
|
|
47062
|
+
const makeFrustumAABBIntersectionTest = function(camera) {
|
|
47063
|
+
const m = math.mat4();
|
|
47064
|
+
math.mulMat4(camera.projMatrix, camera.viewMatrix, m);
|
|
47065
|
+
|
|
47066
|
+
for (let i = 0; i < 3; ++i) {
|
|
47067
|
+
tempPlanes[i * 2 + 0][0] = m[ 3] + m[i];
|
|
47068
|
+
tempPlanes[i * 2 + 0][1] = m[ 7] + m[i + 4];
|
|
47069
|
+
tempPlanes[i * 2 + 0][2] = m[11] + m[i + 8];
|
|
47070
|
+
tempPlanes[i * 2 + 0][3] = m[15] + m[i + 12];
|
|
47071
|
+
|
|
47072
|
+
tempPlanes[i * 2 + 1][0] = m[ 3] - m[i];
|
|
47073
|
+
tempPlanes[i * 2 + 1][1] = m[ 7] - m[i + 4];
|
|
47074
|
+
tempPlanes[i * 2 + 1][2] = m[11] - m[i + 8];
|
|
47075
|
+
tempPlanes[i * 2 + 1][3] = m[15] - m[i + 12];
|
|
47076
|
+
}
|
|
47077
|
+
|
|
47078
|
+
// Normalize each plane
|
|
47079
|
+
tempPlanes.forEach(p => math.divVec4Scalar(p, math.lenVec3(p), p));
|
|
47080
|
+
|
|
47081
|
+
return aabb => tempPlanes.every(p => {
|
|
47082
|
+
// Compute the positive vertex (farthest in direction of normal)
|
|
47083
|
+
tempVec4[0] = aabb[(p[0] >= 0) ? 3 : 0];
|
|
47084
|
+
tempVec4[1] = aabb[(p[1] >= 0) ? 4 : 1];
|
|
47085
|
+
tempVec4[2] = aabb[(p[2] >= 0) ? 5 : 2];
|
|
47086
|
+
tempVec4[3] = 1;
|
|
47087
|
+
return math.dotVec4(p, tempVec4) >= 0;
|
|
47088
|
+
});
|
|
47089
|
+
};
|
|
47090
|
+
|
|
47091
|
+
const makeRayAABBIntersectionTest = (O, D) => aabb => {
|
|
47092
|
+
let tmin = -Infinity;
|
|
47093
|
+
let tmax = Infinity;
|
|
47094
|
+
|
|
47095
|
+
for (let i = 0; i < 3; i++) {
|
|
47096
|
+
const origin = O[i];
|
|
47097
|
+
const direction = D[i];
|
|
47098
|
+
const minBound = aabb[i];
|
|
47099
|
+
const maxBound = aabb[i + 3];
|
|
47100
|
+
|
|
47101
|
+
if (Math.abs(direction) < 1e-8) {
|
|
47102
|
+
// Ray is parallel to plane
|
|
47103
|
+
if ((origin < minBound) || (origin > maxBound)) {
|
|
47104
|
+
return false; // No intersection
|
|
47105
|
+
}
|
|
47106
|
+
} else {
|
|
47107
|
+
const t1 = (minBound - origin) / direction;
|
|
47108
|
+
const t2 = (maxBound - origin) / direction;
|
|
47109
|
+
|
|
47110
|
+
tmin = Math.max(tmin, Math.min(t1, t2)); // near
|
|
47111
|
+
tmax = Math.min(tmax, Math.max(t1, t2)); // far
|
|
47112
|
+
|
|
47113
|
+
if (tmin > tmax) {
|
|
47114
|
+
return false; // No intersection
|
|
47115
|
+
}
|
|
47116
|
+
}
|
|
47117
|
+
}
|
|
47118
|
+
|
|
47119
|
+
return tmax >= 0; // Check if intersection is in front of ray
|
|
47120
|
+
};
|
|
47121
|
+
|
|
46981
47122
|
/**
|
|
46982
47123
|
* @private
|
|
46983
47124
|
*/
|
|
@@ -47730,6 +47871,8 @@ const Renderer$1 = function (scene, options) {
|
|
|
47730
47871
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
47731
47872
|
}
|
|
47732
47873
|
|
|
47874
|
+
frameCtx.testAABB = makeFrustumAABBIntersectionTest(scene.camera);
|
|
47875
|
+
|
|
47733
47876
|
const renderDrawables = function(drawables) {
|
|
47734
47877
|
|
|
47735
47878
|
let normalDrawSAOBinLen = 0;
|
|
@@ -48064,6 +48207,8 @@ const Renderer$1 = function (scene, options) {
|
|
|
48064
48207
|
renderDrawables(uiDrawableList);
|
|
48065
48208
|
}
|
|
48066
48209
|
|
|
48210
|
+
frameCtx.testAABB = null;
|
|
48211
|
+
|
|
48067
48212
|
const endTime = Date.now();
|
|
48068
48213
|
const frameStats = stats.frame;
|
|
48069
48214
|
|
|
@@ -48172,11 +48317,16 @@ const Renderer$1 = function (scene, options) {
|
|
|
48172
48317
|
|
|
48173
48318
|
pickResult.canvasPos = params.canvasPos;
|
|
48174
48319
|
|
|
48320
|
+
math.canvasPosToWorldRay(canvas, pickViewMatrix, pickProjMatrix, projection, canvasPos, tempVec3a, tempVec3b);
|
|
48321
|
+
frameCtx.testAABB = makeRayAABBIntersectionTest(tempVec3a, tempVec3b);
|
|
48175
48322
|
} else {
|
|
48176
48323
|
|
|
48177
48324
|
// Picking with arbitrary World-space ray
|
|
48178
48325
|
// Align camera along ray and fire ray through center of canvas
|
|
48179
48326
|
|
|
48327
|
+
canvasPos[0] = canvas.clientWidth * 0.5;
|
|
48328
|
+
canvasPos[1] = canvas.clientHeight * 0.5;
|
|
48329
|
+
|
|
48180
48330
|
if (params.matrix) {
|
|
48181
48331
|
|
|
48182
48332
|
pickViewMatrix = params.matrix;
|
|
@@ -48186,6 +48336,8 @@ const Renderer$1 = function (scene, options) {
|
|
|
48186
48336
|
nearAndFar[0] = camera.project.near;
|
|
48187
48337
|
nearAndFar[1] = camera.project.far;
|
|
48188
48338
|
|
|
48339
|
+
math.canvasPosToWorldRay(canvas, pickViewMatrix, pickProjMatrix, projection, canvasPos, tempVec3a, tempVec3b);
|
|
48340
|
+
frameCtx.testAABB = makeRayAABBIntersectionTest(tempVec3a, tempVec3b);
|
|
48189
48341
|
} else {
|
|
48190
48342
|
|
|
48191
48343
|
worldRayOrigin.set(params.origin || [0, 0, 0]);
|
|
@@ -48214,10 +48366,9 @@ const Renderer$1 = function (scene, options) {
|
|
|
48214
48366
|
|
|
48215
48367
|
pickResult.origin = worldRayOrigin;
|
|
48216
48368
|
pickResult.direction = worldRayDir;
|
|
48217
|
-
}
|
|
48218
48369
|
|
|
48219
|
-
|
|
48220
|
-
|
|
48370
|
+
frameCtx.testAABB = makeRayAABBIntersectionTest(pickResult.origin, pickResult.direction);
|
|
48371
|
+
}
|
|
48221
48372
|
}
|
|
48222
48373
|
|
|
48223
48374
|
pickBuffer.bind();
|
|
@@ -48254,6 +48405,8 @@ const Renderer$1 = function (scene, options) {
|
|
|
48254
48405
|
renderDrawables(uiDrawableList);
|
|
48255
48406
|
}
|
|
48256
48407
|
|
|
48408
|
+
frameCtx.testAABB = null;
|
|
48409
|
+
|
|
48257
48410
|
const pickID = pixelToInt(pickBuffer.read(0, 0));
|
|
48258
48411
|
const pickable = (pickID >= 0) && pickIDs.items[pickID];
|
|
48259
48412
|
|
|
@@ -54014,7 +54167,7 @@ class SectionCaps {
|
|
|
54014
54167
|
const visibleSceneModels = Object.values(scene.models).filter(sceneModel => (sceneModel.id in modelCaches) && sceneModel.visible);
|
|
54015
54168
|
sectionPlanes.forEach((plane) => {
|
|
54016
54169
|
if (plane.active) {
|
|
54017
|
-
const sliceMesh = math.makeSectionPlaneSlicer(plane
|
|
54170
|
+
const sliceMesh = math.makeSectionPlaneSlicer(plane);
|
|
54018
54171
|
visibleSceneModels.forEach(sceneModel => {
|
|
54019
54172
|
const modelAABB = sceneModel.aabb;
|
|
54020
54173
|
if (math.planeIntersectsAABB3(plane, modelAABB)) {
|
|
@@ -54034,10 +54187,11 @@ class SectionCaps {
|
|
|
54034
54187
|
});
|
|
54035
54188
|
|
|
54036
54189
|
entityCache.meshCaches.filter(meshCache => math.planeIntersectsAABB3(plane, meshCache.mesh.aabb)).forEach((meshCache, meshIdx) => {
|
|
54037
|
-
sliceMesh(modelCenter, meshCache.meshIndices, meshCache.meshVertices
|
|
54190
|
+
const geo = sliceMesh({ origin: modelCenter, indices: meshCache.meshIndices, positions: meshCache.meshVertices }, { onlyPosSliceWithUV: true }).pos;
|
|
54191
|
+
if (geo) {
|
|
54038
54192
|
entityCache.capMeshes.push(new Mesh(scene, {
|
|
54039
54193
|
isObject: true,
|
|
54040
|
-
id: `${plane.id}-${entityId}-${meshIdx}
|
|
54194
|
+
id: `${plane.id}-${entityId}-${meshIdx}`,
|
|
54041
54195
|
material: entity.capMaterial,
|
|
54042
54196
|
origin: math.addVec3(modelCenter, math.mulVec3Scalar(plane.dir, 0.001, tempVec3a$a), tempVec3a$a),
|
|
54043
54197
|
geometry: new ReadableGeometry(scene, {
|
|
@@ -54048,7 +54202,7 @@ class SectionCaps {
|
|
|
54048
54202
|
uv: geo.uv
|
|
54049
54203
|
})
|
|
54050
54204
|
}));
|
|
54051
|
-
}
|
|
54205
|
+
}
|
|
54052
54206
|
});
|
|
54053
54207
|
}
|
|
54054
54208
|
});
|
|
@@ -60460,7 +60614,7 @@ class CameraControl extends Component {
|
|
|
60460
60614
|
*
|
|
60461
60615
|
* See class docs for usage.
|
|
60462
60616
|
*
|
|
60463
|
-
* @param {{Number:Number}|String} value Either a set of new key mappings, or a string to select a keyboard layout,
|
|
60617
|
+
* @param {{Number:(Number | Number[])[]} | String} value Either a set of new key mappings, or a string to select a keyboard layout,
|
|
60464
60618
|
* which causes ````CameraControl```` to use the default key mappings for that layout.
|
|
60465
60619
|
*/
|
|
60466
60620
|
set keyMap(value) {
|
|
@@ -60539,7 +60693,7 @@ class CameraControl extends Component {
|
|
|
60539
60693
|
/**
|
|
60540
60694
|
* Gets custom mappings of keys to {@link CameraControl} actions.
|
|
60541
60695
|
*
|
|
60542
|
-
* @returns {{Number:Number}} Current key mappings.
|
|
60696
|
+
* @returns {{Number:(Number | Number[])[]}} Current key mappings.
|
|
60543
60697
|
*/
|
|
60544
60698
|
get keyMap() {
|
|
60545
60699
|
return this._keyMap;
|
|
@@ -131076,6 +131230,974 @@ class CxConverterIFCLoaderPlugin extends Plugin {
|
|
|
131076
131230
|
}
|
|
131077
131231
|
}
|
|
131078
131232
|
|
|
131233
|
+
/**
|
|
131234
|
+
* Default data access strategy for {@link IFCOpenShellLoaderPlugin}.
|
|
131235
|
+
*
|
|
131236
|
+
* This just loads assets using XMLHttpRequest.
|
|
131237
|
+
*/
|
|
131238
|
+
class IFCOpenShellDefaultDataSource {
|
|
131239
|
+
|
|
131240
|
+
constructor(cfg = {}) {
|
|
131241
|
+
this.cacheBuster = (cfg.cacheBuster !== false);
|
|
131242
|
+
}
|
|
131243
|
+
|
|
131244
|
+
_cacheBusterURL(url) {
|
|
131245
|
+
if (!this.cacheBuster) {
|
|
131246
|
+
return url;
|
|
131247
|
+
}
|
|
131248
|
+
const timestamp = new Date().getTime();
|
|
131249
|
+
if (url.indexOf('?') > -1) {
|
|
131250
|
+
return url + '&_=' + timestamp;
|
|
131251
|
+
} else {
|
|
131252
|
+
return url + '?_=' + timestamp;
|
|
131253
|
+
}
|
|
131254
|
+
}
|
|
131255
|
+
|
|
131256
|
+
/**
|
|
131257
|
+
* Gets the contents of the given IFC file in an arraybuffer.
|
|
131258
|
+
*
|
|
131259
|
+
* @param {String|Number} src Path or ID of an IFC file.
|
|
131260
|
+
* @param {Function} ok Callback fired on success, argument is the IFC file in an arraybuffer.
|
|
131261
|
+
* @param {Function} error Callback fired on error.
|
|
131262
|
+
*/
|
|
131263
|
+
getIFC(src, ok, error) {
|
|
131264
|
+
src = this._cacheBusterURL(src);
|
|
131265
|
+
|
|
131266
|
+
var defaultCallback = () => {
|
|
131267
|
+
};
|
|
131268
|
+
ok = ok || defaultCallback;
|
|
131269
|
+
error = error || defaultCallback;
|
|
131270
|
+
const dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/;
|
|
131271
|
+
const dataUriRegexResult = src.match(dataUriRegex);
|
|
131272
|
+
if (dataUriRegexResult) { // Safari can't handle data URIs through XMLHttpRequest
|
|
131273
|
+
const isBase64 = !!dataUriRegexResult[2];
|
|
131274
|
+
var data = dataUriRegexResult[3];
|
|
131275
|
+
data = window.decodeURIComponent(data);
|
|
131276
|
+
if (isBase64) {
|
|
131277
|
+
data = window.atob(data);
|
|
131278
|
+
}
|
|
131279
|
+
try {
|
|
131280
|
+
const buffer = new ArrayBuffer(data.length);
|
|
131281
|
+
const view = new Uint8Array(buffer);
|
|
131282
|
+
for (var i = 0; i < data.length; i++) {
|
|
131283
|
+
view[i] = data.charCodeAt(i);
|
|
131284
|
+
}
|
|
131285
|
+
ok(buffer);
|
|
131286
|
+
} catch (errMsg) {
|
|
131287
|
+
error(errMsg);
|
|
131288
|
+
}
|
|
131289
|
+
} else {
|
|
131290
|
+
const request = new XMLHttpRequest();
|
|
131291
|
+
request.open('GET', src, true);
|
|
131292
|
+
request.responseType = 'text';
|
|
131293
|
+
request.onreadystatechange = function () {
|
|
131294
|
+
if (request.readyState === 4) {
|
|
131295
|
+
if (request.status === 200) {
|
|
131296
|
+
ok(request.response);
|
|
131297
|
+
} else {
|
|
131298
|
+
error('getIFC error : ' + request.response);
|
|
131299
|
+
}
|
|
131300
|
+
}
|
|
131301
|
+
};
|
|
131302
|
+
request.send(null);
|
|
131303
|
+
}
|
|
131304
|
+
}
|
|
131305
|
+
}
|
|
131306
|
+
|
|
131307
|
+
/**
|
|
131308
|
+
* {@link Viewer} plugin that uses [IfcOpenShell](https://ifcopenshell.org/) to load BIM models directly from IFC files.
|
|
131309
|
+
*
|
|
131310
|
+
* <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>
|
|
131311
|
+
*
|
|
131312
|
+
* [[Run this example](https://xeokit.github.io/xeokit-sdk/examples/index.html#BIMOffline_IFCOpenShellLoaderPlugin_Duplex)]
|
|
131313
|
+
*
|
|
131314
|
+
* ## Overview
|
|
131315
|
+
*
|
|
131316
|
+
* * Loads small-to-medium sized BIM models directly from IFC files.
|
|
131317
|
+
* * Uses [IfcOpenShell API](https://ifcopenshell.org/) to parse IFC files in the browser.
|
|
131318
|
+
* * Loads IFC geometry, element structure metadata, and property sets.
|
|
131319
|
+
* * Not for large models. For best performance with large models, we recommend using {@link XKTLoaderPlugin}.
|
|
131320
|
+
* * Loads double-precision coordinates, enabling models to be viewed at global coordinates without accuracy loss.
|
|
131321
|
+
* * Filter which IFC types don't get loaded.
|
|
131322
|
+
* * Configure initial appearances of specified IFC types.
|
|
131323
|
+
* * Set a custom data source for IFC files.
|
|
131324
|
+
*
|
|
131325
|
+
* ## Limitations
|
|
131326
|
+
*
|
|
131327
|
+
* Loading and parsing huge IFC STEP files can be slow, and can overwhelm the browser, however. To view your
|
|
131328
|
+
* largest IFC models, we recommend instead pre-converting those to xeokit's compressed native .XKT format, then
|
|
131329
|
+
* loading them with {@link XKTLoaderPlugin} instead.</p>
|
|
131330
|
+
*
|
|
131331
|
+
* ## Scene representation
|
|
131332
|
+
*
|
|
131333
|
+
* When loading a model, IFCOpenShellLoaderPlugin creates an {@link Entity} that represents the model, which
|
|
131334
|
+
* will have {@link Entity#isModel} set ````true```` and will be registered by {@link Entity#id}
|
|
131335
|
+
* in {@link Scene#models}. The IFCOpenShellLoaderPlugin also creates an {@link Entity} for each object within the
|
|
131336
|
+
* model. Those Entities will have {@link Entity#isObject} set ````true```` and will be registered
|
|
131337
|
+
* by {@link Entity#id} in {@link Scene#objects}.
|
|
131338
|
+
*
|
|
131339
|
+
* ## Metadata
|
|
131340
|
+
*
|
|
131341
|
+
* When loading a model, IFCOpenShellLoaderPlugin also creates a {@link MetaModel} that represents the model, which contains
|
|
131342
|
+
* a {@link MetaObject} for each IFC element, plus a {@link PropertySet} for each IFC property set. Loading metadata
|
|
131343
|
+
* can be very slow, so we can also optionally disable it if we don't need it.
|
|
131344
|
+
*
|
|
131345
|
+
* ## Usage
|
|
131346
|
+
*
|
|
131347
|
+
* In the example below we'll load the Duplex BIM model from
|
|
131348
|
+
* an [IFC file](https://github.com/xeokit/xeokit-sdk/tree/master/assets/models/ifc). Within our {@link Viewer}, this
|
|
131349
|
+
* will create a bunch of {@link Entity}s that represents the model and its objects, along with a {@link MetaModel},
|
|
131350
|
+
* {@link MetaObject}s and {@link PropertySet}s that hold their metadata.
|
|
131351
|
+
*
|
|
131352
|
+
* ````javascript
|
|
131353
|
+
* import {Viewer, IFCOpenShellLoaderPlugin, NavCubePlugin, TreeViewPlugin} from "../../dist/xeokit-sdk.es.js";
|
|
131354
|
+
*
|
|
131355
|
+
* //------------------------------------------------------------------------------------------------------------------
|
|
131356
|
+
* // 1. Create a Viewer,
|
|
131357
|
+
* // 2. Arrange the camera
|
|
131358
|
+
* //------------------------------------------------------------------------------------------------------------------
|
|
131359
|
+
*
|
|
131360
|
+
* // 1
|
|
131361
|
+
* const viewer = new Viewer({
|
|
131362
|
+
* canvasId: "myCanvas",
|
|
131363
|
+
* transparent: true
|
|
131364
|
+
* });
|
|
131365
|
+
*
|
|
131366
|
+
* // 2
|
|
131367
|
+
* viewer.camera.eye = [-3.933, 2.855, 27.018];
|
|
131368
|
+
* viewer.camera.look = [4.400, 3.724, 8.899];
|
|
131369
|
+
* viewer.camera.up = [-0.018, 0.999, 0.039];
|
|
131370
|
+
*
|
|
131371
|
+
* //------------------------------------------------------------------------------------------------------------------
|
|
131372
|
+
* // 1. Create the IFCOpenShellLoaderPlugin,
|
|
131373
|
+
* // 2. Load an IFC model
|
|
131374
|
+
* //------------------------------------------------------------------------------------------------------------------
|
|
131375
|
+
*
|
|
131376
|
+
* // 1
|
|
131377
|
+
*
|
|
131378
|
+
* const ifcLoader = new IFCOpenShellLoaderPlugin(viewer, {
|
|
131379
|
+
* workerSrc: "./my/directory/IFCOpenShellWorker.js",
|
|
131380
|
+
* ifcOpenShellURL: "./my/directory/ifcopenshell-0.8.3+34a1bc6-cp313-cp313-emscripten_4_0_9_wasm32.whl"
|
|
131381
|
+
* });
|
|
131382
|
+
*
|
|
131383
|
+
* // 2
|
|
131384
|
+
* const model = ifcLoader.load({ // Returns an Entity that represents the model
|
|
131385
|
+
* id: "myModel",
|
|
131386
|
+
* src: "../assets/models/ifc/Duplex.ifc",
|
|
131387
|
+
* excludeTypes: ["IfcSpace"],
|
|
131388
|
+
* edges: true
|
|
131389
|
+
* });
|
|
131390
|
+
*
|
|
131391
|
+
* model.on("loaded", () => {
|
|
131392
|
+
*
|
|
131393
|
+
* //----------------------------------------------------------------------------------------------------------
|
|
131394
|
+
* // 1. Find metadata on the bottom storey
|
|
131395
|
+
* // 2. X-ray all the objects except for the bottom storey
|
|
131396
|
+
* // 3. Fit the bottom storey in view
|
|
131397
|
+
* //----------------------------------------------------------------------------------------------------------
|
|
131398
|
+
*
|
|
131399
|
+
* // 1
|
|
131400
|
+
* const metaModel = viewer.metaScene.metaModels["myModel"]; // MetaModel with ID "myModel"
|
|
131401
|
+
* const metaObject
|
|
131402
|
+
* = viewer.metaScene.metaObjects["1xS3BCk291UvhgP2dvNsgp"]; // MetaObject with ID "1xS3BCk291UvhgP2dvNsgp"
|
|
131403
|
+
*
|
|
131404
|
+
* const name = metaObject.name; // "01 eerste verdieping"
|
|
131405
|
+
* const type = metaObject.type; // "IfcBuildingStorey"
|
|
131406
|
+
* const parent = metaObject.parent; // MetaObject with type "IfcBuilding"
|
|
131407
|
+
* const children = metaObject.children; // Array of child MetaObjects
|
|
131408
|
+
* const objectId = metaObject.id; // "1xS3BCk291UvhgP2dvNsgp"
|
|
131409
|
+
* const objectIds = viewer.metaScene.getObjectIDsInSubtree(objectId); // IDs of leaf sub-objects
|
|
131410
|
+
* const aabb = viewer.scene.getAABB(objectIds); // Axis-aligned boundary of the leaf sub-objects
|
|
131411
|
+
*
|
|
131412
|
+
* // 2
|
|
131413
|
+
* viewer.scene.setObjectsXRayed(viewer.scene.objectIds, true);
|
|
131414
|
+
* viewer.scene.setObjectsXRayed(objectIds, false);
|
|
131415
|
+
*
|
|
131416
|
+
* // 3
|
|
131417
|
+
* viewer.cameraFlight.flyTo(aabb);
|
|
131418
|
+
*
|
|
131419
|
+
* // Find the model Entity by ID
|
|
131420
|
+
* model = viewer.scene.models["myModel"];
|
|
131421
|
+
*
|
|
131422
|
+
* // Destroy the model
|
|
131423
|
+
* model.destroy();
|
|
131424
|
+
* });
|
|
131425
|
+
* ````
|
|
131426
|
+
*
|
|
131427
|
+
* ## Configuring a custom data source
|
|
131428
|
+
*
|
|
131429
|
+
* By default, IFCOpenShellLoaderPlugin will load IFC files over HTTP.
|
|
131430
|
+
*
|
|
131431
|
+
* In the example below, we'll customize the way IFCOpenShellLoaderPlugin loads the files by configuring it with our own data source
|
|
131432
|
+
* object. For simplicity, our custom data source example also uses HTTP, using a couple of xeokit utility functions.
|
|
131433
|
+
*
|
|
131434
|
+
* ````javascript
|
|
131435
|
+
* import {utils} from "xeokit-sdk.es.js";
|
|
131436
|
+
*
|
|
131437
|
+
* class MyDataSource {
|
|
131438
|
+
*
|
|
131439
|
+
* constructor() {
|
|
131440
|
+
* }
|
|
131441
|
+
*
|
|
131442
|
+
* // Gets the contents of the given IFC file in an arraybuffer
|
|
131443
|
+
* getIFC(src, ok, error) {
|
|
131444
|
+
* console.log("MyDataSource#getIFC(" + IFCSrc + ", ... )");
|
|
131445
|
+
* utils.loadArraybuffer(src,
|
|
131446
|
+
* (arraybuffer) => {
|
|
131447
|
+
* ok(arraybuffer);
|
|
131448
|
+
* },
|
|
131449
|
+
* function (errMsg) {
|
|
131450
|
+
* error(errMsg);
|
|
131451
|
+
* });
|
|
131452
|
+
* }
|
|
131453
|
+
* }
|
|
131454
|
+
*
|
|
131455
|
+
* const ifcLoader2 = new IFCOpenShellLoaderPlugin(viewer, {
|
|
131456
|
+
* dataSource: new MyDataSource()
|
|
131457
|
+
* });
|
|
131458
|
+
*
|
|
131459
|
+
* const model5 = ifcLoader2.load({
|
|
131460
|
+
* id: "myModel5",
|
|
131461
|
+
* src: "../assets/models/ifc/Duplex.ifc"
|
|
131462
|
+
* });
|
|
131463
|
+
* ````
|
|
131464
|
+
*
|
|
131465
|
+
* ## Loading multiple copies of a model, without object ID clashes
|
|
131466
|
+
*
|
|
131467
|
+
* Sometimes we need to load two or more instances of the same model, without having clashes
|
|
131468
|
+
* between the IDs of the equivalent objects in the model instances.
|
|
131469
|
+
*
|
|
131470
|
+
* As shown in the example below, we do this by setting {@link IFCOpenShellLoaderPlugin#globalizeObjectIds} ````true```` before we load our models.
|
|
131471
|
+
*
|
|
131472
|
+
* ````javascript
|
|
131473
|
+
* ifcLoader.globalizeObjectIds = true;
|
|
131474
|
+
*
|
|
131475
|
+
* const model = ifcLoader.load({
|
|
131476
|
+
* id: "model1",
|
|
131477
|
+
* src: "../assets/models/ifc/Duplex.ifc"
|
|
131478
|
+
* });
|
|
131479
|
+
*
|
|
131480
|
+
* const model2 = ifcLoader.load({
|
|
131481
|
+
* id: "model2",
|
|
131482
|
+
* src: "../assets/models/ifc/Duplex.ifc"
|
|
131483
|
+
* });
|
|
131484
|
+
* ````
|
|
131485
|
+
*
|
|
131486
|
+
* For each {@link Entity} loaded by these two calls, {@link Entity#id} and {@link MetaObject#id} will get prefixed by
|
|
131487
|
+
* the ID of their model, in order to avoid ID clashes between the two models.
|
|
131488
|
+
*
|
|
131489
|
+
* An Entity belonging to the first model will get an ID like this:
|
|
131490
|
+
*
|
|
131491
|
+
* ````
|
|
131492
|
+
* myModel1#0BTBFw6f90Nfh9rP1dlXrb
|
|
131493
|
+
* ````
|
|
131494
|
+
*
|
|
131495
|
+
* The equivalent Entity in the second model will get an ID like this:
|
|
131496
|
+
*
|
|
131497
|
+
* ````
|
|
131498
|
+
* myModel2#0BTBFw6f90Nfh9rP1dlXrb
|
|
131499
|
+
* ````
|
|
131500
|
+
*
|
|
131501
|
+
* Now, to update the visibility of both of those Entities collectively, using {@link Scene#setObjectsVisible}, we can
|
|
131502
|
+
* supply just the IFC product ID part to that method:
|
|
131503
|
+
*
|
|
131504
|
+
* ````javascript
|
|
131505
|
+
* myViewer.scene.setObjectVisibilities("0BTBFw6f90Nfh9rP1dlXrb", true);
|
|
131506
|
+
* ````
|
|
131507
|
+
*
|
|
131508
|
+
* The method, along with {@link Scene#setObjectsXRayed}, {@link Scene#setObjectsHighlighted} etc, will internally expand
|
|
131509
|
+
* the given ID to refer to the instances of that Entity in both models.
|
|
131510
|
+
*
|
|
131511
|
+
* We can also, of course, reference each Entity directly, using its globalized ID:
|
|
131512
|
+
*
|
|
131513
|
+
* ````javascript
|
|
131514
|
+
* myViewer.scene.setObjectVisibilities("myModel1#0BTBFw6f90Nfh9rP1dlXrb", true);
|
|
131515
|
+
*````
|
|
131516
|
+
*
|
|
131517
|
+
* @class IFCOpenShellLoaderPlugin
|
|
131518
|
+
* @since 2.6.90
|
|
131519
|
+
*/
|
|
131520
|
+
class IFCOpenShellLoaderPlugin extends Plugin {
|
|
131521
|
+
|
|
131522
|
+
/**
|
|
131523
|
+
* @param {Viewer} viewer The {@link Viewer} that will own this plugin.
|
|
131524
|
+
* @param {Object} cfg Plugin configuration.
|
|
131525
|
+
* @param {String} [cfg.id="IFCOpenShellLoader"] Optional ID for this plugin instance.
|
|
131526
|
+
* @param {Object} [cfg.dataSource] Custom data source (defaults to {@link IFCOpenShellDefaultDataSource}).
|
|
131527
|
+
* @param {Object} cfg.ifcopenshell IfcOpenShell API object.
|
|
131528
|
+
* @param {Object} cfg.ifcopenshell_geom IfcOpenShell geometry API object.
|
|
131529
|
+
*/
|
|
131530
|
+
constructor(viewer, cfg) {
|
|
131531
|
+
|
|
131532
|
+
super("IFCOpenShellLoader", viewer, cfg);
|
|
131533
|
+
|
|
131534
|
+
if (!cfg) {
|
|
131535
|
+
throw new Error("IFCOpenShellLoaderPlugin: No configuration given");
|
|
131536
|
+
}
|
|
131537
|
+
|
|
131538
|
+
if (!cfg.ifcopenshell) {
|
|
131539
|
+
throw new Error("IFCOpenShellLoaderPlugin: No ifcopenshell given");
|
|
131540
|
+
}
|
|
131541
|
+
|
|
131542
|
+
if (!cfg.ifcopenshell_geom) {
|
|
131543
|
+
throw new Error("IFCOpenShellLoaderPlugin: No ifcopenshell_geom given");
|
|
131544
|
+
}
|
|
131545
|
+
|
|
131546
|
+
this.ifcopenshell = cfg.ifcopenshell;
|
|
131547
|
+
this.ifcopenshell_geom = cfg.ifcopenshell_geom;
|
|
131548
|
+
|
|
131549
|
+
this.dataSource = cfg.dataSource;
|
|
131550
|
+
}
|
|
131551
|
+
|
|
131552
|
+
/**
|
|
131553
|
+
* Sets a custom data source for IFC files.
|
|
131554
|
+
* @param value
|
|
131555
|
+
*/
|
|
131556
|
+
set dataSource(value) {
|
|
131557
|
+
this._dataSource = value || new IFCOpenShellDefaultDataSource();
|
|
131558
|
+
}
|
|
131559
|
+
|
|
131560
|
+
/**
|
|
131561
|
+
* Gets the data source for IFC files.
|
|
131562
|
+
* @returns {*|IFCOpenShellDefaultDataSource}
|
|
131563
|
+
*/
|
|
131564
|
+
get dataSource() {
|
|
131565
|
+
return this._dataSource;
|
|
131566
|
+
}
|
|
131567
|
+
|
|
131568
|
+
/**
|
|
131569
|
+
* Gets whether IFCOpenShellLoaderPlugin globalizes each {@link Entity#id} and {@link MetaObject#id} as it loads a model.
|
|
131570
|
+
*
|
|
131571
|
+
* Default value is ````false````.
|
|
131572
|
+
*
|
|
131573
|
+
* @type {Boolean}
|
|
131574
|
+
*/
|
|
131575
|
+
get globalizeObjectIds() {
|
|
131576
|
+
return this._globalizeObjectIds;
|
|
131577
|
+
}
|
|
131578
|
+
|
|
131579
|
+
/**
|
|
131580
|
+
* Sets whether IFCOpenShellLoaderPlugin globalizes each {@link Entity#id} and {@link MetaObject#id} as it loads a model.
|
|
131581
|
+
*
|
|
131582
|
+
* Set this ````true```` when you need to load multiple instances of the same model, to avoid ID clashes
|
|
131583
|
+
* between the objects in the different instances.
|
|
131584
|
+
*
|
|
131585
|
+
* When we load a model with this set ````true````, then each {@link Entity#id} and {@link MetaObject#id} will be
|
|
131586
|
+
* prefixed by the ID of the model, ie. ````<modelId>#<objectId>````.
|
|
131587
|
+
*
|
|
131588
|
+
* {@link Entity#originalSystemId} and {@link MetaObject#originalSystemId} will always hold the original, un-prefixed, ID values.
|
|
131589
|
+
*
|
|
131590
|
+
* Default value is ````false````.
|
|
131591
|
+
*
|
|
131592
|
+
* See the main {@link IFCOpenShellLoaderPlugin} class documentation for usage info.
|
|
131593
|
+
*
|
|
131594
|
+
* @type {Boolean}
|
|
131595
|
+
*/
|
|
131596
|
+
set globalizeObjectIds(value) {
|
|
131597
|
+
this._globalizeObjectIds = !!value;
|
|
131598
|
+
}
|
|
131599
|
+
|
|
131600
|
+
/**
|
|
131601
|
+
* Loads an IFC model from a file or text into the {@link Viewer}.
|
|
131602
|
+
*
|
|
131603
|
+
* @param {Object} params
|
|
131604
|
+
* @param {String} [params.id] Optional root Entity ID.
|
|
131605
|
+
* @param {String} [params.src] IFC file path (alternative to `text`).
|
|
131606
|
+
* @param {String} [params.text] IFC text (alternative to `src`).
|
|
131607
|
+
* @param {{String:Object}} [params.objectDefaults]
|
|
131608
|
+
* @param {String[]} [params.excludeTypes] Array of IFC types to exclude.
|
|
131609
|
+
* @param {Number[]} [params.origin=[0,0,0]] Optional World-coordinate origin to apply to the model.
|
|
131610
|
+
* @param {Number[]} [params.position=[0,0,0]] Optional position offset to apply to the model.
|
|
131611
|
+
* @param {Number[]} [params.rotation=[0,0,0]] Optional XYZ Euler rotation (degrees) to apply to the model.
|
|
131612
|
+
* @param {Boolean} [params.backfaces=true] Whether to render backfaces.
|
|
131613
|
+
* @param {Boolean} [params.dtxEnabled=true] Whether to enable data texture storage for geometry buffers.
|
|
131614
|
+
* @param {Boolean} [params.loadMetadata=true] Whether to load metadata.
|
|
131615
|
+
* @param {Boolean} [params.loadMetadataPropertySets=true] Whether to load property sets within the metadata. Only works when `loadMetadata` is true.
|
|
131616
|
+
* @param {Boolean} [params.edges=false] Whether to generate edge lines for the model.
|
|
131617
|
+
* @param {Boolean} [params.saoEnabled=false] Whether to enable SAO for the model.
|
|
131618
|
+
* @param {Boolean} [params.globalizeObjectIds=false] Whether to globalize each {@link Entity#id} and {@link MetaObject#id} as it loads the model.
|
|
131619
|
+
* @returns {Entity}
|
|
131620
|
+
*/
|
|
131621
|
+
async load(params = {}) {
|
|
131622
|
+
|
|
131623
|
+
let {
|
|
131624
|
+
id,
|
|
131625
|
+
backfaces = true,
|
|
131626
|
+
dtxEnabled = true,
|
|
131627
|
+
position,
|
|
131628
|
+
rotation,
|
|
131629
|
+
origin,
|
|
131630
|
+
loadMetadata,
|
|
131631
|
+
loadMetadataPropertySets,
|
|
131632
|
+
edges,
|
|
131633
|
+
saoEnabled,
|
|
131634
|
+
globalizeObjectIds,
|
|
131635
|
+
excludeTypes
|
|
131636
|
+
} = params;
|
|
131637
|
+
|
|
131638
|
+
if (id && this.viewer.scene.components[id]) {
|
|
131639
|
+
this.error(`Component with this ID already exists: ${id} - autogenerating SceneModel ID`);
|
|
131640
|
+
id = null;
|
|
131641
|
+
}
|
|
131642
|
+
|
|
131643
|
+
const sceneModel = new SceneModel(this.viewer.scene, {
|
|
131644
|
+
id,
|
|
131645
|
+
isModel: true,
|
|
131646
|
+
globalizeObjectIds,
|
|
131647
|
+
backfaces,
|
|
131648
|
+
dtxEnabled,
|
|
131649
|
+
position,
|
|
131650
|
+
rotation,
|
|
131651
|
+
origin,
|
|
131652
|
+
edges,
|
|
131653
|
+
saoEnabled
|
|
131654
|
+
});
|
|
131655
|
+
|
|
131656
|
+
const modelId = sceneModel.id;
|
|
131657
|
+
|
|
131658
|
+
if (!params.src && !params.text) {
|
|
131659
|
+
this.error("load() expected 'src' or 'text'");
|
|
131660
|
+
return sceneModel; // Return empty model
|
|
131661
|
+
}
|
|
131662
|
+
|
|
131663
|
+
const spinner = this.viewer.scene.canvas.spinner;
|
|
131664
|
+
spinner.processes++;
|
|
131665
|
+
|
|
131666
|
+
const loadIFC = (fileData) => {
|
|
131667
|
+
const ifc = this.ifcopenshell.file.from_string(fileData);
|
|
131668
|
+
const ctx = {
|
|
131669
|
+
loadMetadataPropertySets: (loadMetadataPropertySets !== false),
|
|
131670
|
+
globalizeObjectIds: globalizeObjectIds || this._globalizeObjectIds,
|
|
131671
|
+
geometryCache: new Map(),
|
|
131672
|
+
ifc,
|
|
131673
|
+
sceneModel
|
|
131674
|
+
};
|
|
131675
|
+
if (excludeTypes) {
|
|
131676
|
+
ctx.excludeTypes = excludeTypes;
|
|
131677
|
+
}
|
|
131678
|
+
this._loadIFCGeometry(ctx);
|
|
131679
|
+
if (loadMetadata !== false) {
|
|
131680
|
+
const metaModelData = this._loadIFCMetaModel(ctx, ifc);
|
|
131681
|
+
this.viewer.metaScene.createMetaModel(modelId, metaModelData);
|
|
131682
|
+
}
|
|
131683
|
+
this.viewer.scene.canvas.spinner.processes--;
|
|
131684
|
+
};
|
|
131685
|
+
|
|
131686
|
+
if (params.src) {
|
|
131687
|
+
this.viewer.scene.canvas.spinner.processes++;
|
|
131688
|
+
this._dataSource.getIFC(
|
|
131689
|
+
params.src,
|
|
131690
|
+
(fileData) => {
|
|
131691
|
+
loadIFC(fileData);
|
|
131692
|
+
this.viewer.scene.canvas.spinner.processes--;
|
|
131693
|
+
},
|
|
131694
|
+
(err) => {
|
|
131695
|
+
this.viewer.scene.canvas.spinner.processes--;
|
|
131696
|
+
this.error(err);
|
|
131697
|
+
}
|
|
131698
|
+
);
|
|
131699
|
+
} else {
|
|
131700
|
+
loadIFC(params.text);
|
|
131701
|
+
}
|
|
131702
|
+
|
|
131703
|
+
sceneModel.once("destroyed", () => {
|
|
131704
|
+
this.viewer.metaScene.destroyMetaModel(modelId);
|
|
131705
|
+
});
|
|
131706
|
+
|
|
131707
|
+
return sceneModel;
|
|
131708
|
+
}
|
|
131709
|
+
|
|
131710
|
+
_loadIFCGeometry(ctx) {
|
|
131711
|
+
const {ifc, sceneModel} = ctx;
|
|
131712
|
+
const {ifcopenshell_geom} = this;
|
|
131713
|
+
|
|
131714
|
+
const settings = ifcopenshell_geom.settings();
|
|
131715
|
+
settings.set(settings.WELD_VERTICES, false);
|
|
131716
|
+
|
|
131717
|
+
const iterator = ifcopenshell_geom.iterator.callKwargs({
|
|
131718
|
+
settings,
|
|
131719
|
+
file_or_filename: ifc,
|
|
131720
|
+
exclude: ctx.excludeTypes,
|
|
131721
|
+
geometry_library: "hybrid-cgal-simple-opencascade"
|
|
131722
|
+
});
|
|
131723
|
+
|
|
131724
|
+
if (iterator.initialize()) {
|
|
131725
|
+
do {
|
|
131726
|
+
const obj = iterator.get();
|
|
131727
|
+
if (obj) {
|
|
131728
|
+
const entity = ifc.by_id(obj.id);
|
|
131729
|
+
this._parseIFCEntity(ctx, obj, entity);
|
|
131730
|
+
}
|
|
131731
|
+
} while (iterator.next());
|
|
131732
|
+
}
|
|
131733
|
+
|
|
131734
|
+
sceneModel.finalize();
|
|
131735
|
+
|
|
131736
|
+
sceneModel.scene.once("tick", () => {
|
|
131737
|
+
if (!sceneModel.destroyed) {
|
|
131738
|
+
sceneModel.scene.fire("modelLoaded", sceneModel.id);
|
|
131739
|
+
sceneModel.fire("loaded", true, false);
|
|
131740
|
+
}
|
|
131741
|
+
});
|
|
131742
|
+
}
|
|
131743
|
+
|
|
131744
|
+
_parseIFCEntity(ctx, obj, ifcEntity) {
|
|
131745
|
+
const {sceneModel, geometryCache} = ctx;
|
|
131746
|
+
const geometry_id = obj.geometry.id;
|
|
131747
|
+
|
|
131748
|
+
const M = obj.transformation.data().components.toJs();
|
|
131749
|
+
const {origin, matrix} = extractRTCTransform(M);
|
|
131750
|
+
|
|
131751
|
+
if (!geometryCache.get(geometry_id)) {
|
|
131752
|
+
|
|
131753
|
+
const srcMaterials = obj.geometry.materials.toJs();
|
|
131754
|
+
const materials = srcMaterials.map((m) => ({
|
|
131755
|
+
diffuse: m.diffuse.components.toJs(),
|
|
131756
|
+
transparency: (m.transparency
|
|
131757
|
+
&& !isNaN(m.transparency)) ? m.transparency : 0.0,
|
|
131758
|
+
}));
|
|
131759
|
+
|
|
131760
|
+
const materialIds = new Int32Array(obj.geometry.material_ids.toJs());
|
|
131761
|
+
|
|
131762
|
+
// Build mapping: materialIndex -> [faceIdx...]
|
|
131763
|
+
const mapping = buildMaterialMapping(materialIds);
|
|
131764
|
+
|
|
131765
|
+
// Create sub-geometry per materialIndex, once
|
|
131766
|
+
const subGeoms = new Map();
|
|
131767
|
+
|
|
131768
|
+
for (const [matIndexStr, faceList] of Object.entries(mapping)) {
|
|
131769
|
+
|
|
131770
|
+
if (!faceList || faceList.length === 0) {
|
|
131771
|
+
continue;
|
|
131772
|
+
}
|
|
131773
|
+
|
|
131774
|
+
// xeokit auto-generates normals on the GPU side
|
|
131775
|
+
|
|
131776
|
+
const positions = new Float32Array(obj.geometry.verts.toJs());
|
|
131777
|
+
const edgeIndices = new Uint32Array(obj.geometry.edges.toJs());
|
|
131778
|
+
const faces = new Uint32Array(obj.geometry.faces.toJs());
|
|
131779
|
+
const matIndex = Number(matIndexStr);
|
|
131780
|
+
const indices = buildIndicesForFaces(faceList, faces);
|
|
131781
|
+
const sceneGeometryId = makeSubGeometryId(geometry_id, matIndex); // deterministic
|
|
131782
|
+
|
|
131783
|
+
sceneModel.createGeometry({
|
|
131784
|
+
id: sceneGeometryId,
|
|
131785
|
+
primitive: "triangles",
|
|
131786
|
+
positions,
|
|
131787
|
+
indices,
|
|
131788
|
+
edgeIndices
|
|
131789
|
+
});
|
|
131790
|
+
|
|
131791
|
+
subGeoms.set(matIndex, sceneGeometryId);
|
|
131792
|
+
}
|
|
131793
|
+
|
|
131794
|
+
geometryCache.set(geometry_id, {
|
|
131795
|
+
subGeoms,
|
|
131796
|
+
materials,
|
|
131797
|
+
// store mapping as Map<number, Uint32Array> to avoid recomputing
|
|
131798
|
+
mapping: new Map(Object.entries(mapping).map(
|
|
131799
|
+
([k, v]) => [Number(k), new Uint32Array(v)]
|
|
131800
|
+
))
|
|
131801
|
+
});
|
|
131802
|
+
}
|
|
131803
|
+
|
|
131804
|
+
// Reuse cached sub-geometries to create per-object meshes
|
|
131805
|
+
|
|
131806
|
+
const cached = geometryCache.get(geometry_id);
|
|
131807
|
+
const meshIds = [];
|
|
131808
|
+
|
|
131809
|
+
for (const [matIndex, sceneGeometryId] of cached.subGeoms.entries()) {
|
|
131810
|
+
const material = cached.materials[matIndex] || {diffuse: [0.6, 0.6, 0.6], transparency: 0.0};
|
|
131811
|
+
const meshId = generateUUID();
|
|
131812
|
+
const diffuse = material.diffuse;
|
|
131813
|
+
sceneModel.createMesh({
|
|
131814
|
+
id: meshId,
|
|
131815
|
+
geometryId: sceneGeometryId,
|
|
131816
|
+
origin,
|
|
131817
|
+
matrix,
|
|
131818
|
+
color: [diffuse[0], diffuse[1], diffuse[2]],
|
|
131819
|
+
opacity: 1.0 - material.transparency
|
|
131820
|
+
});
|
|
131821
|
+
meshIds.push(meshId);
|
|
131822
|
+
}
|
|
131823
|
+
|
|
131824
|
+
sceneModel.createEntity({
|
|
131825
|
+
id: ctx.globalizeObjectIds ? math.globalizeObjectId(ctx.sceneModel.id, ifcEntity.GlobalId) : ifcEntity.GlobalId,
|
|
131826
|
+
isObject: true,
|
|
131827
|
+
meshIds
|
|
131828
|
+
});
|
|
131829
|
+
}
|
|
131830
|
+
|
|
131831
|
+
_loadIFCMetaModel(ctx, ifc) {
|
|
131832
|
+
|
|
131833
|
+
const visited = new Set();
|
|
131834
|
+
const metaObjects = [];
|
|
131835
|
+
const propertySets = [];
|
|
131836
|
+
|
|
131837
|
+
// ---- helpers -----------------------------------------------------------
|
|
131838
|
+
const toStr = (v) => (v === undefined || v === null) ? "" : String(v);
|
|
131839
|
+
|
|
131840
|
+
function getGlobalId(entity) {
|
|
131841
|
+
try {
|
|
131842
|
+
return String(entity.GlobalId);
|
|
131843
|
+
} catch {
|
|
131844
|
+
return null;
|
|
131845
|
+
}
|
|
131846
|
+
}
|
|
131847
|
+
|
|
131848
|
+
function addNode(entity, parent) {
|
|
131849
|
+
const id = getGlobalId(entity);
|
|
131850
|
+
if (!id || visited.has(id)) return false;
|
|
131851
|
+
visited.add(id);
|
|
131852
|
+
const globalizeObjectIds = ctx.globalizeObjectIds;
|
|
131853
|
+
const modelId = ctx.sceneModel.id;
|
|
131854
|
+
const propertySetIds = [];
|
|
131855
|
+
if (ctx.loadMetadataPropertySets) {
|
|
131856
|
+
// Try all possible association fields
|
|
131857
|
+
const associationFields = ["HasAssociations", "IsDefinedBy", "IsDecomposedBy", "ContainsElements"];
|
|
131858
|
+
for (const field of associationFields) {
|
|
131859
|
+
const associations = entity[field];
|
|
131860
|
+
if (associations && associations.length > 0) {
|
|
131861
|
+
for (let j = 0; j < associations.length; j++) {
|
|
131862
|
+
const rel = associations.get(j);
|
|
131863
|
+
if (rel.is_a && rel.is_a() === "IfcRelDefinesByProperties") {
|
|
131864
|
+
const propSet = rel.RelatingPropertyDefinition;
|
|
131865
|
+
if (propSet && propSet.is_a) {
|
|
131866
|
+
// Accept both IfcPropertySet and IfcElementQuantity
|
|
131867
|
+
if (["IfcPropertySet", "IfcElementQuantity"].includes(propSet.is_a())) {
|
|
131868
|
+
const propSetId = propSet.GlobalId ? String(propSet.GlobalId) : null;
|
|
131869
|
+
const propSetName = propSet.Name ? String(propSet.Name) : "";
|
|
131870
|
+
const propSetType = propSet.is_a ? String(propSet.is_a()) : "";
|
|
131871
|
+
const properties = [];
|
|
131872
|
+
const props = propSet.HasProperties || propSet.Quantities;
|
|
131873
|
+
if (props && props.length > 0) {
|
|
131874
|
+
for (let k = 0; k < props.length; k++) {
|
|
131875
|
+
const p = props.get(k);
|
|
131876
|
+
const propName = p.Name ? String(p.Name) : "";
|
|
131877
|
+
let propValue = "";
|
|
131878
|
+
let propType = p.is_a ? String(p.is_a()) : "";
|
|
131879
|
+
if (p.is_a && p.is_a() === "IfcPropertySingleValue") {
|
|
131880
|
+
try {
|
|
131881
|
+
propValue = p.NominalValue ? String(p.NominalValue.wrappedValue) : "";
|
|
131882
|
+
} catch {
|
|
131883
|
+
propValue = "";
|
|
131884
|
+
}
|
|
131885
|
+
} else if (p.is_a && p.is_a() === "IfcPropertyEnumeratedValue") {
|
|
131886
|
+
try {
|
|
131887
|
+
const values = p.EnumerationValues;
|
|
131888
|
+
if (values && values.length > 0) {
|
|
131889
|
+
const arr = [];
|
|
131890
|
+
for (let vi = 0; vi < values.length; vi++) {
|
|
131891
|
+
arr.push(String(values.get(vi).wrappedValue));
|
|
131892
|
+
}
|
|
131893
|
+
propValue = arr.join(", ");
|
|
131894
|
+
}
|
|
131895
|
+
} catch {
|
|
131896
|
+
propValue = "";
|
|
131897
|
+
}
|
|
131898
|
+
} else if (p.is_a && p.is_a() === "IfcQuantityArea") {
|
|
131899
|
+
propValue = p.AreaValue ? String(p.AreaValue) : "";
|
|
131900
|
+
} else if (p.is_a && p.is_a() === "IfcQuantityLength") {
|
|
131901
|
+
propValue = p.LengthValue ? String(p.LengthValue) : "";
|
|
131902
|
+
} else if (p.is_a && p.is_a() === "IfcQuantityVolume") {
|
|
131903
|
+
propValue = p.VolumeValue ? String(p.VolumeValue) : "";
|
|
131904
|
+
} else {
|
|
131905
|
+
try {
|
|
131906
|
+
propValue = p.NominalValue ? String(p.NominalValue) : "";
|
|
131907
|
+
} catch {
|
|
131908
|
+
propValue = "";
|
|
131909
|
+
}
|
|
131910
|
+
}
|
|
131911
|
+
properties.push({
|
|
131912
|
+
name: propName,
|
|
131913
|
+
value: propValue,
|
|
131914
|
+
type: propType
|
|
131915
|
+
});
|
|
131916
|
+
p.destroy?.();
|
|
131917
|
+
}
|
|
131918
|
+
props.destroy?.();
|
|
131919
|
+
}
|
|
131920
|
+
propertySets.push({
|
|
131921
|
+
id: propSetId,
|
|
131922
|
+
// objectId: ctx.globalizeObjectIds ? math.globalizeObjectId(ctx.sceneModel.id, objectId) : objectId,
|
|
131923
|
+
name: propSetName,
|
|
131924
|
+
type: propSetType,
|
|
131925
|
+
properties
|
|
131926
|
+
});
|
|
131927
|
+
propertySetIds.push(propSetId);
|
|
131928
|
+
propSet.destroy?.();
|
|
131929
|
+
}
|
|
131930
|
+
}
|
|
131931
|
+
rel.destroy?.();
|
|
131932
|
+
}
|
|
131933
|
+
}
|
|
131934
|
+
associations.destroy?.();
|
|
131935
|
+
}
|
|
131936
|
+
}
|
|
131937
|
+
}
|
|
131938
|
+
metaObjects.push({
|
|
131939
|
+
id: globalizeObjectIds ? math.globalizeObjectId(modelId, id) : id,
|
|
131940
|
+
type: String(entity.is_a()),
|
|
131941
|
+
parent: parent
|
|
131942
|
+
? (globalizeObjectIds
|
|
131943
|
+
? math.globalizeObjectId(modelId, getGlobalId(parent))
|
|
131944
|
+
: getGlobalId(parent))
|
|
131945
|
+
: null,
|
|
131946
|
+
propertySetIds
|
|
131947
|
+
});
|
|
131948
|
+
return true;
|
|
131949
|
+
}
|
|
131950
|
+
|
|
131951
|
+
function walk(entity, parent = null) {
|
|
131952
|
+
if (!entity) return;
|
|
131953
|
+
|
|
131954
|
+
// add current node (skip children if already visited)
|
|
131955
|
+
if (!addNode(entity, parent)) {
|
|
131956
|
+
entity.destroy?.();
|
|
131957
|
+
return;
|
|
131958
|
+
}
|
|
131959
|
+
|
|
131960
|
+
// 1) Decomposition (IfcRelAggregates / IfcRelNests)
|
|
131961
|
+
const decos = entity.IsDecomposedBy;
|
|
131962
|
+
if (decos) {
|
|
131963
|
+
for (let i = 0; i < decos.length; i++) {
|
|
131964
|
+
const rel = decos.get(i);
|
|
131965
|
+
const children = rel.RelatedObjects;
|
|
131966
|
+
if (children) {
|
|
131967
|
+
for (let j = 0; j < children.length; j++) {
|
|
131968
|
+
const child = children.get(j);
|
|
131969
|
+
walk(child, entity);
|
|
131970
|
+
child.destroy?.();
|
|
131971
|
+
}
|
|
131972
|
+
children.destroy?.();
|
|
131973
|
+
}
|
|
131974
|
+
rel.destroy?.();
|
|
131975
|
+
}
|
|
131976
|
+
}
|
|
131977
|
+
|
|
131978
|
+
// 2) Spatial containment (IfcRelContainedInSpatialStructure)
|
|
131979
|
+
const contains = entity.ContainsElements;
|
|
131980
|
+
if (contains) {
|
|
131981
|
+
for (let i = 0; i < contains.length; i++) {
|
|
131982
|
+
const rel = contains.get(i);
|
|
131983
|
+
const elems = rel.RelatedElements;
|
|
131984
|
+
if (elems) {
|
|
131985
|
+
for (let j = 0; j < elems.length; j++) {
|
|
131986
|
+
const elem = elems.get(j);
|
|
131987
|
+
walk(elem, entity);
|
|
131988
|
+
elem.destroy?.();
|
|
131989
|
+
}
|
|
131990
|
+
elems.destroy?.();
|
|
131991
|
+
}
|
|
131992
|
+
rel.destroy?.();
|
|
131993
|
+
}
|
|
131994
|
+
}
|
|
131995
|
+
|
|
131996
|
+
entity.destroy?.();
|
|
131997
|
+
}
|
|
131998
|
+
|
|
131999
|
+
// ---- metadata extraction ----------------------------------------------
|
|
132000
|
+
let projectId = "";
|
|
132001
|
+
let author = "";
|
|
132002
|
+
let createdAt = ""; // ISO string
|
|
132003
|
+
let schema = "";
|
|
132004
|
+
let creatingApplication = "";
|
|
132005
|
+
|
|
132006
|
+
// Schema (primary)
|
|
132007
|
+
try {
|
|
132008
|
+
// ifcopenshell.file usually exposes a `schema` string property
|
|
132009
|
+
schema = toStr(ifc.schema);
|
|
132010
|
+
} catch {
|
|
132011
|
+
}
|
|
132012
|
+
if (!schema) {
|
|
132013
|
+
// Fallback to STEP header
|
|
132014
|
+
try {
|
|
132015
|
+
const ids = ifc.wrapped_data.header.file_schema.schema_identifiers;
|
|
132016
|
+
if (ids && ids.length > 0) schema = toStr(ids.get(0));
|
|
132017
|
+
} catch {
|
|
132018
|
+
}
|
|
132019
|
+
}
|
|
132020
|
+
|
|
132021
|
+
// Project (also gives us OwnerHistory on many files)
|
|
132022
|
+
const projects = ifc.by_type("IfcProject");
|
|
132023
|
+
if (projects && projects.length > 0) {
|
|
132024
|
+
const project = projects.get(0);
|
|
132025
|
+
projectId = toStr(project.GlobalId);
|
|
132026
|
+
|
|
132027
|
+
// OwnerHistory path (preferred when present)
|
|
132028
|
+
try {
|
|
132029
|
+
const oh = project.OwnerHistory; // deprecated in newer IFC4.x, but present in many files
|
|
132030
|
+
if (oh) {
|
|
132031
|
+
// Author: IfcPersonAndOrganization → ThePerson (GivenName/FamilyName) and TheOrganization.Name
|
|
132032
|
+
try {
|
|
132033
|
+
const user = oh.OwningUser;
|
|
132034
|
+
const person = user?.ThePerson;
|
|
132035
|
+
const org = user?.TheOrganization;
|
|
132036
|
+
const gn = person?.GivenName ? toStr(person.GivenName) : "";
|
|
132037
|
+
const fn = person?.FamilyName ? toStr(person.FamilyName) : "";
|
|
132038
|
+
const personName = (gn || fn) ? [gn, fn].filter(Boolean).join(" ") : "";
|
|
132039
|
+
const orgName = org?.Name ? toStr(org.Name) : "";
|
|
132040
|
+
author = [personName, orgName].filter(Boolean).join(" / ");
|
|
132041
|
+
} catch {
|
|
132042
|
+
}
|
|
132043
|
+
|
|
132044
|
+
// Creation time (UNIX seconds)
|
|
132045
|
+
try {
|
|
132046
|
+
const ts = oh?.CreationDate;
|
|
132047
|
+
if (typeof ts === "number" && isFinite(ts) && ts > 0) {
|
|
132048
|
+
createdAt = new Date(ts * 1000).toISOString();
|
|
132049
|
+
}
|
|
132050
|
+
} catch {
|
|
132051
|
+
}
|
|
132052
|
+
|
|
132053
|
+
// Creating application
|
|
132054
|
+
try {
|
|
132055
|
+
const app = oh?.OwningApplication;
|
|
132056
|
+
const appName =
|
|
132057
|
+
app?.ApplicationFullName ? toStr(app.ApplicationFullName) :
|
|
132058
|
+
app?.ApplicationIdentifier ? toStr(app.ApplicationIdentifier) : "";
|
|
132059
|
+
const appVer = app?.Version ? toStr(app.Version) : "";
|
|
132060
|
+
creatingApplication = [appName, appVer].filter(Boolean).join(" ");
|
|
132061
|
+
} catch {
|
|
132062
|
+
}
|
|
132063
|
+
}
|
|
132064
|
+
} catch {
|
|
132065
|
+
}
|
|
132066
|
+
|
|
132067
|
+
// Clean first project (we’ll traverse below with a fresh pointer anyway)
|
|
132068
|
+
project.destroy?.();
|
|
132069
|
+
}
|
|
132070
|
+
|
|
132071
|
+
// Fallbacks via STEP header if OwnerHistory wasn’t there / incomplete
|
|
132072
|
+
try {
|
|
132073
|
+
const fileName = ifc.wrapped_data.header.file_name;
|
|
132074
|
+
if (!author) {
|
|
132075
|
+
try {
|
|
132076
|
+
const authors = fileName.author;
|
|
132077
|
+
if (authors && authors.length > 0) {
|
|
132078
|
+
// `author` is a LIST in the STEP header; join if multiple
|
|
132079
|
+
const parts = [];
|
|
132080
|
+
for (let i = 0; i < authors.length; i++) parts.push(toStr(authors.get(i)));
|
|
132081
|
+
author = parts.filter(Boolean).join(", ");
|
|
132082
|
+
}
|
|
132083
|
+
} catch {
|
|
132084
|
+
}
|
|
132085
|
+
}
|
|
132086
|
+
if (!createdAt) {
|
|
132087
|
+
const ts = toStr(fileName.time_stamp); // already a string like "2023-08-10T12:34:56"
|
|
132088
|
+
if (ts) {
|
|
132089
|
+
// normalize to ISO if possible
|
|
132090
|
+
const maybe = new Date(ts);
|
|
132091
|
+
if (!isNaN(maybe.getTime())) createdAt = maybe.toISOString();
|
|
132092
|
+
}
|
|
132093
|
+
}
|
|
132094
|
+
if (!creatingApplication) {
|
|
132095
|
+
// STEP header carries "originating_system" and "preprocessor_version"
|
|
132096
|
+
const orig = toStr(fileName.originating_system);
|
|
132097
|
+
const prep = toStr(fileName.preprocessor_version);
|
|
132098
|
+
creatingApplication = [orig, prep].filter(Boolean).join(" / ");
|
|
132099
|
+
}
|
|
132100
|
+
} catch {
|
|
132101
|
+
}
|
|
132102
|
+
|
|
132103
|
+
// If createdAt still missing, sweep for earliest OwnerHistory timestamp across roots
|
|
132104
|
+
if (!createdAt) {
|
|
132105
|
+
try {
|
|
132106
|
+
let minTs = Infinity;
|
|
132107
|
+
const roots = ifc.by_type("IfcRoot");
|
|
132108
|
+
for (let i = 0; i < roots.length; i++) {
|
|
132109
|
+
const r = roots.get(i);
|
|
132110
|
+
const oh = r?.OwnerHistory;
|
|
132111
|
+
const ts = oh?.CreationDate;
|
|
132112
|
+
if (typeof ts === "number" && isFinite(ts) && ts > 0 && ts < minTs) {
|
|
132113
|
+
minTs = ts;
|
|
132114
|
+
}
|
|
132115
|
+
r.destroy?.();
|
|
132116
|
+
}
|
|
132117
|
+
roots.destroy?.();
|
|
132118
|
+
if (isFinite(minTs)) createdAt = new Date(minTs * 1000).toISOString();
|
|
132119
|
+
} catch {
|
|
132120
|
+
}
|
|
132121
|
+
}
|
|
132122
|
+
|
|
132123
|
+
// ---- hierarchy walk ----------------------------------------------------
|
|
132124
|
+
// Re-query projects since we destroyed the first pointer above
|
|
132125
|
+
const projects2 = ifc.by_type("IfcProject");
|
|
132126
|
+
for (let i = 0; i < projects2.length; i++) {
|
|
132127
|
+
const project = projects2.get(i);
|
|
132128
|
+
walk(project, null);
|
|
132129
|
+
project.destroy?.();
|
|
132130
|
+
}
|
|
132131
|
+
projects2.destroy?.();
|
|
132132
|
+
|
|
132133
|
+
return {
|
|
132134
|
+
id: "",
|
|
132135
|
+
projectId,
|
|
132136
|
+
author,
|
|
132137
|
+
createdAt,
|
|
132138
|
+
schema,
|
|
132139
|
+
creatingApplication,
|
|
132140
|
+
metaObjects,
|
|
132141
|
+
propertySets
|
|
132142
|
+
};
|
|
132143
|
+
}
|
|
132144
|
+
|
|
132145
|
+
/**
|
|
132146
|
+
* Destroys this IFCOpenShellLoaderPlugin instance.
|
|
132147
|
+
*/
|
|
132148
|
+
destroy() {
|
|
132149
|
+
super.destroy();
|
|
132150
|
+
}
|
|
132151
|
+
}
|
|
132152
|
+
|
|
132153
|
+
function makeSubGeometryId(geometry_id, matIndex) {
|
|
132154
|
+
return `${geometry_id}:${matIndex}#geom`;
|
|
132155
|
+
}
|
|
132156
|
+
|
|
132157
|
+
function buildMaterialMapping(materialIds) {
|
|
132158
|
+
const mapping = {};
|
|
132159
|
+
for (let faceIdx = 0; faceIdx < materialIds.length; faceIdx++) {
|
|
132160
|
+
const materialId = materialIds[faceIdx];
|
|
132161
|
+
if (materialId == null || materialId < 0) continue; // skip invalid / missing
|
|
132162
|
+
(mapping[materialId] ||= []).push(faceIdx);
|
|
132163
|
+
}
|
|
132164
|
+
return mapping;
|
|
132165
|
+
}
|
|
132166
|
+
|
|
132167
|
+
function buildIndicesForFaces(faceList, faceIndices) {
|
|
132168
|
+
const indices = new Uint32Array(faceList.length * 3);
|
|
132169
|
+
let k = 0;
|
|
132170
|
+
for (const faceIdx of faceList) {
|
|
132171
|
+
const base = faceIdx * 3;
|
|
132172
|
+
indices[k++] = faceIndices[base + 0];
|
|
132173
|
+
indices[k++] = faceIndices[base + 1];
|
|
132174
|
+
indices[k++] = faceIndices[base + 2];
|
|
132175
|
+
}
|
|
132176
|
+
return indices;
|
|
132177
|
+
}
|
|
132178
|
+
|
|
132179
|
+
function generateUUID() {
|
|
132180
|
+
return Math.random().toString(36).substr(2, 9);
|
|
132181
|
+
}
|
|
132182
|
+
|
|
132183
|
+
function extractRTCTransform(transform) {
|
|
132184
|
+
const matrix = flattenMatrixArray(transform);
|
|
132185
|
+
const origin = [];
|
|
132186
|
+
const worldOrigin = matrix.slice(12, 15); // translation xyz
|
|
132187
|
+
worldToRTCPositions(worldOrigin, worldOrigin, origin);
|
|
132188
|
+
matrix.set(worldOrigin, 12);
|
|
132189
|
+
return {origin, matrix};
|
|
132190
|
+
}
|
|
132191
|
+
|
|
132192
|
+
function flattenMatrixArray(m) {
|
|
132193
|
+
return new Float64Array([
|
|
132194
|
+
m[0][0], m[2][0], -m[1][0], m[3][0],
|
|
132195
|
+
m[0][1], m[2][1], -m[1][1], m[3][1],
|
|
132196
|
+
m[0][2], m[2][2], -m[1][2], m[3][2],
|
|
132197
|
+
m[0][3], m[2][3], -m[1][3], m[3][3]
|
|
132198
|
+
]);
|
|
132199
|
+
}
|
|
132200
|
+
|
|
131079
132201
|
/**
|
|
131080
132202
|
* Default data access strategy for {@link LASLoaderPlugin}.
|
|
131081
132203
|
*/
|
|
@@ -135166,6 +136288,8 @@ exports.GIFMediaType = GIFMediaType;
|
|
|
135166
136288
|
exports.GLTFDefaultDataSource = GLTFDefaultDataSource;
|
|
135167
136289
|
exports.GLTFLoaderPlugin = GLTFLoaderPlugin;
|
|
135168
136290
|
exports.HalfFloatType = HalfFloatType;
|
|
136291
|
+
exports.IFCOpenShellDefaultDataSource = IFCOpenShellDefaultDataSource;
|
|
136292
|
+
exports.IFCOpenShellLoaderPlugin = IFCOpenShellLoaderPlugin;
|
|
135169
136293
|
exports.ImagePlane = ImagePlane;
|
|
135170
136294
|
exports.IntType = IntType;
|
|
135171
136295
|
exports.JPEGMediaType = JPEGMediaType;
|