fl-web-component 2.1.1-beta.2 → 2.1.1-beta.21
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/fl-web-component.common.js +11172 -951
- package/dist/fl-web-component.common.js.map +1 -1
- package/dist/fl-web-component.css +1 -1
- package/package.json +1 -1
- package/packages/components/com-flcanvas/components/entityFormatting.js +52 -43
- package/packages/components/com-flcanvas/index.vue +360 -326
- package/packages/components/com-graphics/index.vue +946 -164
- package/packages/utils/StreamLoader.js +1 -1
- package/packages/utils/StreamLoaderParser.worker.js +20 -20
- package/src/utils/instance-parser.js +69 -64
|
@@ -165,9 +165,15 @@ import { onContextHandle } from './component/context';
|
|
|
165
165
|
|
|
166
166
|
const isDebug = process.env.NODE_ENV !== 'production' || process.env.VUE_APP_IS_WATCH === true;
|
|
167
167
|
// const isDebug = false;
|
|
168
|
-
const
|
|
168
|
+
const COLLISION_CONTACT_EPSILON = 1;
|
|
169
169
|
const COLLISION_OBB_AXIS_EPSILON = 1e-8;
|
|
170
170
|
const COLLISION_DEBUG_GROUP_NAME = '__collision_obb_debug_group__';
|
|
171
|
+
const OCCLUSION_DEBUG_GROUP_NAME = '__occlusion_bounds_debug_group__';
|
|
172
|
+
const OCCLUSION_DEBUG_FLAG_COLORS = {
|
|
173
|
+
1: 0xffc107,
|
|
174
|
+
2: 0x409eff,
|
|
175
|
+
3: 0xff4dff,
|
|
176
|
+
};
|
|
171
177
|
|
|
172
178
|
export default {
|
|
173
179
|
name: 'FlModel',
|
|
@@ -215,7 +221,11 @@ export default {
|
|
|
215
221
|
isPaused: false,
|
|
216
222
|
},
|
|
217
223
|
isolateMode: false,
|
|
218
|
-
|
|
224
|
+
sceneBoundingBoxDebugEnabled: false,
|
|
225
|
+
// collisionObbDebugEnabled: isDebug,
|
|
226
|
+
collisionObbDebugEnabled: false,
|
|
227
|
+
// occlusionBoundsDebugEnabled: isDebug,
|
|
228
|
+
occlusionBoundsDebugEnabled: false,
|
|
219
229
|
};
|
|
220
230
|
},
|
|
221
231
|
watch: {
|
|
@@ -224,6 +234,13 @@ export default {
|
|
|
224
234
|
this.detachTransformControls();
|
|
225
235
|
}
|
|
226
236
|
},
|
|
237
|
+
sceneBoundingBoxDebugEnabled(val) {
|
|
238
|
+
if (val) {
|
|
239
|
+
this.refreshSceneBoundingBoxHelper(true);
|
|
240
|
+
} else {
|
|
241
|
+
this.hideSceneBoundingBox();
|
|
242
|
+
}
|
|
243
|
+
},
|
|
227
244
|
},
|
|
228
245
|
beforeCreate() {
|
|
229
246
|
this.spaceUp = true;
|
|
@@ -258,6 +275,7 @@ export default {
|
|
|
258
275
|
'sceneBoundingBox',
|
|
259
276
|
'sceneBoundingBoxHelper',
|
|
260
277
|
'collisionObbDebugGroup',
|
|
278
|
+
'occlusionBoundsDebugGroup',
|
|
261
279
|
];
|
|
262
280
|
arr.forEach(item => {
|
|
263
281
|
this[item] = null;
|
|
@@ -279,6 +297,7 @@ export default {
|
|
|
279
297
|
'isDragging',
|
|
280
298
|
'boundingBoxVisible',
|
|
281
299
|
'collisionObbDebugEnabled',
|
|
300
|
+
'occlusionBoundsDebugEnabled',
|
|
282
301
|
].forEach(item => {
|
|
283
302
|
this[item] = false;
|
|
284
303
|
});
|
|
@@ -360,11 +379,16 @@ export default {
|
|
|
360
379
|
wheelTimeout: null,
|
|
361
380
|
},
|
|
362
381
|
},
|
|
382
|
+
cameraControlMoveObserver: {
|
|
383
|
+
enabled: false,
|
|
384
|
+
interval: 500,
|
|
385
|
+
},
|
|
363
386
|
streamLoader: null,
|
|
364
387
|
// isPerformingInitialCentering: false, // 标记是否正在执行初始居中
|
|
365
388
|
isObserverEnabled: true, // 标记相机变更观察者是否启用
|
|
366
389
|
sceneBoxes: new Map(),
|
|
367
390
|
documentModelIds: new Map(),
|
|
391
|
+
documentSceneBoundsIds: new Map(),
|
|
368
392
|
outlineInstanceProxyMap: new Map(),
|
|
369
393
|
occlusionWorker: null,
|
|
370
394
|
occlusionWorkerRequestMap: new Map(),
|
|
@@ -1204,6 +1228,28 @@ export default {
|
|
|
1204
1228
|
if (instancedMesh && instancedMesh.userData) return instancedMesh.userData.copyMatrix || null;
|
|
1205
1229
|
return null;
|
|
1206
1230
|
},
|
|
1231
|
+
setInstanceCullingVisible(instancedMesh, instanceId, visible) {
|
|
1232
|
+
const instanceState = this.getInstanceState(instancedMesh, instanceId);
|
|
1233
|
+
if (!instanceState || typeof instanceState.instanceIndex !== 'number') return;
|
|
1234
|
+
|
|
1235
|
+
const copyMatrix =
|
|
1236
|
+
instanceState.copyMatrix || this.getInstanceCopyMatrix(instancedMesh, instanceId);
|
|
1237
|
+
if (!copyMatrix) return;
|
|
1238
|
+
|
|
1239
|
+
const shouldShow =
|
|
1240
|
+
visible &&
|
|
1241
|
+
instanceState.visible !== false &&
|
|
1242
|
+
this.isSourceVisible(instanceId, instancedMesh);
|
|
1243
|
+
const matrix = new this.THREE.Matrix4().copy(copyMatrix);
|
|
1244
|
+
if (!shouldShow) {
|
|
1245
|
+
matrix.makeTranslation(9999999, 9999999, 9999999);
|
|
1246
|
+
this.removeOutlineObject(instancedMesh, instanceState.instanceIndex);
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
instancedMesh.setMatrixAt(instanceState.instanceIndex, matrix);
|
|
1250
|
+
instancedMesh.instanceMatrix.needsUpdate = true;
|
|
1251
|
+
instanceState.cullingVisible = !!visible;
|
|
1252
|
+
},
|
|
1207
1253
|
isSourceHiddenInstance(instancedMesh, instanceId) {
|
|
1208
1254
|
const instanceState = this.getInstanceState(instancedMesh, instanceId);
|
|
1209
1255
|
if (instanceState && instanceState.sourceVisible !== undefined) {
|
|
@@ -1469,71 +1515,178 @@ export default {
|
|
|
1469
1515
|
updateGlobalSceneBoundingBox() {
|
|
1470
1516
|
const state = this.noObserver || {};
|
|
1471
1517
|
const occScene = state.occlusionState && state.occlusionState._occScene;
|
|
1518
|
+
const mergedBox = new this.THREE.Box3();
|
|
1519
|
+
let hasValidBox = false;
|
|
1520
|
+
const mergeBox = box => {
|
|
1521
|
+
if (!box || !box.isBox3 || box.isEmpty()) return;
|
|
1522
|
+
if (!hasValidBox) {
|
|
1523
|
+
mergedBox.copy(box);
|
|
1524
|
+
hasValidBox = true;
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
mergedBox.union(box);
|
|
1528
|
+
};
|
|
1472
1529
|
|
|
1473
|
-
//
|
|
1530
|
+
// 离屏遮挡场景只包含参与遮挡的模型,需要继续合并场景范围索引中的二维图形范围。
|
|
1474
1531
|
if (occScene && occScene.children && occScene.children.length > 0) {
|
|
1475
1532
|
const occBox = new this.THREE.Box3().setFromObject(occScene);
|
|
1476
|
-
|
|
1477
|
-
this.sceneBoundingBox = occBox;
|
|
1478
|
-
return this.sceneBoundingBox;
|
|
1479
|
-
}
|
|
1533
|
+
mergeBox(occBox);
|
|
1480
1534
|
}
|
|
1481
1535
|
|
|
1482
|
-
//
|
|
1483
|
-
if (this.
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
minZ = Infinity;
|
|
1487
|
-
let maxX = -Infinity,
|
|
1488
|
-
maxY = -Infinity,
|
|
1489
|
-
maxZ = -Infinity;
|
|
1490
|
-
this._boxIndex.forEach(box => {
|
|
1491
|
-
if (!box || !box.isBox3 || box.isEmpty()) return;
|
|
1492
|
-
const mn = box.min;
|
|
1493
|
-
const mx = box.max;
|
|
1494
|
-
if (mn.x < minX) minX = mn.x;
|
|
1495
|
-
if (mn.y < minY) minY = mn.y;
|
|
1496
|
-
if (mn.z < minZ) minZ = mn.z;
|
|
1497
|
-
if (mx.x > maxX) maxX = mx.x;
|
|
1498
|
-
if (mx.y > maxY) maxY = mx.y;
|
|
1499
|
-
if (mx.z > maxZ) maxZ = mx.z;
|
|
1536
|
+
// sceneBoundsIndex 包含二维和三维图形,是全场景 dolly 距离的主要范围来源。
|
|
1537
|
+
if (this._sceneBoundsIndex && this._sceneBoundsIndex.size > 0) {
|
|
1538
|
+
this._sceneBoundsIndex.forEach(box => {
|
|
1539
|
+
mergeBox(box);
|
|
1500
1540
|
});
|
|
1501
|
-
if (
|
|
1502
|
-
Number.isFinite(minX) &&
|
|
1503
|
-
Number.isFinite(minY) &&
|
|
1504
|
-
Number.isFinite(minZ) &&
|
|
1505
|
-
Number.isFinite(maxX) &&
|
|
1506
|
-
Number.isFinite(maxY) &&
|
|
1507
|
-
Number.isFinite(maxZ)
|
|
1508
|
-
) {
|
|
1509
|
-
this.sceneBoundingBox = new this.THREE.Box3(
|
|
1510
|
-
new this.THREE.Vector3(minX, minY, minZ),
|
|
1511
|
-
new this.THREE.Vector3(maxX, maxY, maxZ)
|
|
1512
|
-
);
|
|
1513
|
-
return this.sceneBoundingBox;
|
|
1514
|
-
}
|
|
1515
1541
|
}
|
|
1516
1542
|
|
|
1517
1543
|
const boxes = Array.from((state.sceneBoxes && state.sceneBoxes.values()) || []);
|
|
1518
1544
|
if (boxes.length > 0) {
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
firstBox.union(boxes[i]);
|
|
1545
|
+
for (let i = 0; i < boxes.length; i++) {
|
|
1546
|
+
mergeBox(boxes[i]);
|
|
1522
1547
|
}
|
|
1523
|
-
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
if (hasValidBox) {
|
|
1551
|
+
this.sceneBoundingBox = mergedBox;
|
|
1552
|
+
this.refreshSceneBoundingBoxHelper(this.sceneBoundingBoxDebugEnabled);
|
|
1524
1553
|
return this.sceneBoundingBox;
|
|
1525
1554
|
}
|
|
1526
1555
|
|
|
1527
1556
|
this.sceneBoundingBox = new this.THREE.Box3();
|
|
1557
|
+
this.refreshSceneBoundingBoxHelper(this.sceneBoundingBoxDebugEnabled);
|
|
1528
1558
|
return this.sceneBoundingBox;
|
|
1529
1559
|
},
|
|
1560
|
+
upsertIndexedBox(indexMap, modelId, box) {
|
|
1561
|
+
if (!indexMap || !modelId || !box || !box.isBox3) return null;
|
|
1562
|
+
const key = String(modelId);
|
|
1563
|
+
const existingBox = indexMap.get(key);
|
|
1564
|
+
if (!existingBox) {
|
|
1565
|
+
indexMap.set(key, box);
|
|
1566
|
+
return box;
|
|
1567
|
+
}
|
|
1568
|
+
existingBox.union(box);
|
|
1569
|
+
return existingBox;
|
|
1570
|
+
},
|
|
1571
|
+
createOcclusionProxyBox(box) {
|
|
1572
|
+
if (!box || !box.isBox3) return null;
|
|
1573
|
+
const proxyBox = box.clone();
|
|
1574
|
+
proxyBox.userData = { ...(box.userData || {}) };
|
|
1575
|
+
delete proxyBox.userData.occlusionProxyBoxes;
|
|
1576
|
+
return proxyBox;
|
|
1577
|
+
},
|
|
1578
|
+
mergeModelBoxUserData(targetBox, sourceBox) {
|
|
1579
|
+
if (!targetBox || !sourceBox || !sourceBox.userData) return;
|
|
1580
|
+
const sourceUserData = sourceBox.userData;
|
|
1581
|
+
if (!targetBox.userData) {
|
|
1582
|
+
targetBox.userData = sourceUserData;
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
const targetUserData = targetBox.userData;
|
|
1587
|
+
const targetProxyBoxes = Array.isArray(targetUserData.occlusionProxyBoxes)
|
|
1588
|
+
? targetUserData.occlusionProxyBoxes
|
|
1589
|
+
: [this.createOcclusionProxyBox(targetBox)].filter(Boolean);
|
|
1590
|
+
const sourceProxyBoxes = Array.isArray(sourceUserData.occlusionProxyBoxes)
|
|
1591
|
+
? sourceUserData.occlusionProxyBoxes
|
|
1592
|
+
: [this.createOcclusionProxyBox(sourceBox)].filter(Boolean);
|
|
1593
|
+
targetUserData.visible = targetUserData.visible !== false || sourceUserData.visible !== false;
|
|
1594
|
+
targetUserData.sourceVisible =
|
|
1595
|
+
targetUserData.sourceVisible !== false || sourceUserData.sourceVisible !== false;
|
|
1596
|
+
targetUserData.transparent = !!targetUserData.transparent && !!sourceUserData.transparent;
|
|
1597
|
+
targetUserData.mergedBoxCount = (targetUserData.mergedBoxCount || 1) + 1;
|
|
1598
|
+
// 多图元模型分别参与遮挡测试,任一图元可见时都会保留整个模型。
|
|
1599
|
+
targetUserData.occlusionEnabled =
|
|
1600
|
+
!!targetUserData.occlusionEnabled && !!sourceUserData.occlusionEnabled;
|
|
1601
|
+
targetUserData.occlusionProxyBoxes = targetProxyBoxes.concat(sourceProxyBoxes);
|
|
1602
|
+
|
|
1603
|
+
const targetObbs = Array.isArray(targetUserData.collisionObbs)
|
|
1604
|
+
? targetUserData.collisionObbs
|
|
1605
|
+
: [];
|
|
1606
|
+
const sourceObbs = Array.isArray(sourceUserData.collisionObbs)
|
|
1607
|
+
? sourceUserData.collisionObbs
|
|
1608
|
+
: [];
|
|
1609
|
+
if (sourceObbs.length > 0) {
|
|
1610
|
+
targetUserData.collisionObbs = targetObbs.concat(sourceObbs);
|
|
1611
|
+
}
|
|
1612
|
+
},
|
|
1613
|
+
_createCollisionObb(center, halfSize, rotation, matrix) {
|
|
1614
|
+
const worldCenter = center.clone();
|
|
1615
|
+
const worldHalfSize = halfSize.clone();
|
|
1616
|
+
const worldRotation = new this.THREE.Matrix3();
|
|
1617
|
+
|
|
1618
|
+
if (!matrix) {
|
|
1619
|
+
return new OBB(worldCenter, worldHalfSize, rotation.clone());
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
worldCenter.applyMatrix4(matrix);
|
|
1623
|
+
|
|
1624
|
+
const localAxes = [
|
|
1625
|
+
new this.THREE.Vector3(),
|
|
1626
|
+
new this.THREE.Vector3(),
|
|
1627
|
+
new this.THREE.Vector3(),
|
|
1628
|
+
];
|
|
1629
|
+
rotation.extractBasis(localAxes[0], localAxes[1], localAxes[2]);
|
|
1630
|
+
|
|
1631
|
+
const linearMatrix = new this.THREE.Matrix3().setFromMatrix4(matrix);
|
|
1632
|
+
const worldAxes = localAxes.map(axis => axis.clone().applyMatrix3(linearMatrix));
|
|
1633
|
+
const halfSizes = [halfSize.x, halfSize.y, halfSize.z];
|
|
1634
|
+
|
|
1635
|
+
for (let i = 0; i < 3; i++) {
|
|
1636
|
+
const axisLength = worldAxes[i].length();
|
|
1637
|
+
if (axisLength <= COLLISION_OBB_AXIS_EPSILON) {
|
|
1638
|
+
worldAxes[i].copy(localAxes[i]);
|
|
1639
|
+
continue;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
worldAxes[i].multiplyScalar(1 / axisLength);
|
|
1643
|
+
halfSizes[i] *= axisLength;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
worldHalfSize.set(halfSizes[0], halfSizes[1], halfSizes[2]);
|
|
1647
|
+
worldRotation.set(
|
|
1648
|
+
worldAxes[0].x,
|
|
1649
|
+
worldAxes[1].x,
|
|
1650
|
+
worldAxes[2].x,
|
|
1651
|
+
worldAxes[0].y,
|
|
1652
|
+
worldAxes[1].y,
|
|
1653
|
+
worldAxes[2].y,
|
|
1654
|
+
worldAxes[0].z,
|
|
1655
|
+
worldAxes[1].z,
|
|
1656
|
+
worldAxes[2].z
|
|
1657
|
+
);
|
|
1658
|
+
|
|
1659
|
+
return new OBB(worldCenter, worldHalfSize, worldRotation);
|
|
1660
|
+
},
|
|
1661
|
+
_createCollisionObbFromBox(box, matrix) {
|
|
1662
|
+
const center = box.getCenter(new this.THREE.Vector3());
|
|
1663
|
+
const halfSize = box.getSize(new this.THREE.Vector3()).multiplyScalar(0.5);
|
|
1664
|
+
const rotation = new this.THREE.Matrix3().identity();
|
|
1665
|
+
return this._createCollisionObb(center, halfSize, rotation, matrix);
|
|
1666
|
+
},
|
|
1667
|
+
upsertModelBox(modelId, box) {
|
|
1668
|
+
if (!this._boxIndex || !modelId || !box || !box.isBox3) return;
|
|
1669
|
+
const key = String(modelId);
|
|
1670
|
+
const existingBox = this._boxIndex.get(key);
|
|
1671
|
+
if (!existingBox) {
|
|
1672
|
+
this._boxIndex.set(key, box);
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
this.mergeModelBoxUserData(existingBox, box);
|
|
1676
|
+
existingBox.union(box);
|
|
1677
|
+
},
|
|
1530
1678
|
setBoxIndex(boxJson, documentId, isAdd = true) {
|
|
1531
1679
|
if (!this._boxIndex) this._boxIndex = new Map();
|
|
1680
|
+
if (!this._sceneBoundsIndex) this._sceneBoundsIndex = new Map();
|
|
1532
1681
|
if (!documentId) {
|
|
1533
1682
|
this._boxIndex.clear();
|
|
1683
|
+
this._sceneBoundsIndex.clear();
|
|
1534
1684
|
if (this.noObserver && this.noObserver.documentModelIds) {
|
|
1535
1685
|
this.noObserver.documentModelIds.clear();
|
|
1536
1686
|
}
|
|
1687
|
+
if (this.noObserver && this.noObserver.documentSceneBoundsIds) {
|
|
1688
|
+
this.noObserver.documentSceneBoundsIds.clear();
|
|
1689
|
+
}
|
|
1537
1690
|
this.hasExecutedCentering = false;
|
|
1538
1691
|
this.buildOctreeFromBoxIndex();
|
|
1539
1692
|
return;
|
|
@@ -1542,32 +1695,50 @@ export default {
|
|
|
1542
1695
|
if (isAdd && boxJson) {
|
|
1543
1696
|
const arr = boxJson ? boxJson : [];
|
|
1544
1697
|
const modelIds = new Set();
|
|
1698
|
+
const sceneBoundIds = new Set();
|
|
1545
1699
|
const validGeomTypes = new Set(Object.values(GEOM_TYPES));
|
|
1546
1700
|
for (let i = 0; i < arr.length; i++) {
|
|
1547
1701
|
const it = arr[i];
|
|
1548
|
-
|
|
1702
|
+
const tempGeomType = it.geomType || it.geom_type;
|
|
1703
|
+
if (!validGeomTypes.has(tempGeomType)) {
|
|
1549
1704
|
continue;
|
|
1550
1705
|
}
|
|
1551
|
-
|
|
1706
|
+
const minSource = it.min;
|
|
1707
|
+
const maxSource = it.max;
|
|
1708
|
+
const hasValidMinMax =
|
|
1709
|
+
minSource &&
|
|
1710
|
+
maxSource &&
|
|
1711
|
+
minSource.length >= 3 &&
|
|
1712
|
+
maxSource.length >= 3 &&
|
|
1713
|
+
Number.isFinite(minSource[0]) &&
|
|
1714
|
+
Number.isFinite(minSource[1]) &&
|
|
1715
|
+
Number.isFinite(minSource[2]) &&
|
|
1716
|
+
Number.isFinite(maxSource[0]) &&
|
|
1717
|
+
Number.isFinite(maxSource[1]) &&
|
|
1718
|
+
Number.isFinite(maxSource[2]);
|
|
1719
|
+
if (!hasValidMinMax) {
|
|
1552
1720
|
continue;
|
|
1553
1721
|
}
|
|
1722
|
+
const obbData = it.obb || [];
|
|
1723
|
+
const modelId = String(it.id);
|
|
1554
1724
|
|
|
1555
1725
|
// 使用新的 min/max 数据结构
|
|
1556
|
-
const min = new this.THREE.Vector3(
|
|
1557
|
-
const max = new this.THREE.Vector3(
|
|
1726
|
+
const min = new this.THREE.Vector3(minSource[0], minSource[1], minSource[2]);
|
|
1727
|
+
const max = new this.THREE.Vector3(maxSource[0], maxSource[1], maxSource[2]);
|
|
1558
1728
|
|
|
1559
1729
|
const sourceBox = new this.THREE.Box3(min.clone(), max.clone());
|
|
1560
1730
|
// 八叉树仍使用 AABB 粗筛,最终碰撞使用 sourceBox 派生的 OBB 精筛
|
|
1561
1731
|
const boxThree = sourceBox.clone();
|
|
1562
1732
|
|
|
1733
|
+
const tempMeshMatrix = it.meshMatrix || it.mesh_matrix;
|
|
1563
1734
|
const hasMatrix = Array.isArray(it.matrix) && it.matrix.length >= 16;
|
|
1564
|
-
const hasMeshMatrix = Array.isArray(
|
|
1735
|
+
const hasMeshMatrix = Array.isArray(tempMeshMatrix) && tempMeshMatrix.length >= 16;
|
|
1565
1736
|
let localMatrix = null;
|
|
1566
1737
|
let collisionMatrix = null;
|
|
1567
1738
|
if (hasMatrix || hasMeshMatrix) {
|
|
1568
1739
|
localMatrix = new this.THREE.Matrix4();
|
|
1569
1740
|
if (hasMeshMatrix) {
|
|
1570
|
-
localMatrix.fromArray(
|
|
1741
|
+
localMatrix.fromArray(tempMeshMatrix);
|
|
1571
1742
|
} else {
|
|
1572
1743
|
localMatrix.identity();
|
|
1573
1744
|
}
|
|
@@ -1588,25 +1759,32 @@ export default {
|
|
|
1588
1759
|
boxThree.applyMatrix4(collisionMatrix);
|
|
1589
1760
|
}
|
|
1590
1761
|
|
|
1762
|
+
this.upsertIndexedBox(this._sceneBoundsIndex, modelId, boxThree.clone());
|
|
1763
|
+
sceneBoundIds.add(modelId);
|
|
1764
|
+
|
|
1765
|
+
// 没有 OBB 数据的二维图形只参与全场景范围,不纳入碰撞和遮挡索引。
|
|
1766
|
+
if (obbData.length === 0) {
|
|
1767
|
+
continue;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1591
1770
|
const userData = {
|
|
1592
1771
|
flag: it.flag || 1, // 默认为 1
|
|
1593
|
-
geomType:
|
|
1594
|
-
obbData
|
|
1772
|
+
geomType: tempGeomType,
|
|
1773
|
+
obbData, // 存储原始 OBB 数据
|
|
1595
1774
|
transparent: it.transp > 0,
|
|
1596
1775
|
sourceVisible: it.visible === false ? false : true,
|
|
1597
1776
|
visible: it.visible === false ? false : true, // 默认显示
|
|
1598
1777
|
occlusionEnabled:
|
|
1599
|
-
|
|
1778
|
+
tempGeomType === GEOM_TYPES.geom_3d || tempGeomType === GEOM_TYPES.geom_3d_obj,
|
|
1600
1779
|
};
|
|
1601
1780
|
if (it.flag === 1) {
|
|
1602
1781
|
userData.indices = it.indices;
|
|
1603
1782
|
userData.matrix = localMatrix ? localMatrix.toArray() : it.matrix;
|
|
1604
|
-
userData.meshMatrix =
|
|
1783
|
+
userData.meshMatrix = tempMeshMatrix;
|
|
1605
1784
|
// userData.matrix = new this.THREE.Matrix4().identity();
|
|
1606
1785
|
}
|
|
1607
1786
|
// 如果是 flag=3,解析并存储中心点、半轴长、旋转矩阵
|
|
1608
|
-
else if (it.flag === 3 &&
|
|
1609
|
-
const obbData = it.obb;
|
|
1787
|
+
else if (it.flag === 3 && obbData.length === 15) {
|
|
1610
1788
|
const center = new this.THREE.Vector3(obbData[0], obbData[1], obbData[2]);
|
|
1611
1789
|
const halfSize = new this.THREE.Vector3(obbData[3], obbData[4], obbData[5]);
|
|
1612
1790
|
const rotation = new this.THREE.Matrix3().fromArray(obbData.slice(6, 15));
|
|
@@ -1629,27 +1807,26 @@ export default {
|
|
|
1629
1807
|
halfSize: halfSize,
|
|
1630
1808
|
};
|
|
1631
1809
|
|
|
1632
|
-
const collisionObb =
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1810
|
+
const collisionObb = this._createCollisionObb(
|
|
1811
|
+
center,
|
|
1812
|
+
halfSize,
|
|
1813
|
+
rotation,
|
|
1814
|
+
collisionMatrix
|
|
1815
|
+
);
|
|
1636
1816
|
userData.collisionObbs = [collisionObb];
|
|
1637
1817
|
}
|
|
1638
1818
|
// 如果是 flag=2,解析并存储多个子包围盒 (min, max)
|
|
1639
|
-
else if (it.flag === 2 &&
|
|
1819
|
+
else if (it.flag === 2 && obbData.length > 0) {
|
|
1640
1820
|
const subBoxes = [];
|
|
1641
1821
|
const collisionObbs = [];
|
|
1642
|
-
for (let k = 0; k <
|
|
1643
|
-
if (k + 5 <
|
|
1644
|
-
const min = new this.THREE.Vector3(
|
|
1645
|
-
const max = new this.THREE.Vector3(
|
|
1822
|
+
for (let k = 0; k < obbData.length; k += 6) {
|
|
1823
|
+
if (k + 5 < obbData.length) {
|
|
1824
|
+
const min = new this.THREE.Vector3(obbData[k], obbData[k + 1], obbData[k + 2]);
|
|
1825
|
+
const max = new this.THREE.Vector3(obbData[k + 3], obbData[k + 4], obbData[k + 5]);
|
|
1646
1826
|
subBoxes.push({ min, max });
|
|
1647
1827
|
|
|
1648
1828
|
const subBox = new this.THREE.Box3(min.clone(), max.clone());
|
|
1649
|
-
const collisionObb =
|
|
1650
|
-
if (collisionMatrix) {
|
|
1651
|
-
collisionObb.applyMatrix4(collisionMatrix);
|
|
1652
|
-
}
|
|
1829
|
+
const collisionObb = this._createCollisionObbFromBox(subBox, collisionMatrix);
|
|
1653
1830
|
collisionObbs.push(collisionObb);
|
|
1654
1831
|
}
|
|
1655
1832
|
}
|
|
@@ -1660,20 +1837,17 @@ export default {
|
|
|
1660
1837
|
}
|
|
1661
1838
|
|
|
1662
1839
|
if (!userData.collisionObbs || userData.collisionObbs.length === 0) {
|
|
1663
|
-
const collisionObb =
|
|
1664
|
-
if (collisionMatrix) {
|
|
1665
|
-
collisionObb.applyMatrix4(collisionMatrix);
|
|
1666
|
-
}
|
|
1840
|
+
const collisionObb = this._createCollisionObbFromBox(sourceBox, collisionMatrix);
|
|
1667
1841
|
userData.collisionObbs = [collisionObb];
|
|
1668
1842
|
}
|
|
1669
1843
|
|
|
1670
1844
|
boxThree.userData = userData;
|
|
1671
1845
|
|
|
1672
|
-
|
|
1673
|
-
this._boxIndex.set(modelId, boxThree);
|
|
1846
|
+
this.upsertModelBox(modelId, boxThree);
|
|
1674
1847
|
modelIds.add(modelId);
|
|
1675
1848
|
}
|
|
1676
1849
|
this.noObserver.documentModelIds.set(documentId, modelIds);
|
|
1850
|
+
this.noObserver.documentSceneBoundsIds.set(documentId, sceneBoundIds);
|
|
1677
1851
|
} else {
|
|
1678
1852
|
const modelIds = this.noObserver.documentModelIds.get(documentId);
|
|
1679
1853
|
if (modelIds) {
|
|
@@ -1687,14 +1861,38 @@ export default {
|
|
|
1687
1861
|
this.hasExecutedCentering = false;
|
|
1688
1862
|
}
|
|
1689
1863
|
}
|
|
1864
|
+
const sceneBoundIds =
|
|
1865
|
+
this.noObserver.documentSceneBoundsIds &&
|
|
1866
|
+
this.noObserver.documentSceneBoundsIds.get(documentId);
|
|
1867
|
+
if (sceneBoundIds) {
|
|
1868
|
+
sceneBoundIds.forEach(id => {
|
|
1869
|
+
this._sceneBoundsIndex.delete(id);
|
|
1870
|
+
});
|
|
1871
|
+
this.noObserver.documentSceneBoundsIds.delete(documentId);
|
|
1872
|
+
}
|
|
1690
1873
|
}
|
|
1691
1874
|
this.buildOctreeFromBoxIndex();
|
|
1692
1875
|
this.refreshCollisionObbDebug();
|
|
1876
|
+
this.refreshOcclusionBoundsDebug();
|
|
1693
1877
|
console.log('time end', Date.now());
|
|
1694
1878
|
},
|
|
1695
1879
|
shouldApplyOcclusionByBox(box) {
|
|
1696
1880
|
return !!(box && box.userData && box.userData.occlusionEnabled);
|
|
1697
1881
|
},
|
|
1882
|
+
appendOcclusionCandidates(candidates, modelId, box, frustum) {
|
|
1883
|
+
if (!box) return;
|
|
1884
|
+
const userData = box.userData || {};
|
|
1885
|
+
const proxyBoxes = Array.isArray(userData.occlusionProxyBoxes)
|
|
1886
|
+
? userData.occlusionProxyBoxes
|
|
1887
|
+
: [box];
|
|
1888
|
+
|
|
1889
|
+
for (let i = 0; i < proxyBoxes.length; i++) {
|
|
1890
|
+
const proxyBox = proxyBoxes[i];
|
|
1891
|
+
if (!proxyBox || (proxyBox.userData && proxyBox.userData.visible === false)) continue;
|
|
1892
|
+
if (frustum && !frustum.intersectsBox(proxyBox)) continue;
|
|
1893
|
+
candidates.push({ modelId, box: proxyBox });
|
|
1894
|
+
}
|
|
1895
|
+
},
|
|
1698
1896
|
tryInitialCenterAfterBoundsReady() {
|
|
1699
1897
|
if (this.hasExecutedCentering || this.userInteracting) return;
|
|
1700
1898
|
if (
|
|
@@ -1703,7 +1901,9 @@ export default {
|
|
|
1703
1901
|
this.sceneBoundingBox.isEmpty()
|
|
1704
1902
|
)
|
|
1705
1903
|
return;
|
|
1706
|
-
|
|
1904
|
+
const hasBoxIndex = this._boxIndex && this._boxIndex.size > 0;
|
|
1905
|
+
const hasSceneBoundsIndex = this._sceneBoundsIndex && this._sceneBoundsIndex.size > 0;
|
|
1906
|
+
if (!hasBoxIndex && !hasSceneBoundsIndex) return;
|
|
1707
1907
|
|
|
1708
1908
|
this.smartModelCenter(this.sceneBoundingBox, 0);
|
|
1709
1909
|
},
|
|
@@ -1805,6 +2005,53 @@ export default {
|
|
|
1805
2005
|
}
|
|
1806
2006
|
node.items.push({ id, box });
|
|
1807
2007
|
},
|
|
2008
|
+
_buildCollisionOctreeFromEntries(entries) {
|
|
2009
|
+
if (!Array.isArray(entries) || entries.length === 0) {
|
|
2010
|
+
return null;
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
let minX = Infinity;
|
|
2014
|
+
let minY = Infinity;
|
|
2015
|
+
let minZ = Infinity;
|
|
2016
|
+
let maxX = -Infinity;
|
|
2017
|
+
let maxY = -Infinity;
|
|
2018
|
+
let maxZ = -Infinity;
|
|
2019
|
+
|
|
2020
|
+
entries.forEach(item => {
|
|
2021
|
+
const box = item && item.box;
|
|
2022
|
+
if (!box || !box.isBox3) return;
|
|
2023
|
+
const mn = box.min;
|
|
2024
|
+
const mx = box.max;
|
|
2025
|
+
if (mn.x < minX) minX = mn.x;
|
|
2026
|
+
if (mn.y < minY) minY = mn.y;
|
|
2027
|
+
if (mn.z < minZ) minZ = mn.z;
|
|
2028
|
+
if (mx.x > maxX) maxX = mx.x;
|
|
2029
|
+
if (mx.y > maxY) maxY = mx.y;
|
|
2030
|
+
if (mx.z > maxZ) maxZ = mx.z;
|
|
2031
|
+
});
|
|
2032
|
+
|
|
2033
|
+
if (minX === Infinity) {
|
|
2034
|
+
return null;
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
const rootBox = new this.THREE.Box3(
|
|
2038
|
+
new this.THREE.Vector3(minX, minY, minZ),
|
|
2039
|
+
new this.THREE.Vector3(maxX, maxY, maxZ)
|
|
2040
|
+
);
|
|
2041
|
+
const root = {
|
|
2042
|
+
box: rootBox,
|
|
2043
|
+
items: [],
|
|
2044
|
+
children: null,
|
|
2045
|
+
depth: 0,
|
|
2046
|
+
};
|
|
2047
|
+
|
|
2048
|
+
entries.forEach(item => {
|
|
2049
|
+
if (!item || !item.box) return;
|
|
2050
|
+
this._octreeInsert(root, String(item.id), item.box);
|
|
2051
|
+
});
|
|
2052
|
+
|
|
2053
|
+
return root;
|
|
2054
|
+
},
|
|
1808
2055
|
_isCollisionBoxAvailable(box) {
|
|
1809
2056
|
if (!box || !box.isBox3 || box.isEmpty()) return false;
|
|
1810
2057
|
if (box.userData && box.userData.visible === false) return false;
|
|
@@ -1935,7 +2182,8 @@ export default {
|
|
|
1935
2182
|
_buildCollisionPairMap() {
|
|
1936
2183
|
const pairMap = new Map();
|
|
1937
2184
|
const collisionPairs = this.getOctreeCollisionPairs();
|
|
1938
|
-
const pairs =
|
|
2185
|
+
const pairs =
|
|
2186
|
+
collisionPairs && Array.isArray(collisionPairs.pairs) ? collisionPairs.pairs : [];
|
|
1939
2187
|
for (let i = 0; i < pairs.length; i++) {
|
|
1940
2188
|
const pair = pairs[i];
|
|
1941
2189
|
if (!pair) continue;
|
|
@@ -1956,20 +2204,15 @@ export default {
|
|
|
1956
2204
|
|
|
1957
2205
|
this._clearCollisionObbDebugGroup(false);
|
|
1958
2206
|
const pairMap = this._buildCollisionPairMap();
|
|
1959
|
-
if (
|
|
2207
|
+
if (!this._boxIndex || this._boxIndex.size === 0) return;
|
|
1960
2208
|
|
|
1961
|
-
|
|
1962
|
-
const box =
|
|
1963
|
-
this._boxIndex && this._boxIndex.has(String(modelId))
|
|
1964
|
-
? this._boxIndex.get(String(modelId))
|
|
1965
|
-
: this._boxIndex && this._boxIndex.has(modelId)
|
|
1966
|
-
? this._boxIndex.get(modelId)
|
|
1967
|
-
: null;
|
|
2209
|
+
this._boxIndex.forEach((box, modelId) => {
|
|
1968
2210
|
if (!this._isCollisionBoxAvailable(box)) return;
|
|
1969
2211
|
|
|
1970
2212
|
const obbs = this._getCollisionObbs(box);
|
|
1971
2213
|
if (!obbs.length) return;
|
|
1972
2214
|
|
|
2215
|
+
const relatedIds = pairMap.get(String(modelId));
|
|
1973
2216
|
const isColliding = relatedIds && relatedIds.size > 0;
|
|
1974
2217
|
const color = isColliding ? 0xff3b30 : 0x36cfc9;
|
|
1975
2218
|
for (let i = 0; i < obbs.length; i++) {
|
|
@@ -1995,6 +2238,165 @@ export default {
|
|
|
1995
2238
|
this.hideCollisionObbDebug();
|
|
1996
2239
|
}
|
|
1997
2240
|
},
|
|
2241
|
+
_getOcclusionDebugGroup() {
|
|
2242
|
+
if (!this.scene) return null;
|
|
2243
|
+
if (this.occlusionBoundsDebugGroup && this.occlusionBoundsDebugGroup.parent === this.scene) {
|
|
2244
|
+
return this.occlusionBoundsDebugGroup;
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
const group = new this.THREE.Group();
|
|
2248
|
+
group.name = OCCLUSION_DEBUG_GROUP_NAME;
|
|
2249
|
+
group.userData.transformControlHelper = true;
|
|
2250
|
+
group.userData.occlusionBoundsDebugHelper = true;
|
|
2251
|
+
this.scene.add(group);
|
|
2252
|
+
this.occlusionBoundsDebugGroup = group;
|
|
2253
|
+
return group;
|
|
2254
|
+
},
|
|
2255
|
+
_createOcclusionFlag1LineSegments(box, color) {
|
|
2256
|
+
const userData = box && box.userData ? box.userData : {};
|
|
2257
|
+
const vertices = userData.obbData;
|
|
2258
|
+
if (!vertices || vertices.length < 9) return null;
|
|
2259
|
+
|
|
2260
|
+
const sourceGeometry = new this.THREE.BufferGeometry();
|
|
2261
|
+
sourceGeometry.setAttribute(
|
|
2262
|
+
'position',
|
|
2263
|
+
new this.THREE.BufferAttribute(new Float32Array(vertices), 3)
|
|
2264
|
+
);
|
|
2265
|
+
|
|
2266
|
+
const indices = userData.indices;
|
|
2267
|
+
if (indices && indices.length >= 3) {
|
|
2268
|
+
if (indices.isBufferAttribute) {
|
|
2269
|
+
sourceGeometry.setIndex(indices);
|
|
2270
|
+
} else if (ArrayBuffer.isView(indices)) {
|
|
2271
|
+
sourceGeometry.setIndex(new this.THREE.BufferAttribute(indices, 1));
|
|
2272
|
+
} else {
|
|
2273
|
+
sourceGeometry.setIndex(indices);
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
let wireframeGeometry = null;
|
|
2278
|
+
try {
|
|
2279
|
+
// flag=1 对应真实遮挡网格,必须按顶点和三角形索引绘制,不能退化成 AABB/OBB。
|
|
2280
|
+
wireframeGeometry = new this.THREE.WireframeGeometry(sourceGeometry);
|
|
2281
|
+
} catch (error) {
|
|
2282
|
+
console.warn('flag=1 遮挡调试线框生成失败', error);
|
|
2283
|
+
}
|
|
2284
|
+
sourceGeometry.dispose();
|
|
2285
|
+
if (!wireframeGeometry || wireframeGeometry.getAttribute('position').count === 0) {
|
|
2286
|
+
if (wireframeGeometry) wireframeGeometry.dispose();
|
|
2287
|
+
return null;
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
const material = new this.THREE.LineBasicMaterial({
|
|
2291
|
+
color,
|
|
2292
|
+
transparent: true,
|
|
2293
|
+
opacity: 0.95,
|
|
2294
|
+
depthTest: false,
|
|
2295
|
+
depthWrite: false,
|
|
2296
|
+
toneMapped: false,
|
|
2297
|
+
});
|
|
2298
|
+
const lineSegments = new this.THREE.LineSegments(wireframeGeometry, material);
|
|
2299
|
+
const transformMatrix = new this.THREE.Matrix4();
|
|
2300
|
+
const matrixArr = userData.matrix;
|
|
2301
|
+
if (matrixArr && matrixArr.length >= 16) {
|
|
2302
|
+
transformMatrix.fromArray(matrixArr);
|
|
2303
|
+
}
|
|
2304
|
+
if (this.bizToThreeMatrix) {
|
|
2305
|
+
transformMatrix.premultiply(this.bizToThreeMatrix);
|
|
2306
|
+
}
|
|
2307
|
+
lineSegments.matrixAutoUpdate = false;
|
|
2308
|
+
lineSegments.matrix.copy(transformMatrix);
|
|
2309
|
+
lineSegments.matrixWorldNeedsUpdate = true;
|
|
2310
|
+
lineSegments.renderOrder = 998;
|
|
2311
|
+
lineSegments.userData.transformControlHelper = true;
|
|
2312
|
+
lineSegments.userData.occlusionBoundsDebugHelper = true;
|
|
2313
|
+
lineSegments.userData.occlusionFlag = 1;
|
|
2314
|
+
return lineSegments;
|
|
2315
|
+
},
|
|
2316
|
+
_createOcclusionBoxLineSegments(obb, color, flag) {
|
|
2317
|
+
const lineSegments = this._createCollisionObbLineSegments(obb, color);
|
|
2318
|
+
if (!lineSegments) return null;
|
|
2319
|
+
|
|
2320
|
+
lineSegments.renderOrder = 998;
|
|
2321
|
+
lineSegments.userData.collisionObbDebugHelper = false;
|
|
2322
|
+
lineSegments.userData.occlusionBoundsDebugHelper = true;
|
|
2323
|
+
lineSegments.userData.occlusionFlag = flag;
|
|
2324
|
+
return lineSegments;
|
|
2325
|
+
},
|
|
2326
|
+
_clearOcclusionBoundsDebugGroup(removeGroup = false) {
|
|
2327
|
+
const group = this.occlusionBoundsDebugGroup;
|
|
2328
|
+
if (!group) return;
|
|
2329
|
+
|
|
2330
|
+
while (group.children.length > 0) {
|
|
2331
|
+
const child = group.children[group.children.length - 1];
|
|
2332
|
+
group.remove(child);
|
|
2333
|
+
if (child.geometry) child.geometry.dispose();
|
|
2334
|
+
if (Array.isArray(child.material)) {
|
|
2335
|
+
child.material.forEach(material => material && material.dispose());
|
|
2336
|
+
} else if (child.material) {
|
|
2337
|
+
child.material.dispose();
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
if (removeGroup && this.scene && group.parent === this.scene) {
|
|
2342
|
+
this.scene.remove(group);
|
|
2343
|
+
this.occlusionBoundsDebugGroup = null;
|
|
2344
|
+
} else if (!this.scene) {
|
|
2345
|
+
this.occlusionBoundsDebugGroup = null;
|
|
2346
|
+
}
|
|
2347
|
+
},
|
|
2348
|
+
hideOcclusionBoundsDebug() {
|
|
2349
|
+
this._clearOcclusionBoundsDebugGroup();
|
|
2350
|
+
this.occlusionBoundsDebugEnabled = false;
|
|
2351
|
+
},
|
|
2352
|
+
refreshOcclusionBoundsDebug() {
|
|
2353
|
+
if (!this.occlusionBoundsDebugEnabled) return;
|
|
2354
|
+
const group = this._getOcclusionDebugGroup();
|
|
2355
|
+
if (!group) return;
|
|
2356
|
+
|
|
2357
|
+
this._clearOcclusionBoundsDebugGroup(false);
|
|
2358
|
+
if (!this._boxIndex || this._boxIndex.size === 0) return;
|
|
2359
|
+
|
|
2360
|
+
this._boxIndex.forEach((box, modelId) => {
|
|
2361
|
+
if (!this._isCollisionBoxAvailable(box) || !this.shouldApplyOcclusionByBox(box)) return;
|
|
2362
|
+
|
|
2363
|
+
const userData = box.userData || {};
|
|
2364
|
+
const flag = Number(userData.flag) || 1;
|
|
2365
|
+
const color = OCCLUSION_DEBUG_FLAG_COLORS[flag];
|
|
2366
|
+
if (!color) return;
|
|
2367
|
+
|
|
2368
|
+
if (flag === 1) {
|
|
2369
|
+
const lineSegments = this._createOcclusionFlag1LineSegments(box, color);
|
|
2370
|
+
if (!lineSegments) return;
|
|
2371
|
+
lineSegments.name = `${OCCLUSION_DEBUG_GROUP_NAME}:${modelId}:flag-1`;
|
|
2372
|
+
lineSegments.userData.modelId = modelId;
|
|
2373
|
+
group.add(lineSegments);
|
|
2374
|
+
return;
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
const obbs = this._getCollisionObbs(box);
|
|
2378
|
+
for (let i = 0; i < obbs.length; i++) {
|
|
2379
|
+
const lineSegments = this._createOcclusionBoxLineSegments(obbs[i], color, flag);
|
|
2380
|
+
if (!lineSegments) continue;
|
|
2381
|
+
lineSegments.name = `${OCCLUSION_DEBUG_GROUP_NAME}:${modelId}:flag-${flag}:${i}`;
|
|
2382
|
+
lineSegments.userData.modelId = modelId;
|
|
2383
|
+
group.add(lineSegments);
|
|
2384
|
+
}
|
|
2385
|
+
});
|
|
2386
|
+
},
|
|
2387
|
+
showOcclusionBoundsDebug() {
|
|
2388
|
+
this.occlusionBoundsDebugEnabled = true;
|
|
2389
|
+
this.refreshOcclusionBoundsDebug();
|
|
2390
|
+
},
|
|
2391
|
+
toggleOcclusionBoundsDebug(forceVisible) {
|
|
2392
|
+
const nextVisible =
|
|
2393
|
+
typeof forceVisible === 'boolean' ? forceVisible : !this.occlusionBoundsDebugEnabled;
|
|
2394
|
+
if (nextVisible) {
|
|
2395
|
+
this.showOcclusionBoundsDebug();
|
|
2396
|
+
} else {
|
|
2397
|
+
this.hideOcclusionBoundsDebug();
|
|
2398
|
+
}
|
|
2399
|
+
},
|
|
1998
2400
|
_getObbAxisInfo(obb) {
|
|
1999
2401
|
const xAxis = new this.THREE.Vector3();
|
|
2000
2402
|
const yAxis = new this.THREE.Vector3();
|
|
@@ -2064,16 +2466,15 @@ export default {
|
|
|
2064
2466
|
const obbsB = this._getCollisionObbs(boxB);
|
|
2065
2467
|
for (let i = 0; i < obbsA.length; i++) {
|
|
2066
2468
|
for (let j = 0; j < obbsB.length; j++) {
|
|
2067
|
-
if (this._getObbMinPenetration(obbsA[i], obbsB[j])
|
|
2469
|
+
if (this._getObbMinPenetration(obbsA[i], obbsB[j]) >= COLLISION_CONTACT_EPSILON) {
|
|
2068
2470
|
return true;
|
|
2069
2471
|
}
|
|
2070
2472
|
}
|
|
2071
2473
|
}
|
|
2072
2474
|
return false;
|
|
2073
2475
|
},
|
|
2074
|
-
|
|
2476
|
+
_queryOctreeByBoxWithRoot(root, box) {
|
|
2075
2477
|
const results = [];
|
|
2076
|
-
const root = this._octree;
|
|
2077
2478
|
if (!root || !this._isCollisionBoxAvailable(box)) return results;
|
|
2078
2479
|
|
|
2079
2480
|
const stack = [root];
|
|
@@ -2099,25 +2500,88 @@ export default {
|
|
|
2099
2500
|
|
|
2100
2501
|
return results;
|
|
2101
2502
|
},
|
|
2102
|
-
|
|
2503
|
+
_queryOctreeByBox(box) {
|
|
2504
|
+
return this._queryOctreeByBoxWithRoot(this._octree, box);
|
|
2505
|
+
},
|
|
2506
|
+
getOctreeCollisionPairs(options = {}) {
|
|
2103
2507
|
const pairs = [];
|
|
2104
2508
|
if (!this._boxIndex || this._boxIndex.size === 0) return { pairs };
|
|
2105
2509
|
|
|
2106
|
-
|
|
2107
|
-
|
|
2510
|
+
const normalizeIds = value =>
|
|
2511
|
+
Array.isArray(value)
|
|
2512
|
+
? Array.from(
|
|
2513
|
+
new Set(
|
|
2514
|
+
value
|
|
2515
|
+
.map(item => (item === null || item === undefined ? '' : String(item)))
|
|
2516
|
+
.filter(item => item !== '')
|
|
2517
|
+
)
|
|
2518
|
+
)
|
|
2519
|
+
: [];
|
|
2520
|
+
|
|
2521
|
+
const getEntriesByIds = ids => {
|
|
2522
|
+
const entries = [];
|
|
2523
|
+
const idSet = new Set(ids);
|
|
2524
|
+
this._boxIndex.forEach((box, id) => {
|
|
2525
|
+
const key = String(id);
|
|
2526
|
+
if (idSet.has(key)) {
|
|
2527
|
+
entries.push({
|
|
2528
|
+
id: key,
|
|
2529
|
+
box,
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2532
|
+
});
|
|
2533
|
+
return entries;
|
|
2534
|
+
};
|
|
2535
|
+
|
|
2536
|
+
const sourceIds = normalizeIds(options.sourceIds);
|
|
2537
|
+
const targetIds = normalizeIds(options.targetIds);
|
|
2538
|
+
const includeIds = Array.isArray(options.includeIds) ? normalizeIds(options.includeIds) : [];
|
|
2539
|
+
|
|
2540
|
+
let root = this._octree;
|
|
2541
|
+
let sourceEntries = [];
|
|
2542
|
+
let dedupePairKeySet = null;
|
|
2543
|
+
|
|
2544
|
+
if (sourceIds.length > 0 && targetIds.length > 0) {
|
|
2545
|
+
const targetEntries = getEntriesByIds(targetIds);
|
|
2546
|
+
sourceEntries = getEntriesByIds(sourceIds);
|
|
2547
|
+
if (sourceEntries.length === 0 || targetEntries.length === 0) return { pairs };
|
|
2548
|
+
root = this._buildCollisionOctreeFromEntries(targetEntries);
|
|
2549
|
+
if (!root) return { pairs };
|
|
2550
|
+
dedupePairKeySet = new Set();
|
|
2551
|
+
} else if (includeIds.length > 0) {
|
|
2552
|
+
sourceEntries = getEntriesByIds(includeIds);
|
|
2553
|
+
if (sourceEntries.length === 0) return { pairs };
|
|
2554
|
+
root = this._buildCollisionOctreeFromEntries(sourceEntries);
|
|
2555
|
+
if (!root) return { pairs };
|
|
2556
|
+
} else {
|
|
2557
|
+
if (!this._octree) {
|
|
2558
|
+
this.buildOctreeFromBoxIndex();
|
|
2559
|
+
}
|
|
2560
|
+
if (!this._octree) return { pairs };
|
|
2561
|
+
this._boxIndex.forEach((box, id) => {
|
|
2562
|
+
sourceEntries.push({
|
|
2563
|
+
id: String(id),
|
|
2564
|
+
box,
|
|
2565
|
+
});
|
|
2566
|
+
});
|
|
2108
2567
|
}
|
|
2109
|
-
if (!this._octree) return { pairs };
|
|
2110
2568
|
|
|
2111
|
-
|
|
2569
|
+
sourceEntries.forEach(({ id: idA, box: boxA }) => {
|
|
2112
2570
|
if (!this._isCollisionBoxAvailable(boxA)) return;
|
|
2113
2571
|
|
|
2114
|
-
const hits = this.
|
|
2572
|
+
const hits = this._queryOctreeByBoxWithRoot(root, boxA);
|
|
2115
2573
|
for (let i = 0; i < hits.length; i++) {
|
|
2116
2574
|
const item = hits[i];
|
|
2117
2575
|
if (!item || String(item.id) === String(idA)) continue;
|
|
2118
2576
|
const boxB = item.box;
|
|
2119
2577
|
if (!this._isCollisionBoxMatched(boxA, boxB)) continue;
|
|
2120
2578
|
|
|
2579
|
+
if (dedupePairKeySet) {
|
|
2580
|
+
const pairKey = `${String(idA)}__${String(item.id)}`;
|
|
2581
|
+
if (dedupePairKeySet.has(pairKey)) continue;
|
|
2582
|
+
dedupePairKeySet.add(pairKey);
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2121
2585
|
pairs.push({
|
|
2122
2586
|
idA: this._formatCollisionPairId(idA),
|
|
2123
2587
|
idB: this._formatCollisionPairId(item.id),
|
|
@@ -2571,7 +3035,8 @@ export default {
|
|
|
2571
3035
|
/**
|
|
2572
3036
|
* 执行视椎体裁切,清理视椎体外的InstancedMesh实例
|
|
2573
3037
|
*/
|
|
2574
|
-
async performFrustumCulling() {
|
|
3038
|
+
async performFrustumCulling(options = {}) {
|
|
3039
|
+
const skipUnload = !!(options && options.skipUnload);
|
|
2575
3040
|
const modelState = this.noObserver
|
|
2576
3041
|
? this.noObserver.modelStateManager
|
|
2577
3042
|
: this.modelStateManager;
|
|
@@ -2613,7 +3078,7 @@ export default {
|
|
|
2613
3078
|
visibleIdSet.add(this.formatModelId(hits[i].modelId));
|
|
2614
3079
|
continue;
|
|
2615
3080
|
}
|
|
2616
|
-
|
|
3081
|
+
this.appendOcclusionCandidates(candidates, hits[i].modelId, hits[i].box, globalFrustum);
|
|
2617
3082
|
}
|
|
2618
3083
|
} else if (this._boxIndex && this._boxIndex.size > 0) {
|
|
2619
3084
|
this._boxIndex.forEach((box, modelId) => {
|
|
@@ -2624,7 +3089,7 @@ export default {
|
|
|
2624
3089
|
visibleIdSet.add(this.formatModelId(modelId));
|
|
2625
3090
|
return;
|
|
2626
3091
|
}
|
|
2627
|
-
|
|
3092
|
+
this.appendOcclusionCandidates(candidates, modelId, box, globalFrustum);
|
|
2628
3093
|
}
|
|
2629
3094
|
});
|
|
2630
3095
|
}
|
|
@@ -2740,6 +3205,7 @@ export default {
|
|
|
2740
3205
|
const tBuild0 = performance.now();
|
|
2741
3206
|
|
|
2742
3207
|
const flag1Items = [];
|
|
3208
|
+
const flag2Items = [];
|
|
2743
3209
|
const flag3Items = [];
|
|
2744
3210
|
|
|
2745
3211
|
let globalIdx = 1;
|
|
@@ -2753,6 +3219,8 @@ export default {
|
|
|
2753
3219
|
const flag = (c.box.userData && c.box.userData.flag) || 1;
|
|
2754
3220
|
if (flag === 3 && c.box.userData && c.box.userData.obb) {
|
|
2755
3221
|
flag3Items.push({ c, idx });
|
|
3222
|
+
} else if (flag === 2) {
|
|
3223
|
+
flag2Items.push({ c, idx });
|
|
2756
3224
|
} else if (flag === 1) {
|
|
2757
3225
|
flag1Items.push({ c, idx });
|
|
2758
3226
|
}
|
|
@@ -2769,7 +3237,8 @@ export default {
|
|
|
2769
3237
|
const _tempMatA = occObjs._tempMatA || (occObjs._tempMatA = new this.THREE.Matrix4());
|
|
2770
3238
|
|
|
2771
3239
|
let boxes = occObjs.boxes;
|
|
2772
|
-
const
|
|
3240
|
+
const boxItems = flag3Items.concat(flag2Items);
|
|
3241
|
+
const boxCount = boxItems.length;
|
|
2773
3242
|
if (boxCount > 0) {
|
|
2774
3243
|
const needCapacity = boxCount;
|
|
2775
3244
|
const capacity = boxes && typeof boxes.capacity === 'number' ? boxes.capacity : 0;
|
|
@@ -2799,19 +3268,26 @@ export default {
|
|
|
2799
3268
|
}
|
|
2800
3269
|
boxes.mesh.count = boxCount;
|
|
2801
3270
|
for (let i = 0; i < boxCount; i++) {
|
|
2802
|
-
const item =
|
|
2803
|
-
const { matrix, halfSize } = item.c.box.userData.obb;
|
|
3271
|
+
const item = boxItems[i];
|
|
2804
3272
|
const idx = item.idx;
|
|
2805
3273
|
const r = (idx & 255) / 255;
|
|
2806
3274
|
const g = ((idx >> 8) & 255) / 255;
|
|
2807
3275
|
const b = ((idx >> 16) & 255) / 255;
|
|
2808
3276
|
_tempColor.setRGB(r, g, b);
|
|
2809
3277
|
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
_tempMatrix.
|
|
3278
|
+
const obb = item.c.box.userData && item.c.box.userData.obb;
|
|
3279
|
+
if (obb) {
|
|
3280
|
+
_tempScale.copy(obb.halfSize).multiplyScalar(2);
|
|
3281
|
+
_tempMatrix.copy(obb.matrix);
|
|
3282
|
+
_tempMatrix.scale(_tempScale);
|
|
3283
|
+
if (typeof this.bizToThreeMatrix !== 'undefined' && this.bizToThreeMatrix) {
|
|
3284
|
+
_tempMatrix.premultiply(this.bizToThreeMatrix);
|
|
3285
|
+
}
|
|
3286
|
+
} else {
|
|
3287
|
+
item.c.box.getSize(_tempScale);
|
|
3288
|
+
item.c.box.getCenter(_tempVec3);
|
|
3289
|
+
_tempMatrix.makeTranslation(_tempVec3.x, _tempVec3.y, _tempVec3.z);
|
|
3290
|
+
_tempMatrix.scale(_tempScale);
|
|
2815
3291
|
}
|
|
2816
3292
|
|
|
2817
3293
|
boxes.mesh.setMatrixAt(i, _tempMatrix);
|
|
@@ -3254,12 +3730,12 @@ export default {
|
|
|
3254
3730
|
this.renderer.toneMapping = prevToneMapping;
|
|
3255
3731
|
} catch (e) {
|
|
3256
3732
|
for (let i = 0; i < candidates.length; i++) {
|
|
3257
|
-
visibleIdSet.add(candidates[i].modelId);
|
|
3733
|
+
visibleIdSet.add(this.formatModelId(candidates[i].modelId));
|
|
3258
3734
|
}
|
|
3259
3735
|
}
|
|
3260
3736
|
} else {
|
|
3261
3737
|
for (let i = 0; i < candidates.length; i++) {
|
|
3262
|
-
visibleIdSet.add(candidates[i].modelId);
|
|
3738
|
+
visibleIdSet.add(this.formatModelId(candidates[i].modelId));
|
|
3263
3739
|
}
|
|
3264
3740
|
}
|
|
3265
3741
|
visibleIds.length = 0;
|
|
@@ -3268,37 +3744,43 @@ export default {
|
|
|
3268
3744
|
const parentsToCheck = new Set();
|
|
3269
3745
|
this.scene.traverse(child => {
|
|
3270
3746
|
if (!child.isInstancedMesh) return;
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
? child.parent.userData.instancesMap
|
|
3280
|
-
: null;
|
|
3747
|
+
const instancesMap =
|
|
3748
|
+
child && child.userData && child.userData.instancesMap instanceof Map
|
|
3749
|
+
? child.userData.instancesMap
|
|
3750
|
+
: child.parent &&
|
|
3751
|
+
child.parent.userData &&
|
|
3752
|
+
child.parent.userData.instancesMap instanceof Map
|
|
3753
|
+
? child.parent.userData.instancesMap
|
|
3754
|
+
: null;
|
|
3281
3755
|
|
|
3756
|
+
// 多实例复用同一个 InstancedMesh 时,仅在全部实例都不可见时整体卸载。
|
|
3757
|
+
if (child.count >= 2) {
|
|
3282
3758
|
if (instancesMap) {
|
|
3283
3759
|
let allInactive = true;
|
|
3284
3760
|
const modelIds = Array.from(instancesMap.keys());
|
|
3761
|
+
let unloadModelId = null;
|
|
3285
3762
|
|
|
3286
3763
|
// 第一遍遍历:检查是否有任何一个实例处于活跃状态(在视锥体内且可见)
|
|
3287
3764
|
for (const modelId of modelIds) {
|
|
3288
3765
|
// 检查已取消勾选模型进行卸载
|
|
3289
|
-
const documentId = modelId.split(':')[1];
|
|
3290
|
-
if (!this.noObserver.documentModelIds.get(documentId)) {
|
|
3291
|
-
|
|
3292
|
-
|
|
3766
|
+
const documentId = String(modelId).split(':')[1];
|
|
3767
|
+
if (documentId && !this.noObserver.documentModelIds.get(documentId)) {
|
|
3768
|
+
if (!skipUnload) this.setInstanceCullingVisible(child, modelId, false);
|
|
3769
|
+
unloadModelId = modelId;
|
|
3770
|
+
continue;
|
|
3293
3771
|
}
|
|
3294
3772
|
|
|
3295
|
-
|
|
3773
|
+
const formattedModelId = this.formatModelId(modelId);
|
|
3774
|
+
if (
|
|
3775
|
+
bypassList &&
|
|
3776
|
+
bypassList.size > 0 &&
|
|
3777
|
+
(bypassList.has(modelId) || bypassList.has(formattedModelId))
|
|
3778
|
+
) {
|
|
3779
|
+
if (!skipUnload) this.setInstanceCullingVisible(child, modelId, true);
|
|
3780
|
+
allInactive = false;
|
|
3781
|
+
continue;
|
|
3782
|
+
}
|
|
3296
3783
|
|
|
3297
|
-
const instanceInfo = instancesMap.get(modelId);
|
|
3298
|
-
const instanceIndex =
|
|
3299
|
-
instanceInfo && typeof instanceInfo.instanceIndex === 'number'
|
|
3300
|
-
? instanceInfo.instanceIndex
|
|
3301
|
-
: null;
|
|
3302
3784
|
const box =
|
|
3303
3785
|
this._boxIndex && this._boxIndex.has(String(modelId))
|
|
3304
3786
|
? this._boxIndex.get(String(modelId))
|
|
@@ -3306,16 +3788,19 @@ export default {
|
|
|
3306
3788
|
? this._boxIndex.get(modelId)
|
|
3307
3789
|
: null;
|
|
3308
3790
|
if (!this.shouldApplyOcclusionByBox(box)) {
|
|
3791
|
+
if (!skipUnload) this.setInstanceCullingVisible(child, modelId, true);
|
|
3309
3792
|
allInactive = false;
|
|
3793
|
+
toLoadSet.delete(formattedModelId);
|
|
3310
3794
|
continue;
|
|
3311
3795
|
}
|
|
3312
3796
|
|
|
3313
|
-
const
|
|
3314
|
-
|
|
3315
|
-
// console.log('modelId', modelId, 'inFrustum', inFrustum, 'modelInVisible', modelInVisible)
|
|
3797
|
+
const modelInVisible = visibleIdSet.has(formattedModelId);
|
|
3798
|
+
toLoadSet.delete(formattedModelId);
|
|
3316
3799
|
|
|
3317
|
-
//
|
|
3318
|
-
|
|
3800
|
+
// 可见集合已经聚合了同一模型的全部图元,任一图元可见时都保留该模型实例。
|
|
3801
|
+
const isActive = modelInVisible;
|
|
3802
|
+
if (!skipUnload) this.setInstanceCullingVisible(child, modelId, isActive);
|
|
3803
|
+
if (isActive) {
|
|
3319
3804
|
allInactive = false;
|
|
3320
3805
|
}
|
|
3321
3806
|
}
|
|
@@ -3329,20 +3814,24 @@ export default {
|
|
|
3329
3814
|
? this._boxIndex.get(modelId)
|
|
3330
3815
|
: null;
|
|
3331
3816
|
if (!this.shouldApplyOcclusionByBox(box)) {
|
|
3332
|
-
|
|
3333
|
-
toLoadSet.delete(modelId);
|
|
3334
|
-
}
|
|
3817
|
+
toLoadSet.delete(this.formatModelId(modelId));
|
|
3335
3818
|
continue;
|
|
3336
3819
|
}
|
|
3337
3820
|
// 无论是否卸载,只要模型已在场景中,就从待加载集合中移除
|
|
3338
|
-
|
|
3339
|
-
toLoadSet.delete(modelId);
|
|
3340
|
-
}
|
|
3821
|
+
toLoadSet.delete(this.formatModelId(modelId));
|
|
3341
3822
|
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3823
|
+
if (!unloadModelId) unloadModelId = modelId;
|
|
3824
|
+
}
|
|
3825
|
+
|
|
3826
|
+
// 仅当所有实例都不活跃时,才整体卸载一次,避免重复释放同一网格。
|
|
3827
|
+
if (
|
|
3828
|
+
!skipUnload &&
|
|
3829
|
+
!this.firstPerSign &&
|
|
3830
|
+
!this.roaming &&
|
|
3831
|
+
allInactive &&
|
|
3832
|
+
unloadModelId !== null
|
|
3833
|
+
) {
|
|
3834
|
+
toUnload.push({ modelId: unloadModelId, child });
|
|
3346
3835
|
}
|
|
3347
3836
|
}
|
|
3348
3837
|
// if (toLoadSet.has(modelId)) {
|
|
@@ -3351,10 +3840,18 @@ export default {
|
|
|
3351
3840
|
return;
|
|
3352
3841
|
}
|
|
3353
3842
|
const modelId =
|
|
3354
|
-
|
|
3843
|
+
instancesMap && instancesMap.size > 0
|
|
3844
|
+
? instancesMap.keys().next().value
|
|
3845
|
+
: child.parent && child.parent.userData && child.parent.userData.instanceId
|
|
3355
3846
|
? child.parent.userData.instanceId
|
|
3356
3847
|
: child.uuid;
|
|
3357
|
-
|
|
3848
|
+
const formattedModelId = this.formatModelId(modelId);
|
|
3849
|
+
if (
|
|
3850
|
+
bypassList &&
|
|
3851
|
+
bypassList.size > 0 &&
|
|
3852
|
+
(bypassList.has(modelId) || bypassList.has(formattedModelId))
|
|
3853
|
+
)
|
|
3854
|
+
return;
|
|
3358
3855
|
const box =
|
|
3359
3856
|
this._boxIndex && this._boxIndex.has(String(modelId))
|
|
3360
3857
|
? this._boxIndex.get(String(modelId))
|
|
@@ -3362,18 +3859,13 @@ export default {
|
|
|
3362
3859
|
? this._boxIndex.get(modelId)
|
|
3363
3860
|
: null;
|
|
3364
3861
|
if (!this.shouldApplyOcclusionByBox(box)) {
|
|
3365
|
-
|
|
3366
|
-
toLoadSet.delete(modelId);
|
|
3367
|
-
}
|
|
3862
|
+
toLoadSet.delete(formattedModelId);
|
|
3368
3863
|
return;
|
|
3369
3864
|
}
|
|
3370
3865
|
|
|
3371
|
-
const
|
|
3372
|
-
|
|
3373
|
-
if (modelInVisible) {
|
|
3374
|
-
toLoadSet.delete(modelId);
|
|
3375
|
-
}
|
|
3376
|
-
if (!this.firstPerSign && !this.roaming && (!inFrustum || !modelInVisible)) {
|
|
3866
|
+
const modelInVisible = visibleIdSet.has(formattedModelId);
|
|
3867
|
+
toLoadSet.delete(formattedModelId);
|
|
3868
|
+
if (!skipUnload && !this.firstPerSign && !this.roaming && !modelInVisible) {
|
|
3377
3869
|
toUnload.push({ modelId, child });
|
|
3378
3870
|
}
|
|
3379
3871
|
});
|
|
@@ -3491,9 +3983,15 @@ export default {
|
|
|
3491
3983
|
* @param {Object} config - 配置项
|
|
3492
3984
|
* @param {Object} config.modelApi - 模型API接口
|
|
3493
3985
|
* @param {string} [config.projectId] - 项目ID
|
|
3986
|
+
* @param {Object} [config.cameraControlMoveObserver] - 普通视角运动中触发加载的配置
|
|
3987
|
+
* @param {boolean} [config.cameraControlMoveObserver.enabled=false] - 是否开启普通视角运动中加载
|
|
3988
|
+
* @param {number} [config.cameraControlMoveObserver.interval=500] - 触发间隔,单位毫秒
|
|
3989
|
+
* @param {boolean} [config.enableCameraControlMoveObserver=false] - 是否开启普通视角运动中加载
|
|
3990
|
+
* @param {number} [config.cameraControlMoveInterval=500] - 触发间隔,单位毫秒
|
|
3494
3991
|
*/
|
|
3495
3992
|
init(config = {}) {
|
|
3496
3993
|
console.log('init');
|
|
3994
|
+
this.setCameraControlMoveObserverConfig(config);
|
|
3497
3995
|
this.initStreamLoader(config.modelApi, config);
|
|
3498
3996
|
},
|
|
3499
3997
|
|
|
@@ -3920,6 +4418,124 @@ export default {
|
|
|
3920
4418
|
this.noObserver.isObserverEnabled = enabled;
|
|
3921
4419
|
},
|
|
3922
4420
|
|
|
4421
|
+
setCameraControlMoveObserverConfig(config = {}) {
|
|
4422
|
+
if (!this.noObserver) return;
|
|
4423
|
+
const nestedConfig = config.cameraControlMoveObserver || {};
|
|
4424
|
+
const enabled =
|
|
4425
|
+
typeof nestedConfig.enabled === 'boolean'
|
|
4426
|
+
? nestedConfig.enabled
|
|
4427
|
+
: typeof config.enableCameraControlMoveObserver === 'boolean'
|
|
4428
|
+
? config.enableCameraControlMoveObserver
|
|
4429
|
+
: false;
|
|
4430
|
+
const rawInterval =
|
|
4431
|
+
nestedConfig.interval !== undefined
|
|
4432
|
+
? nestedConfig.interval
|
|
4433
|
+
: config.cameraControlMoveInterval;
|
|
4434
|
+
const interval = Number(rawInterval);
|
|
4435
|
+
const safeInterval = Number.isFinite(interval) && interval > 0 ? interval : 500;
|
|
4436
|
+
|
|
4437
|
+
this.noObserver.cameraControlMoveObserver = {
|
|
4438
|
+
enabled,
|
|
4439
|
+
interval: safeInterval,
|
|
4440
|
+
};
|
|
4441
|
+
this.resetCameraControlMoveSnapshot(false);
|
|
4442
|
+
},
|
|
4443
|
+
|
|
4444
|
+
isCameraControlMoveObserverEnabled() {
|
|
4445
|
+
const config = this.noObserver && this.noObserver.cameraControlMoveObserver;
|
|
4446
|
+
return !!(config && config.enabled);
|
|
4447
|
+
},
|
|
4448
|
+
|
|
4449
|
+
getCameraControlTarget(target) {
|
|
4450
|
+
if (!target || !this.cameraControls) return false;
|
|
4451
|
+
if (typeof this.cameraControls.getTarget === 'function') {
|
|
4452
|
+
this.cameraControls.getTarget(target);
|
|
4453
|
+
return true;
|
|
4454
|
+
}
|
|
4455
|
+
if (this.cameraControls._target) {
|
|
4456
|
+
target.copy(this.cameraControls._target);
|
|
4457
|
+
return true;
|
|
4458
|
+
}
|
|
4459
|
+
return false;
|
|
4460
|
+
},
|
|
4461
|
+
|
|
4462
|
+
resetCameraControlMoveSnapshot(syncCurrent = false) {
|
|
4463
|
+
if (!this.noObserver) return;
|
|
4464
|
+
if (!this.noObserver._cameraControlMoveState) {
|
|
4465
|
+
this.noObserver._cameraControlMoveState = {
|
|
4466
|
+
position: new this.THREE.Vector3(),
|
|
4467
|
+
quaternion: new this.THREE.Quaternion(),
|
|
4468
|
+
target: new this.THREE.Vector3(),
|
|
4469
|
+
currentTarget: new this.THREE.Vector3(),
|
|
4470
|
+
initialized: false,
|
|
4471
|
+
hasTarget: false,
|
|
4472
|
+
};
|
|
4473
|
+
}
|
|
4474
|
+
const state = this.noObserver._cameraControlMoveState;
|
|
4475
|
+
if (!syncCurrent || !this.camera) {
|
|
4476
|
+
state.initialized = false;
|
|
4477
|
+
state.hasTarget = false;
|
|
4478
|
+
return;
|
|
4479
|
+
}
|
|
4480
|
+
|
|
4481
|
+
this.camera.updateMatrixWorld(true);
|
|
4482
|
+
state.position.copy(this.camera.position);
|
|
4483
|
+
state.quaternion.copy(this.camera.quaternion);
|
|
4484
|
+
state.hasTarget = this.getCameraControlTarget(state.target);
|
|
4485
|
+
state.initialized = true;
|
|
4486
|
+
},
|
|
4487
|
+
|
|
4488
|
+
observeCameraControlMove() {
|
|
4489
|
+
if (
|
|
4490
|
+
!this.noObserver ||
|
|
4491
|
+
!this.noObserver.isObserverEnabled ||
|
|
4492
|
+
!this.isCameraControlMoveObserverEnabled() ||
|
|
4493
|
+
!this.camera ||
|
|
4494
|
+
!this.cameraControls ||
|
|
4495
|
+
typeof this._cameraChangeObserver !== 'function'
|
|
4496
|
+
) {
|
|
4497
|
+
return;
|
|
4498
|
+
}
|
|
4499
|
+
|
|
4500
|
+
const loadingState = this.noObserver.batchLoadingState || {};
|
|
4501
|
+
const interactionState = loadingState.interactionState || {};
|
|
4502
|
+
const wheelActive = !!interactionState.wheelTimeout;
|
|
4503
|
+
const controlActive = !!this.noObserver.isControlActive;
|
|
4504
|
+
if ((!controlActive && !wheelActive) || this.firstPerSign) {
|
|
4505
|
+
this.resetCameraControlMoveSnapshot(false);
|
|
4506
|
+
return;
|
|
4507
|
+
}
|
|
4508
|
+
|
|
4509
|
+
if (!this.noObserver._cameraControlMoveState) {
|
|
4510
|
+
this.resetCameraControlMoveSnapshot(true);
|
|
4511
|
+
return;
|
|
4512
|
+
}
|
|
4513
|
+
|
|
4514
|
+
const state = this.noObserver._cameraControlMoveState;
|
|
4515
|
+
if (!state.initialized) {
|
|
4516
|
+
this.resetCameraControlMoveSnapshot(true);
|
|
4517
|
+
return;
|
|
4518
|
+
}
|
|
4519
|
+
|
|
4520
|
+
this.camera.updateMatrixWorld(true);
|
|
4521
|
+
const hasTarget = this.getCameraControlTarget(state.currentTarget);
|
|
4522
|
+
const positionMoved = state.position.distanceToSquared(this.camera.position) > 1e-8;
|
|
4523
|
+
const quaternionMoved = 1 - Math.abs(state.quaternion.dot(this.camera.quaternion)) > 1e-10;
|
|
4524
|
+
const targetMoved =
|
|
4525
|
+
hasTarget !== state.hasTarget ||
|
|
4526
|
+
(hasTarget && state.target.distanceToSquared(state.currentTarget) > 1e-8);
|
|
4527
|
+
|
|
4528
|
+
if (!positionMoved && !quaternionMoved && !targetMoved) return;
|
|
4529
|
+
|
|
4530
|
+
state.position.copy(this.camera.position);
|
|
4531
|
+
state.quaternion.copy(this.camera.quaternion);
|
|
4532
|
+
state.hasTarget = hasTarget;
|
|
4533
|
+
if (hasTarget) {
|
|
4534
|
+
state.target.copy(state.currentTarget);
|
|
4535
|
+
}
|
|
4536
|
+
this._cameraChangeObserver('controlMove');
|
|
4537
|
+
},
|
|
4538
|
+
|
|
3923
4539
|
/**
|
|
3924
4540
|
* 初始化相机变更观察者 (CameraChangeObserver)
|
|
3925
4541
|
* 统一监听:用户交互(Wheel/Control)、程序化动画、API调用
|
|
@@ -3954,7 +4570,9 @@ export default {
|
|
|
3954
4570
|
// 执行视锥体裁剪
|
|
3955
4571
|
// 第一视角不裁切
|
|
3956
4572
|
if (this.modelStateManager.frustumCheckEnabled) {
|
|
3957
|
-
await this.performFrustumCulling(
|
|
4573
|
+
await this.performFrustumCulling({
|
|
4574
|
+
skipUnload: source === 'controlMove',
|
|
4575
|
+
});
|
|
3958
4576
|
}
|
|
3959
4577
|
|
|
3960
4578
|
// 执行流式加载请求
|
|
@@ -3966,11 +4584,19 @@ export default {
|
|
|
3966
4584
|
}
|
|
3967
4585
|
},
|
|
3968
4586
|
source => {
|
|
3969
|
-
//
|
|
4587
|
+
// 漫游、第一人称移动、普通视角拖拽/缩放中,使用节流策略避免持续运动被防抖吞掉。
|
|
3970
4588
|
// 避免在持续运动中因防抖重置定时器而无法触发
|
|
3971
4589
|
if (source === 'roam' || source === 'firstPersonMove') {
|
|
3972
4590
|
return { type: 'throttle', limit: 1000 };
|
|
3973
4591
|
}
|
|
4592
|
+
if (source === 'controlMove') {
|
|
4593
|
+
const config = this.noObserver && this.noObserver.cameraControlMoveObserver;
|
|
4594
|
+
const interval =
|
|
4595
|
+
config && Number.isFinite(config.interval) && config.interval > 0
|
|
4596
|
+
? config.interval
|
|
4597
|
+
: 500;
|
|
4598
|
+
return { type: 'throttle', limit: interval };
|
|
4599
|
+
}
|
|
3974
4600
|
return 300; // 默认防抖 300ms
|
|
3975
4601
|
}
|
|
3976
4602
|
);
|
|
@@ -3984,8 +4610,20 @@ export default {
|
|
|
3984
4610
|
this._wheelHandler = res => {
|
|
3985
4611
|
this.$emit('wheelStart', res);
|
|
3986
4612
|
|
|
3987
|
-
|
|
3988
|
-
this.
|
|
4613
|
+
const cameraControlMoveEnabled = this.isCameraControlMoveObserverEnabled();
|
|
4614
|
+
const loadingState = this.noObserver
|
|
4615
|
+
? this.noObserver.batchLoadingState
|
|
4616
|
+
: this.batchLoadingState;
|
|
4617
|
+
const wheelAlreadyActive =
|
|
4618
|
+
loadingState &&
|
|
4619
|
+
loadingState.interactionState &&
|
|
4620
|
+
loadingState.interactionState.wheelTimeout;
|
|
4621
|
+
if (cameraControlMoveEnabled && !wheelAlreadyActive) {
|
|
4622
|
+
this.resetCameraControlMoveSnapshot(true);
|
|
4623
|
+
} else if (!cameraControlMoveEnabled) {
|
|
4624
|
+
// 未开启普通视角运动加载时,保持原有交互中断流程。
|
|
4625
|
+
this.beginInteraction('wheel');
|
|
4626
|
+
}
|
|
3989
4627
|
|
|
3990
4628
|
// 设置滚轮交互的结束检测
|
|
3991
4629
|
this.scheduleWheelInteractionEnd(res, 100);
|
|
@@ -4027,9 +4665,12 @@ export default {
|
|
|
4027
4665
|
// 4. 监听器 - Controls (用户拖拽/操作)
|
|
4028
4666
|
this._onControlStart = res => {
|
|
4029
4667
|
this.noObserver.isControlActive = true;
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4668
|
+
if (this.isCameraControlMoveObserverEnabled()) {
|
|
4669
|
+
this.resetCameraControlMoveSnapshot(true);
|
|
4670
|
+
} else {
|
|
4671
|
+
// 未开启普通视角运动加载时,保持原有交互中断流程。
|
|
4672
|
+
this.beginInteraction('this.camera');
|
|
4673
|
+
}
|
|
4033
4674
|
this.$emit('controlStart', res);
|
|
4034
4675
|
|
|
4035
4676
|
// 记录鼠标按下时的位置
|
|
@@ -4046,8 +4687,10 @@ export default {
|
|
|
4046
4687
|
|
|
4047
4688
|
this.$emit('controlEnd', res);
|
|
4048
4689
|
|
|
4049
|
-
|
|
4050
|
-
|
|
4690
|
+
if (!this.isCameraControlMoveObserverEnabled()) {
|
|
4691
|
+
// 未开启普通视角运动加载时,保持原有交互中断流程。
|
|
4692
|
+
this.endInteraction('this.camera', res, { immediateResume: false });
|
|
4693
|
+
}
|
|
4051
4694
|
|
|
4052
4695
|
// 记录鼠标抬起时的位置并计算是否发生了拖拽
|
|
4053
4696
|
const event = res.originalEvent || window.event;
|
|
@@ -4076,6 +4719,9 @@ export default {
|
|
|
4076
4719
|
// 触发观察者
|
|
4077
4720
|
this._cameraChangeObserver('control');
|
|
4078
4721
|
}
|
|
4722
|
+
if (this.isCameraControlMoveObserverEnabled()) {
|
|
4723
|
+
this.resetCameraControlMoveSnapshot(false);
|
|
4724
|
+
}
|
|
4079
4725
|
};
|
|
4080
4726
|
|
|
4081
4727
|
this.cameraControls.addEventListener('controlstart', this._onControlStart);
|
|
@@ -4136,12 +4782,37 @@ export default {
|
|
|
4136
4782
|
},
|
|
4137
4783
|
// 动态设置视角滚轮的距离
|
|
4138
4784
|
setCameraConfig() {
|
|
4785
|
+
if (
|
|
4786
|
+
!this.sceneBoundingBox ||
|
|
4787
|
+
!this.sceneBoundingBox.isBox3 ||
|
|
4788
|
+
this.sceneBoundingBox.isEmpty()
|
|
4789
|
+
) {
|
|
4790
|
+
return;
|
|
4791
|
+
}
|
|
4139
4792
|
// let box3 = new this.THREE.Box3().setFromObject(this.sceneBoundingBox);
|
|
4140
4793
|
let size = new this.THREE.Vector3();
|
|
4141
4794
|
this.sceneBoundingBox.getSize(size);
|
|
4142
4795
|
const maxBorder = Math.max(size.x, size.y, size.z);
|
|
4796
|
+
if (isDebug) {
|
|
4797
|
+
const center = this.sceneBoundingBox.getCenter(new this.THREE.Vector3());
|
|
4798
|
+
console.log('sceneBoundingBox 调试信息', {
|
|
4799
|
+
size: {
|
|
4800
|
+
x: size.x,
|
|
4801
|
+
y: size.y,
|
|
4802
|
+
z: size.z,
|
|
4803
|
+
},
|
|
4804
|
+
center: {
|
|
4805
|
+
x: center.x,
|
|
4806
|
+
y: center.y,
|
|
4807
|
+
z: center.z,
|
|
4808
|
+
},
|
|
4809
|
+
maxBorder,
|
|
4810
|
+
});
|
|
4811
|
+
this.refreshSceneBoundingBoxHelper(this.sceneBoundingBoxDebugEnabled);
|
|
4812
|
+
}
|
|
4143
4813
|
// this.cameraControls.this.camera.far = maxBorder * 10; // 设置相机的远裁剪面
|
|
4144
4814
|
this.cameraControls.minDistance = maxBorder * 0.2; // 动态设置视角滚轮的距离
|
|
4815
|
+
// this.cameraControls.minDistance = 1000; // 动态设置视角滚轮的距离
|
|
4145
4816
|
this.camera.updateProjectionMatrix();
|
|
4146
4817
|
},
|
|
4147
4818
|
// 获取mesh的中心点
|
|
@@ -4469,8 +5140,19 @@ export default {
|
|
|
4469
5140
|
if (this.threeMeasure) {
|
|
4470
5141
|
this.threeMeasure.updateParams(width, height);
|
|
4471
5142
|
}
|
|
5143
|
+
this.updateMeshLineResolution(width, height);
|
|
4472
5144
|
}
|
|
4473
5145
|
},
|
|
5146
|
+
updateMeshLineResolution(width, height) {
|
|
5147
|
+
if (!this.modelGroup || !width || !height) return;
|
|
5148
|
+
this.modelGroup.traverse(child => {
|
|
5149
|
+
const material = child && child.material;
|
|
5150
|
+
if (material && material.type === 'MeshLineMaterial' && material.resolution) {
|
|
5151
|
+
material.resolution.set(width, height);
|
|
5152
|
+
material.needsUpdate = true;
|
|
5153
|
+
}
|
|
5154
|
+
});
|
|
5155
|
+
},
|
|
4474
5156
|
setModelCenter(target, options = {}) {
|
|
4475
5157
|
// return
|
|
4476
5158
|
let box3;
|
|
@@ -4644,7 +5326,9 @@ export default {
|
|
|
4644
5326
|
children.instanceMatrix.needsUpdate = true;
|
|
4645
5327
|
});
|
|
4646
5328
|
this.refreshCollisionObbDebug();
|
|
4647
|
-
this.
|
|
5329
|
+
this.refreshOcclusionBoundsDebug();
|
|
5330
|
+
// 调用此方法后 模型隐藏后再执行显示操作后无法正常显示,故在此先注释
|
|
5331
|
+
// this.notifyCameraChange(); // 触发场景更新
|
|
4648
5332
|
break;
|
|
4649
5333
|
}
|
|
4650
5334
|
case 'opacity':
|
|
@@ -4749,6 +5433,7 @@ export default {
|
|
|
4749
5433
|
this.buildOctreeFromBoxIndex();
|
|
4750
5434
|
}
|
|
4751
5435
|
this.refreshCollisionObbDebug();
|
|
5436
|
+
this.refreshOcclusionBoundsDebug();
|
|
4752
5437
|
},
|
|
4753
5438
|
showOutlinePass(instanceId) {
|
|
4754
5439
|
let targetObj = this.getObjectByName(instanceId);
|
|
@@ -4896,6 +5581,7 @@ export default {
|
|
|
4896
5581
|
children.instanceMatrix.needsUpdate = true;
|
|
4897
5582
|
});
|
|
4898
5583
|
this.refreshCollisionObbDebug();
|
|
5584
|
+
this.refreshOcclusionBoundsDebug();
|
|
4899
5585
|
break;
|
|
4900
5586
|
}
|
|
4901
5587
|
case 'opacity':
|
|
@@ -5317,6 +6003,7 @@ export default {
|
|
|
5317
6003
|
}
|
|
5318
6004
|
this.clearBypassCullingModelIds();
|
|
5319
6005
|
this.hideCollisionObbDebug();
|
|
6006
|
+
this.hideOcclusionBoundsDebug();
|
|
5320
6007
|
|
|
5321
6008
|
if (this.scene) {
|
|
5322
6009
|
this.removeTraverse();
|
|
@@ -5582,6 +6269,7 @@ export default {
|
|
|
5582
6269
|
if (nodeType === 'custom-root') return true;
|
|
5583
6270
|
if (userData.transformControlHelper === true) return true;
|
|
5584
6271
|
if (userData.collisionObbDebugHelper === true) return true;
|
|
6272
|
+
if (userData.occlusionBoundsDebugHelper === true) return true;
|
|
5585
6273
|
if (object.isCamera || object.isLight) return true;
|
|
5586
6274
|
if (typeof object.type === 'string' && /Helper$/i.test(object.type)) return true;
|
|
5587
6275
|
return object.type === 'CSS2DObject' || object.type === 'TransformControlsRoot';
|
|
@@ -6545,6 +7233,7 @@ export default {
|
|
|
6545
7233
|
});
|
|
6546
7234
|
}
|
|
6547
7235
|
this.cameraControls.enabled && this.cameraControls.update(this.timeStamp);
|
|
7236
|
+
this.observeCameraControlMove();
|
|
6548
7237
|
// 计算相机近裁面到包围盒当前面的距离
|
|
6549
7238
|
this.cameraTrack();
|
|
6550
7239
|
this.firstPerspective();
|
|
@@ -6692,6 +7381,7 @@ export default {
|
|
|
6692
7381
|
object.userData.instancesMap instanceof Map &&
|
|
6693
7382
|
object.userData.instancesMap.get(instanceId);
|
|
6694
7383
|
if (instanceInfo && instanceInfo.visible === false) return false;
|
|
7384
|
+
if (instanceInfo && instanceInfo.cullingVisible === false) return false;
|
|
6695
7385
|
|
|
6696
7386
|
const box =
|
|
6697
7387
|
this._boxIndex && this._boxIndex.has(String(instanceId))
|
|
@@ -6755,6 +7445,13 @@ export default {
|
|
|
6755
7445
|
const loadingState = this.noObserver
|
|
6756
7446
|
? this.noObserver.batchLoadingState
|
|
6757
7447
|
: this.batchLoadingState;
|
|
7448
|
+
const rect = this.renderer
|
|
7449
|
+
? this.renderer.domElement.getBoundingClientRect()
|
|
7450
|
+
: { width: window.innerWidth, height: window.innerHeight };
|
|
7451
|
+
options.meshLineResolution = {
|
|
7452
|
+
width: rect.width || window.innerWidth || 1,
|
|
7453
|
+
height: rect.height || window.innerHeight || 1,
|
|
7454
|
+
};
|
|
6758
7455
|
// 如果已经在加载中,先停止之前的加载
|
|
6759
7456
|
if (loadingState.isLoading) {
|
|
6760
7457
|
this.stopBatchLoading('restart');
|
|
@@ -7318,8 +8015,15 @@ export default {
|
|
|
7318
8015
|
|
|
7319
8016
|
loadingState.interactionState.wheelTimeout = setTimeout(() => {
|
|
7320
8017
|
this.$emit('wheelEnd', event);
|
|
7321
|
-
this.endInteraction('wheel', event, { immediateResume: false });
|
|
7322
8018
|
loadingState.interactionState.wheelTimeout = null;
|
|
8019
|
+
if (this.isCameraControlMoveObserverEnabled()) {
|
|
8020
|
+
this.resetCameraControlMoveSnapshot(false);
|
|
8021
|
+
if (typeof this._cameraChangeObserver === 'function') {
|
|
8022
|
+
this._cameraChangeObserver('wheel');
|
|
8023
|
+
}
|
|
8024
|
+
} else {
|
|
8025
|
+
this.endInteraction('wheel', event, { immediateResume: false });
|
|
8026
|
+
}
|
|
7323
8027
|
}, delay);
|
|
7324
8028
|
},
|
|
7325
8029
|
|
|
@@ -7347,9 +8051,87 @@ export default {
|
|
|
7347
8051
|
/**
|
|
7348
8052
|
* 隐藏场景包围盒
|
|
7349
8053
|
*/
|
|
8054
|
+
refreshSceneBoundingBoxHelper(forceVisible = this.boundingBoxVisible) {
|
|
8055
|
+
if (this.sceneBoundingBoxHelper && this.scene) {
|
|
8056
|
+
this.scene.remove(this.sceneBoundingBoxHelper);
|
|
8057
|
+
this.sceneBoundingBoxHelper.geometry && this.sceneBoundingBoxHelper.geometry.dispose();
|
|
8058
|
+
if (Array.isArray(this.sceneBoundingBoxHelper.material)) {
|
|
8059
|
+
this.sceneBoundingBoxHelper.material.forEach(material => {
|
|
8060
|
+
material && material.dispose && material.dispose();
|
|
8061
|
+
});
|
|
8062
|
+
} else if (
|
|
8063
|
+
this.sceneBoundingBoxHelper.material &&
|
|
8064
|
+
this.sceneBoundingBoxHelper.material.dispose
|
|
8065
|
+
) {
|
|
8066
|
+
this.sceneBoundingBoxHelper.material.dispose();
|
|
8067
|
+
}
|
|
8068
|
+
this.sceneBoundingBoxHelper = null;
|
|
8069
|
+
}
|
|
8070
|
+
|
|
8071
|
+
if (
|
|
8072
|
+
!forceVisible ||
|
|
8073
|
+
!this.scene ||
|
|
8074
|
+
!this.sceneBoundingBox ||
|
|
8075
|
+
!this.sceneBoundingBox.isBox3 ||
|
|
8076
|
+
this.sceneBoundingBox.isEmpty()
|
|
8077
|
+
) {
|
|
8078
|
+
this.boundingBoxVisible = false;
|
|
8079
|
+
return false;
|
|
8080
|
+
}
|
|
8081
|
+
|
|
8082
|
+
const helper = new this.THREE.Box3Helper(this.sceneBoundingBox, 0x00ff88);
|
|
8083
|
+
helper.name = 'sceneBoundingBoxHelper';
|
|
8084
|
+
helper.renderOrder = 999;
|
|
8085
|
+
if (helper.material) {
|
|
8086
|
+
helper.material.depthTest = false;
|
|
8087
|
+
helper.material.transparent = true;
|
|
8088
|
+
helper.material.opacity = 0.9;
|
|
8089
|
+
}
|
|
8090
|
+
this.scene.add(helper);
|
|
8091
|
+
this.sceneBoundingBoxHelper = helper;
|
|
8092
|
+
this.boundingBoxVisible = true;
|
|
8093
|
+
return true;
|
|
8094
|
+
},
|
|
8095
|
+
|
|
8096
|
+
showSceneBoundingBox() {
|
|
8097
|
+
this.sceneBoundingBoxDebugEnabled = true;
|
|
8098
|
+
const shown = this.refreshSceneBoundingBoxHelper(true);
|
|
8099
|
+
if (!shown) {
|
|
8100
|
+
console.warn('sceneBoundingBox 无有效范围,无法绘制调试线框');
|
|
8101
|
+
return;
|
|
8102
|
+
}
|
|
8103
|
+
|
|
8104
|
+
const size = this.sceneBoundingBox.getSize(new this.THREE.Vector3());
|
|
8105
|
+
const center = this.sceneBoundingBox.getCenter(new this.THREE.Vector3());
|
|
8106
|
+
console.log('sceneBoundingBox 线框已绘制', {
|
|
8107
|
+
size: {
|
|
8108
|
+
x: size.x,
|
|
8109
|
+
y: size.y,
|
|
8110
|
+
z: size.z,
|
|
8111
|
+
},
|
|
8112
|
+
center: {
|
|
8113
|
+
x: center.x,
|
|
8114
|
+
y: center.y,
|
|
8115
|
+
z: center.z,
|
|
8116
|
+
},
|
|
8117
|
+
});
|
|
8118
|
+
},
|
|
8119
|
+
|
|
7350
8120
|
hideSceneBoundingBox() {
|
|
8121
|
+
this.sceneBoundingBoxDebugEnabled = false;
|
|
7351
8122
|
if (this.sceneBoundingBoxHelper && this.scene) {
|
|
7352
8123
|
this.scene.remove(this.sceneBoundingBoxHelper);
|
|
8124
|
+
this.sceneBoundingBoxHelper.geometry && this.sceneBoundingBoxHelper.geometry.dispose();
|
|
8125
|
+
if (Array.isArray(this.sceneBoundingBoxHelper.material)) {
|
|
8126
|
+
this.sceneBoundingBoxHelper.material.forEach(material => {
|
|
8127
|
+
material && material.dispose && material.dispose();
|
|
8128
|
+
});
|
|
8129
|
+
} else if (
|
|
8130
|
+
this.sceneBoundingBoxHelper.material &&
|
|
8131
|
+
this.sceneBoundingBoxHelper.material.dispose
|
|
8132
|
+
) {
|
|
8133
|
+
this.sceneBoundingBoxHelper.material.dispose();
|
|
8134
|
+
}
|
|
7353
8135
|
this.sceneBoundingBoxHelper = null;
|
|
7354
8136
|
this.boundingBoxVisible = false;
|
|
7355
8137
|
console.log('场景包围盒已隐藏');
|
|
@@ -7360,7 +8142,7 @@ export default {
|
|
|
7360
8142
|
* 切换场景包围盒显示状态
|
|
7361
8143
|
*/
|
|
7362
8144
|
toggleSceneBoundingBox() {
|
|
7363
|
-
if (this.
|
|
8145
|
+
if (this.sceneBoundingBoxDebugEnabled) {
|
|
7364
8146
|
this.hideSceneBoundingBox();
|
|
7365
8147
|
} else {
|
|
7366
8148
|
this.showSceneBoundingBox();
|