fl-web-component 2.1.1-beta.0 → 2.1.1-beta.10
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 +1301 -826
- 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 +19 -14
- package/packages/components/com-flcanvas/index.vue +350 -302
- package/packages/components/com-graphics/index.vue +559 -80
- package/src/utils/instance-parser.js +5 -0
|
@@ -165,7 +165,7 @@ 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 COLLISION_PENETRATION_EPSILON =
|
|
168
|
+
const COLLISION_PENETRATION_EPSILON = 1;
|
|
169
169
|
const COLLISION_OBB_AXIS_EPSILON = 1e-8;
|
|
170
170
|
const COLLISION_DEBUG_GROUP_NAME = '__collision_obb_debug_group__';
|
|
171
171
|
|
|
@@ -215,7 +215,9 @@ export default {
|
|
|
215
215
|
isPaused: false,
|
|
216
216
|
},
|
|
217
217
|
isolateMode: false,
|
|
218
|
-
|
|
218
|
+
sceneBoundingBoxDebugEnabled: false,
|
|
219
|
+
// collisionObbDebugEnabled: isDebug,
|
|
220
|
+
collisionObbDebugEnabled: false,
|
|
219
221
|
};
|
|
220
222
|
},
|
|
221
223
|
watch: {
|
|
@@ -224,6 +226,13 @@ export default {
|
|
|
224
226
|
this.detachTransformControls();
|
|
225
227
|
}
|
|
226
228
|
},
|
|
229
|
+
sceneBoundingBoxDebugEnabled(val) {
|
|
230
|
+
if (val) {
|
|
231
|
+
this.refreshSceneBoundingBoxHelper(true);
|
|
232
|
+
} else {
|
|
233
|
+
this.hideSceneBoundingBox();
|
|
234
|
+
}
|
|
235
|
+
},
|
|
227
236
|
},
|
|
228
237
|
beforeCreate() {
|
|
229
238
|
this.spaceUp = true;
|
|
@@ -360,11 +369,16 @@ export default {
|
|
|
360
369
|
wheelTimeout: null,
|
|
361
370
|
},
|
|
362
371
|
},
|
|
372
|
+
cameraControlMoveObserver: {
|
|
373
|
+
enabled: false,
|
|
374
|
+
interval: 500,
|
|
375
|
+
},
|
|
363
376
|
streamLoader: null,
|
|
364
377
|
// isPerformingInitialCentering: false, // 标记是否正在执行初始居中
|
|
365
378
|
isObserverEnabled: true, // 标记相机变更观察者是否启用
|
|
366
379
|
sceneBoxes: new Map(),
|
|
367
380
|
documentModelIds: new Map(),
|
|
381
|
+
documentSceneBoundsIds: new Map(),
|
|
368
382
|
outlineInstanceProxyMap: new Map(),
|
|
369
383
|
occlusionWorker: null,
|
|
370
384
|
occlusionWorkerRequestMap: new Map(),
|
|
@@ -1469,71 +1483,108 @@ export default {
|
|
|
1469
1483
|
updateGlobalSceneBoundingBox() {
|
|
1470
1484
|
const state = this.noObserver || {};
|
|
1471
1485
|
const occScene = state.occlusionState && state.occlusionState._occScene;
|
|
1486
|
+
const mergedBox = new this.THREE.Box3();
|
|
1487
|
+
let hasValidBox = false;
|
|
1488
|
+
const mergeBox = box => {
|
|
1489
|
+
if (!box || !box.isBox3 || box.isEmpty()) return;
|
|
1490
|
+
if (!hasValidBox) {
|
|
1491
|
+
mergedBox.copy(box);
|
|
1492
|
+
hasValidBox = true;
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
mergedBox.union(box);
|
|
1496
|
+
};
|
|
1472
1497
|
|
|
1473
|
-
//
|
|
1498
|
+
// 离屏遮挡场景只包含参与遮挡的模型,需要继续合并场景范围索引中的二维图形范围。
|
|
1474
1499
|
if (occScene && occScene.children && occScene.children.length > 0) {
|
|
1475
1500
|
const occBox = new this.THREE.Box3().setFromObject(occScene);
|
|
1476
|
-
|
|
1477
|
-
this.sceneBoundingBox = occBox;
|
|
1478
|
-
return this.sceneBoundingBox;
|
|
1479
|
-
}
|
|
1501
|
+
mergeBox(occBox);
|
|
1480
1502
|
}
|
|
1481
1503
|
|
|
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;
|
|
1504
|
+
// sceneBoundsIndex 包含二维和三维图形,是全场景 dolly 距离的主要范围来源。
|
|
1505
|
+
if (this._sceneBoundsIndex && this._sceneBoundsIndex.size > 0) {
|
|
1506
|
+
this._sceneBoundsIndex.forEach(box => {
|
|
1507
|
+
mergeBox(box);
|
|
1500
1508
|
});
|
|
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
1509
|
}
|
|
1516
1510
|
|
|
1517
1511
|
const boxes = Array.from((state.sceneBoxes && state.sceneBoxes.values()) || []);
|
|
1518
1512
|
if (boxes.length > 0) {
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
firstBox.union(boxes[i]);
|
|
1513
|
+
for (let i = 0; i < boxes.length; i++) {
|
|
1514
|
+
mergeBox(boxes[i]);
|
|
1522
1515
|
}
|
|
1523
|
-
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
if (hasValidBox) {
|
|
1519
|
+
this.sceneBoundingBox = mergedBox;
|
|
1520
|
+
this.refreshSceneBoundingBoxHelper(this.sceneBoundingBoxDebugEnabled);
|
|
1524
1521
|
return this.sceneBoundingBox;
|
|
1525
1522
|
}
|
|
1526
1523
|
|
|
1527
1524
|
this.sceneBoundingBox = new this.THREE.Box3();
|
|
1525
|
+
this.refreshSceneBoundingBoxHelper(this.sceneBoundingBoxDebugEnabled);
|
|
1528
1526
|
return this.sceneBoundingBox;
|
|
1529
1527
|
},
|
|
1528
|
+
upsertIndexedBox(indexMap, modelId, box) {
|
|
1529
|
+
if (!indexMap || !modelId || !box || !box.isBox3) return null;
|
|
1530
|
+
const key = String(modelId);
|
|
1531
|
+
const existingBox = indexMap.get(key);
|
|
1532
|
+
if (!existingBox) {
|
|
1533
|
+
indexMap.set(key, box);
|
|
1534
|
+
return box;
|
|
1535
|
+
}
|
|
1536
|
+
existingBox.union(box);
|
|
1537
|
+
return existingBox;
|
|
1538
|
+
},
|
|
1539
|
+
mergeModelBoxUserData(targetBox, sourceUserData) {
|
|
1540
|
+
if (!targetBox || !sourceUserData) return;
|
|
1541
|
+
if (!targetBox.userData) {
|
|
1542
|
+
targetBox.userData = sourceUserData;
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
const targetUserData = targetBox.userData;
|
|
1547
|
+
targetUserData.visible = targetUserData.visible !== false || sourceUserData.visible !== false;
|
|
1548
|
+
targetUserData.sourceVisible =
|
|
1549
|
+
targetUserData.sourceVisible !== false || sourceUserData.sourceVisible !== false;
|
|
1550
|
+
targetUserData.transparent = !!targetUserData.transparent && !!sourceUserData.transparent;
|
|
1551
|
+
targetUserData.mergedBoxCount = (targetUserData.mergedBoxCount || 1) + 1;
|
|
1552
|
+
// 同一模型包含多个图元时,单个图元的遮挡代理不再可靠,改由视锥体合并盒兜底加载。
|
|
1553
|
+
targetUserData.occlusionEnabled = false;
|
|
1554
|
+
|
|
1555
|
+
const targetObbs = Array.isArray(targetUserData.collisionObbs)
|
|
1556
|
+
? targetUserData.collisionObbs
|
|
1557
|
+
: [];
|
|
1558
|
+
const sourceObbs = Array.isArray(sourceUserData.collisionObbs)
|
|
1559
|
+
? sourceUserData.collisionObbs
|
|
1560
|
+
: [];
|
|
1561
|
+
if (sourceObbs.length > 0) {
|
|
1562
|
+
targetUserData.collisionObbs = targetObbs.concat(sourceObbs);
|
|
1563
|
+
}
|
|
1564
|
+
},
|
|
1565
|
+
upsertModelBox(modelId, box) {
|
|
1566
|
+
if (!this._boxIndex || !modelId || !box || !box.isBox3) return;
|
|
1567
|
+
const key = String(modelId);
|
|
1568
|
+
const existingBox = this._boxIndex.get(key);
|
|
1569
|
+
if (!existingBox) {
|
|
1570
|
+
this._boxIndex.set(key, box);
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
existingBox.union(box);
|
|
1574
|
+
this.mergeModelBoxUserData(existingBox, box.userData);
|
|
1575
|
+
},
|
|
1530
1576
|
setBoxIndex(boxJson, documentId, isAdd = true) {
|
|
1531
1577
|
if (!this._boxIndex) this._boxIndex = new Map();
|
|
1578
|
+
if (!this._sceneBoundsIndex) this._sceneBoundsIndex = new Map();
|
|
1532
1579
|
if (!documentId) {
|
|
1533
1580
|
this._boxIndex.clear();
|
|
1581
|
+
this._sceneBoundsIndex.clear();
|
|
1534
1582
|
if (this.noObserver && this.noObserver.documentModelIds) {
|
|
1535
1583
|
this.noObserver.documentModelIds.clear();
|
|
1536
1584
|
}
|
|
1585
|
+
if (this.noObserver && this.noObserver.documentSceneBoundsIds) {
|
|
1586
|
+
this.noObserver.documentSceneBoundsIds.clear();
|
|
1587
|
+
}
|
|
1537
1588
|
this.hasExecutedCentering = false;
|
|
1538
1589
|
this.buildOctreeFromBoxIndex();
|
|
1539
1590
|
return;
|
|
@@ -1542,19 +1593,35 @@ export default {
|
|
|
1542
1593
|
if (isAdd && boxJson) {
|
|
1543
1594
|
const arr = boxJson ? boxJson : [];
|
|
1544
1595
|
const modelIds = new Set();
|
|
1596
|
+
const sceneBoundIds = new Set();
|
|
1545
1597
|
const validGeomTypes = new Set(Object.values(GEOM_TYPES));
|
|
1546
1598
|
for (let i = 0; i < arr.length; i++) {
|
|
1547
1599
|
const it = arr[i];
|
|
1548
1600
|
if (!validGeomTypes.has(it.geomType)) {
|
|
1549
1601
|
continue;
|
|
1550
1602
|
}
|
|
1551
|
-
|
|
1603
|
+
const minSource = it.min;
|
|
1604
|
+
const maxSource = it.max;
|
|
1605
|
+
const hasValidMinMax =
|
|
1606
|
+
minSource &&
|
|
1607
|
+
maxSource &&
|
|
1608
|
+
minSource.length >= 3 &&
|
|
1609
|
+
maxSource.length >= 3 &&
|
|
1610
|
+
Number.isFinite(minSource[0]) &&
|
|
1611
|
+
Number.isFinite(minSource[1]) &&
|
|
1612
|
+
Number.isFinite(minSource[2]) &&
|
|
1613
|
+
Number.isFinite(maxSource[0]) &&
|
|
1614
|
+
Number.isFinite(maxSource[1]) &&
|
|
1615
|
+
Number.isFinite(maxSource[2]);
|
|
1616
|
+
if (!hasValidMinMax) {
|
|
1552
1617
|
continue;
|
|
1553
1618
|
}
|
|
1619
|
+
const obbData = it.obb || [];
|
|
1620
|
+
const modelId = String(it.id);
|
|
1554
1621
|
|
|
1555
1622
|
// 使用新的 min/max 数据结构
|
|
1556
|
-
const min = new this.THREE.Vector3(
|
|
1557
|
-
const max = new this.THREE.Vector3(
|
|
1623
|
+
const min = new this.THREE.Vector3(minSource[0], minSource[1], minSource[2]);
|
|
1624
|
+
const max = new this.THREE.Vector3(maxSource[0], maxSource[1], maxSource[2]);
|
|
1558
1625
|
|
|
1559
1626
|
const sourceBox = new this.THREE.Box3(min.clone(), max.clone());
|
|
1560
1627
|
// 八叉树仍使用 AABB 粗筛,最终碰撞使用 sourceBox 派生的 OBB 精筛
|
|
@@ -1588,10 +1655,18 @@ export default {
|
|
|
1588
1655
|
boxThree.applyMatrix4(collisionMatrix);
|
|
1589
1656
|
}
|
|
1590
1657
|
|
|
1658
|
+
this.upsertIndexedBox(this._sceneBoundsIndex, modelId, boxThree.clone());
|
|
1659
|
+
sceneBoundIds.add(modelId);
|
|
1660
|
+
|
|
1661
|
+
// 没有 OBB 数据的二维图形只参与全场景范围,不纳入碰撞和遮挡索引。
|
|
1662
|
+
if (obbData.length === 0) {
|
|
1663
|
+
continue;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1591
1666
|
const userData = {
|
|
1592
1667
|
flag: it.flag || 1, // 默认为 1
|
|
1593
1668
|
geomType: it.geomType,
|
|
1594
|
-
obbData
|
|
1669
|
+
obbData, // 存储原始 OBB 数据
|
|
1595
1670
|
transparent: it.transp > 0,
|
|
1596
1671
|
sourceVisible: it.visible === false ? false : true,
|
|
1597
1672
|
visible: it.visible === false ? false : true, // 默认显示
|
|
@@ -1605,8 +1680,7 @@ export default {
|
|
|
1605
1680
|
// userData.matrix = new this.THREE.Matrix4().identity();
|
|
1606
1681
|
}
|
|
1607
1682
|
// 如果是 flag=3,解析并存储中心点、半轴长、旋转矩阵
|
|
1608
|
-
else if (it.flag === 3 &&
|
|
1609
|
-
const obbData = it.obb;
|
|
1683
|
+
else if (it.flag === 3 && obbData.length === 15) {
|
|
1610
1684
|
const center = new this.THREE.Vector3(obbData[0], obbData[1], obbData[2]);
|
|
1611
1685
|
const halfSize = new this.THREE.Vector3(obbData[3], obbData[4], obbData[5]);
|
|
1612
1686
|
const rotation = new this.THREE.Matrix3().fromArray(obbData.slice(6, 15));
|
|
@@ -1636,13 +1710,13 @@ export default {
|
|
|
1636
1710
|
userData.collisionObbs = [collisionObb];
|
|
1637
1711
|
}
|
|
1638
1712
|
// 如果是 flag=2,解析并存储多个子包围盒 (min, max)
|
|
1639
|
-
else if (it.flag === 2 &&
|
|
1713
|
+
else if (it.flag === 2 && obbData.length > 0) {
|
|
1640
1714
|
const subBoxes = [];
|
|
1641
1715
|
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(
|
|
1716
|
+
for (let k = 0; k < obbData.length; k += 6) {
|
|
1717
|
+
if (k + 5 < obbData.length) {
|
|
1718
|
+
const min = new this.THREE.Vector3(obbData[k], obbData[k + 1], obbData[k + 2]);
|
|
1719
|
+
const max = new this.THREE.Vector3(obbData[k + 3], obbData[k + 4], obbData[k + 5]);
|
|
1646
1720
|
subBoxes.push({ min, max });
|
|
1647
1721
|
|
|
1648
1722
|
const subBox = new this.THREE.Box3(min.clone(), max.clone());
|
|
@@ -1669,11 +1743,11 @@ export default {
|
|
|
1669
1743
|
|
|
1670
1744
|
boxThree.userData = userData;
|
|
1671
1745
|
|
|
1672
|
-
|
|
1673
|
-
this._boxIndex.set(modelId, boxThree);
|
|
1746
|
+
this.upsertModelBox(modelId, boxThree);
|
|
1674
1747
|
modelIds.add(modelId);
|
|
1675
1748
|
}
|
|
1676
1749
|
this.noObserver.documentModelIds.set(documentId, modelIds);
|
|
1750
|
+
this.noObserver.documentSceneBoundsIds.set(documentId, sceneBoundIds);
|
|
1677
1751
|
} else {
|
|
1678
1752
|
const modelIds = this.noObserver.documentModelIds.get(documentId);
|
|
1679
1753
|
if (modelIds) {
|
|
@@ -1687,6 +1761,15 @@ export default {
|
|
|
1687
1761
|
this.hasExecutedCentering = false;
|
|
1688
1762
|
}
|
|
1689
1763
|
}
|
|
1764
|
+
const sceneBoundIds =
|
|
1765
|
+
this.noObserver.documentSceneBoundsIds &&
|
|
1766
|
+
this.noObserver.documentSceneBoundsIds.get(documentId);
|
|
1767
|
+
if (sceneBoundIds) {
|
|
1768
|
+
sceneBoundIds.forEach(id => {
|
|
1769
|
+
this._sceneBoundsIndex.delete(id);
|
|
1770
|
+
});
|
|
1771
|
+
this.noObserver.documentSceneBoundsIds.delete(documentId);
|
|
1772
|
+
}
|
|
1690
1773
|
}
|
|
1691
1774
|
this.buildOctreeFromBoxIndex();
|
|
1692
1775
|
this.refreshCollisionObbDebug();
|
|
@@ -1703,7 +1786,9 @@ export default {
|
|
|
1703
1786
|
this.sceneBoundingBox.isEmpty()
|
|
1704
1787
|
)
|
|
1705
1788
|
return;
|
|
1706
|
-
|
|
1789
|
+
const hasBoxIndex = this._boxIndex && this._boxIndex.size > 0;
|
|
1790
|
+
const hasSceneBoundsIndex = this._sceneBoundsIndex && this._sceneBoundsIndex.size > 0;
|
|
1791
|
+
if (!hasBoxIndex && !hasSceneBoundsIndex) return;
|
|
1707
1792
|
|
|
1708
1793
|
this.smartModelCenter(this.sceneBoundingBox, 0);
|
|
1709
1794
|
},
|
|
@@ -1805,6 +1890,53 @@ export default {
|
|
|
1805
1890
|
}
|
|
1806
1891
|
node.items.push({ id, box });
|
|
1807
1892
|
},
|
|
1893
|
+
_buildCollisionOctreeFromEntries(entries) {
|
|
1894
|
+
if (!Array.isArray(entries) || entries.length === 0) {
|
|
1895
|
+
return null;
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
let minX = Infinity;
|
|
1899
|
+
let minY = Infinity;
|
|
1900
|
+
let minZ = Infinity;
|
|
1901
|
+
let maxX = -Infinity;
|
|
1902
|
+
let maxY = -Infinity;
|
|
1903
|
+
let maxZ = -Infinity;
|
|
1904
|
+
|
|
1905
|
+
entries.forEach(item => {
|
|
1906
|
+
const box = item && item.box;
|
|
1907
|
+
if (!box || !box.isBox3) return;
|
|
1908
|
+
const mn = box.min;
|
|
1909
|
+
const mx = box.max;
|
|
1910
|
+
if (mn.x < minX) minX = mn.x;
|
|
1911
|
+
if (mn.y < minY) minY = mn.y;
|
|
1912
|
+
if (mn.z < minZ) minZ = mn.z;
|
|
1913
|
+
if (mx.x > maxX) maxX = mx.x;
|
|
1914
|
+
if (mx.y > maxY) maxY = mx.y;
|
|
1915
|
+
if (mx.z > maxZ) maxZ = mx.z;
|
|
1916
|
+
});
|
|
1917
|
+
|
|
1918
|
+
if (minX === Infinity) {
|
|
1919
|
+
return null;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
const rootBox = new this.THREE.Box3(
|
|
1923
|
+
new this.THREE.Vector3(minX, minY, minZ),
|
|
1924
|
+
new this.THREE.Vector3(maxX, maxY, maxZ)
|
|
1925
|
+
);
|
|
1926
|
+
const root = {
|
|
1927
|
+
box: rootBox,
|
|
1928
|
+
items: [],
|
|
1929
|
+
children: null,
|
|
1930
|
+
depth: 0,
|
|
1931
|
+
};
|
|
1932
|
+
|
|
1933
|
+
entries.forEach(item => {
|
|
1934
|
+
if (!item || !item.box) return;
|
|
1935
|
+
this._octreeInsert(root, String(item.id), item.box);
|
|
1936
|
+
});
|
|
1937
|
+
|
|
1938
|
+
return root;
|
|
1939
|
+
},
|
|
1808
1940
|
_isCollisionBoxAvailable(box) {
|
|
1809
1941
|
if (!box || !box.isBox3 || box.isEmpty()) return false;
|
|
1810
1942
|
if (box.userData && box.userData.visible === false) return false;
|
|
@@ -1935,7 +2067,8 @@ export default {
|
|
|
1935
2067
|
_buildCollisionPairMap() {
|
|
1936
2068
|
const pairMap = new Map();
|
|
1937
2069
|
const collisionPairs = this.getOctreeCollisionPairs();
|
|
1938
|
-
const pairs =
|
|
2070
|
+
const pairs =
|
|
2071
|
+
collisionPairs && Array.isArray(collisionPairs.pairs) ? collisionPairs.pairs : [];
|
|
1939
2072
|
for (let i = 0; i < pairs.length; i++) {
|
|
1940
2073
|
const pair = pairs[i];
|
|
1941
2074
|
if (!pair) continue;
|
|
@@ -2071,9 +2204,8 @@ export default {
|
|
|
2071
2204
|
}
|
|
2072
2205
|
return false;
|
|
2073
2206
|
},
|
|
2074
|
-
|
|
2207
|
+
_queryOctreeByBoxWithRoot(root, box) {
|
|
2075
2208
|
const results = [];
|
|
2076
|
-
const root = this._octree;
|
|
2077
2209
|
if (!root || !this._isCollisionBoxAvailable(box)) return results;
|
|
2078
2210
|
|
|
2079
2211
|
const stack = [root];
|
|
@@ -2099,25 +2231,88 @@ export default {
|
|
|
2099
2231
|
|
|
2100
2232
|
return results;
|
|
2101
2233
|
},
|
|
2102
|
-
|
|
2234
|
+
_queryOctreeByBox(box) {
|
|
2235
|
+
return this._queryOctreeByBoxWithRoot(this._octree, box);
|
|
2236
|
+
},
|
|
2237
|
+
getOctreeCollisionPairs(options = {}) {
|
|
2103
2238
|
const pairs = [];
|
|
2104
2239
|
if (!this._boxIndex || this._boxIndex.size === 0) return { pairs };
|
|
2105
2240
|
|
|
2106
|
-
|
|
2107
|
-
|
|
2241
|
+
const normalizeIds = value =>
|
|
2242
|
+
Array.isArray(value)
|
|
2243
|
+
? Array.from(
|
|
2244
|
+
new Set(
|
|
2245
|
+
value
|
|
2246
|
+
.map(item => (item === null || item === undefined ? '' : String(item)))
|
|
2247
|
+
.filter(item => item !== '')
|
|
2248
|
+
)
|
|
2249
|
+
)
|
|
2250
|
+
: [];
|
|
2251
|
+
|
|
2252
|
+
const getEntriesByIds = ids => {
|
|
2253
|
+
const entries = [];
|
|
2254
|
+
const idSet = new Set(ids);
|
|
2255
|
+
this._boxIndex.forEach((box, id) => {
|
|
2256
|
+
const key = String(id);
|
|
2257
|
+
if (idSet.has(key)) {
|
|
2258
|
+
entries.push({
|
|
2259
|
+
id: key,
|
|
2260
|
+
box,
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
});
|
|
2264
|
+
return entries;
|
|
2265
|
+
};
|
|
2266
|
+
|
|
2267
|
+
const sourceIds = normalizeIds(options.sourceIds);
|
|
2268
|
+
const targetIds = normalizeIds(options.targetIds);
|
|
2269
|
+
const includeIds = Array.isArray(options.includeIds) ? normalizeIds(options.includeIds) : [];
|
|
2270
|
+
|
|
2271
|
+
let root = this._octree;
|
|
2272
|
+
let sourceEntries = [];
|
|
2273
|
+
let dedupePairKeySet = null;
|
|
2274
|
+
|
|
2275
|
+
if (sourceIds.length > 0 && targetIds.length > 0) {
|
|
2276
|
+
const targetEntries = getEntriesByIds(targetIds);
|
|
2277
|
+
sourceEntries = getEntriesByIds(sourceIds);
|
|
2278
|
+
if (sourceEntries.length === 0 || targetEntries.length === 0) return { pairs };
|
|
2279
|
+
root = this._buildCollisionOctreeFromEntries(targetEntries);
|
|
2280
|
+
if (!root) return { pairs };
|
|
2281
|
+
dedupePairKeySet = new Set();
|
|
2282
|
+
} else if (includeIds.length > 0) {
|
|
2283
|
+
sourceEntries = getEntriesByIds(includeIds);
|
|
2284
|
+
if (sourceEntries.length === 0) return { pairs };
|
|
2285
|
+
root = this._buildCollisionOctreeFromEntries(sourceEntries);
|
|
2286
|
+
if (!root) return { pairs };
|
|
2287
|
+
} else {
|
|
2288
|
+
if (!this._octree) {
|
|
2289
|
+
this.buildOctreeFromBoxIndex();
|
|
2290
|
+
}
|
|
2291
|
+
if (!this._octree) return { pairs };
|
|
2292
|
+
this._boxIndex.forEach((box, id) => {
|
|
2293
|
+
sourceEntries.push({
|
|
2294
|
+
id: String(id),
|
|
2295
|
+
box,
|
|
2296
|
+
});
|
|
2297
|
+
});
|
|
2108
2298
|
}
|
|
2109
|
-
if (!this._octree) return { pairs };
|
|
2110
2299
|
|
|
2111
|
-
|
|
2300
|
+
sourceEntries.forEach(({ id: idA, box: boxA }) => {
|
|
2112
2301
|
if (!this._isCollisionBoxAvailable(boxA)) return;
|
|
2113
2302
|
|
|
2114
|
-
const hits = this.
|
|
2303
|
+
const hits = this._queryOctreeByBoxWithRoot(root, boxA);
|
|
2115
2304
|
for (let i = 0; i < hits.length; i++) {
|
|
2116
2305
|
const item = hits[i];
|
|
2117
2306
|
if (!item || String(item.id) === String(idA)) continue;
|
|
2118
2307
|
const boxB = item.box;
|
|
2119
2308
|
if (!this._isCollisionBoxMatched(boxA, boxB)) continue;
|
|
2120
2309
|
|
|
2310
|
+
if (dedupePairKeySet) {
|
|
2311
|
+
const pairKey = `${String(idA)}__${String(item.id)}`;
|
|
2312
|
+
if (dedupePairKeySet.has(pairKey)) continue;
|
|
2313
|
+
dedupePairKeySet.add(pairKey);
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2121
2316
|
pairs.push({
|
|
2122
2317
|
idA: this._formatCollisionPairId(idA),
|
|
2123
2318
|
idB: this._formatCollisionPairId(item.id),
|
|
@@ -2571,7 +2766,8 @@ export default {
|
|
|
2571
2766
|
/**
|
|
2572
2767
|
* 执行视椎体裁切,清理视椎体外的InstancedMesh实例
|
|
2573
2768
|
*/
|
|
2574
|
-
async performFrustumCulling() {
|
|
2769
|
+
async performFrustumCulling(options = {}) {
|
|
2770
|
+
const skipUnload = !!(options && options.skipUnload);
|
|
2575
2771
|
const modelState = this.noObserver
|
|
2576
2772
|
? this.noObserver.modelStateManager
|
|
2577
2773
|
: this.modelStateManager;
|
|
@@ -3340,7 +3536,7 @@ export default {
|
|
|
3340
3536
|
}
|
|
3341
3537
|
|
|
3342
3538
|
// 仅当所有实例都不活跃时,才执行卸载
|
|
3343
|
-
if (!this.firstPerSign && !this.roaming && allInactive) {
|
|
3539
|
+
if (!skipUnload && !this.firstPerSign && !this.roaming && allInactive) {
|
|
3344
3540
|
toUnload.push({ modelId, child });
|
|
3345
3541
|
}
|
|
3346
3542
|
}
|
|
@@ -3373,7 +3569,7 @@ export default {
|
|
|
3373
3569
|
if (modelInVisible) {
|
|
3374
3570
|
toLoadSet.delete(modelId);
|
|
3375
3571
|
}
|
|
3376
|
-
if (!this.firstPerSign && !this.roaming && (!inFrustum || !modelInVisible)) {
|
|
3572
|
+
if (!skipUnload && !this.firstPerSign && !this.roaming && (!inFrustum || !modelInVisible)) {
|
|
3377
3573
|
toUnload.push({ modelId, child });
|
|
3378
3574
|
}
|
|
3379
3575
|
});
|
|
@@ -3491,9 +3687,15 @@ export default {
|
|
|
3491
3687
|
* @param {Object} config - 配置项
|
|
3492
3688
|
* @param {Object} config.modelApi - 模型API接口
|
|
3493
3689
|
* @param {string} [config.projectId] - 项目ID
|
|
3690
|
+
* @param {Object} [config.cameraControlMoveObserver] - 普通视角运动中触发加载的配置
|
|
3691
|
+
* @param {boolean} [config.cameraControlMoveObserver.enabled=false] - 是否开启普通视角运动中加载
|
|
3692
|
+
* @param {number} [config.cameraControlMoveObserver.interval=500] - 触发间隔,单位毫秒
|
|
3693
|
+
* @param {boolean} [config.enableCameraControlMoveObserver=false] - 是否开启普通视角运动中加载
|
|
3694
|
+
* @param {number} [config.cameraControlMoveInterval=500] - 触发间隔,单位毫秒
|
|
3494
3695
|
*/
|
|
3495
3696
|
init(config = {}) {
|
|
3496
3697
|
console.log('init');
|
|
3698
|
+
this.setCameraControlMoveObserverConfig(config);
|
|
3497
3699
|
this.initStreamLoader(config.modelApi, config);
|
|
3498
3700
|
},
|
|
3499
3701
|
|
|
@@ -3920,6 +4122,124 @@ export default {
|
|
|
3920
4122
|
this.noObserver.isObserverEnabled = enabled;
|
|
3921
4123
|
},
|
|
3922
4124
|
|
|
4125
|
+
setCameraControlMoveObserverConfig(config = {}) {
|
|
4126
|
+
if (!this.noObserver) return;
|
|
4127
|
+
const nestedConfig = config.cameraControlMoveObserver || {};
|
|
4128
|
+
const enabled =
|
|
4129
|
+
typeof nestedConfig.enabled === 'boolean'
|
|
4130
|
+
? nestedConfig.enabled
|
|
4131
|
+
: typeof config.enableCameraControlMoveObserver === 'boolean'
|
|
4132
|
+
? config.enableCameraControlMoveObserver
|
|
4133
|
+
: false;
|
|
4134
|
+
const rawInterval =
|
|
4135
|
+
nestedConfig.interval !== undefined
|
|
4136
|
+
? nestedConfig.interval
|
|
4137
|
+
: config.cameraControlMoveInterval;
|
|
4138
|
+
const interval = Number(rawInterval);
|
|
4139
|
+
const safeInterval = Number.isFinite(interval) && interval > 0 ? interval : 500;
|
|
4140
|
+
|
|
4141
|
+
this.noObserver.cameraControlMoveObserver = {
|
|
4142
|
+
enabled,
|
|
4143
|
+
interval: safeInterval,
|
|
4144
|
+
};
|
|
4145
|
+
this.resetCameraControlMoveSnapshot(false);
|
|
4146
|
+
},
|
|
4147
|
+
|
|
4148
|
+
isCameraControlMoveObserverEnabled() {
|
|
4149
|
+
const config = this.noObserver && this.noObserver.cameraControlMoveObserver;
|
|
4150
|
+
return !!(config && config.enabled);
|
|
4151
|
+
},
|
|
4152
|
+
|
|
4153
|
+
getCameraControlTarget(target) {
|
|
4154
|
+
if (!target || !this.cameraControls) return false;
|
|
4155
|
+
if (typeof this.cameraControls.getTarget === 'function') {
|
|
4156
|
+
this.cameraControls.getTarget(target);
|
|
4157
|
+
return true;
|
|
4158
|
+
}
|
|
4159
|
+
if (this.cameraControls._target) {
|
|
4160
|
+
target.copy(this.cameraControls._target);
|
|
4161
|
+
return true;
|
|
4162
|
+
}
|
|
4163
|
+
return false;
|
|
4164
|
+
},
|
|
4165
|
+
|
|
4166
|
+
resetCameraControlMoveSnapshot(syncCurrent = false) {
|
|
4167
|
+
if (!this.noObserver) return;
|
|
4168
|
+
if (!this.noObserver._cameraControlMoveState) {
|
|
4169
|
+
this.noObserver._cameraControlMoveState = {
|
|
4170
|
+
position: new this.THREE.Vector3(),
|
|
4171
|
+
quaternion: new this.THREE.Quaternion(),
|
|
4172
|
+
target: new this.THREE.Vector3(),
|
|
4173
|
+
currentTarget: new this.THREE.Vector3(),
|
|
4174
|
+
initialized: false,
|
|
4175
|
+
hasTarget: false,
|
|
4176
|
+
};
|
|
4177
|
+
}
|
|
4178
|
+
const state = this.noObserver._cameraControlMoveState;
|
|
4179
|
+
if (!syncCurrent || !this.camera) {
|
|
4180
|
+
state.initialized = false;
|
|
4181
|
+
state.hasTarget = false;
|
|
4182
|
+
return;
|
|
4183
|
+
}
|
|
4184
|
+
|
|
4185
|
+
this.camera.updateMatrixWorld(true);
|
|
4186
|
+
state.position.copy(this.camera.position);
|
|
4187
|
+
state.quaternion.copy(this.camera.quaternion);
|
|
4188
|
+
state.hasTarget = this.getCameraControlTarget(state.target);
|
|
4189
|
+
state.initialized = true;
|
|
4190
|
+
},
|
|
4191
|
+
|
|
4192
|
+
observeCameraControlMove() {
|
|
4193
|
+
if (
|
|
4194
|
+
!this.noObserver ||
|
|
4195
|
+
!this.noObserver.isObserverEnabled ||
|
|
4196
|
+
!this.isCameraControlMoveObserverEnabled() ||
|
|
4197
|
+
!this.camera ||
|
|
4198
|
+
!this.cameraControls ||
|
|
4199
|
+
typeof this._cameraChangeObserver !== 'function'
|
|
4200
|
+
) {
|
|
4201
|
+
return;
|
|
4202
|
+
}
|
|
4203
|
+
|
|
4204
|
+
const loadingState = this.noObserver.batchLoadingState || {};
|
|
4205
|
+
const interactionState = loadingState.interactionState || {};
|
|
4206
|
+
const wheelActive = !!interactionState.wheelTimeout;
|
|
4207
|
+
const controlActive = !!this.noObserver.isControlActive;
|
|
4208
|
+
if ((!controlActive && !wheelActive) || this.firstPerSign) {
|
|
4209
|
+
this.resetCameraControlMoveSnapshot(false);
|
|
4210
|
+
return;
|
|
4211
|
+
}
|
|
4212
|
+
|
|
4213
|
+
if (!this.noObserver._cameraControlMoveState) {
|
|
4214
|
+
this.resetCameraControlMoveSnapshot(true);
|
|
4215
|
+
return;
|
|
4216
|
+
}
|
|
4217
|
+
|
|
4218
|
+
const state = this.noObserver._cameraControlMoveState;
|
|
4219
|
+
if (!state.initialized) {
|
|
4220
|
+
this.resetCameraControlMoveSnapshot(true);
|
|
4221
|
+
return;
|
|
4222
|
+
}
|
|
4223
|
+
|
|
4224
|
+
this.camera.updateMatrixWorld(true);
|
|
4225
|
+
const hasTarget = this.getCameraControlTarget(state.currentTarget);
|
|
4226
|
+
const positionMoved = state.position.distanceToSquared(this.camera.position) > 1e-8;
|
|
4227
|
+
const quaternionMoved = 1 - Math.abs(state.quaternion.dot(this.camera.quaternion)) > 1e-10;
|
|
4228
|
+
const targetMoved =
|
|
4229
|
+
hasTarget !== state.hasTarget ||
|
|
4230
|
+
(hasTarget && state.target.distanceToSquared(state.currentTarget) > 1e-8);
|
|
4231
|
+
|
|
4232
|
+
if (!positionMoved && !quaternionMoved && !targetMoved) return;
|
|
4233
|
+
|
|
4234
|
+
state.position.copy(this.camera.position);
|
|
4235
|
+
state.quaternion.copy(this.camera.quaternion);
|
|
4236
|
+
state.hasTarget = hasTarget;
|
|
4237
|
+
if (hasTarget) {
|
|
4238
|
+
state.target.copy(state.currentTarget);
|
|
4239
|
+
}
|
|
4240
|
+
this._cameraChangeObserver('controlMove');
|
|
4241
|
+
},
|
|
4242
|
+
|
|
3923
4243
|
/**
|
|
3924
4244
|
* 初始化相机变更观察者 (CameraChangeObserver)
|
|
3925
4245
|
* 统一监听:用户交互(Wheel/Control)、程序化动画、API调用
|
|
@@ -3954,7 +4274,9 @@ export default {
|
|
|
3954
4274
|
// 执行视锥体裁剪
|
|
3955
4275
|
// 第一视角不裁切
|
|
3956
4276
|
if (this.modelStateManager.frustumCheckEnabled) {
|
|
3957
|
-
await this.performFrustumCulling(
|
|
4277
|
+
await this.performFrustumCulling({
|
|
4278
|
+
skipUnload: source === 'controlMove',
|
|
4279
|
+
});
|
|
3958
4280
|
}
|
|
3959
4281
|
|
|
3960
4282
|
// 执行流式加载请求
|
|
@@ -3966,11 +4288,19 @@ export default {
|
|
|
3966
4288
|
}
|
|
3967
4289
|
},
|
|
3968
4290
|
source => {
|
|
3969
|
-
//
|
|
4291
|
+
// 漫游、第一人称移动、普通视角拖拽/缩放中,使用节流策略避免持续运动被防抖吞掉。
|
|
3970
4292
|
// 避免在持续运动中因防抖重置定时器而无法触发
|
|
3971
4293
|
if (source === 'roam' || source === 'firstPersonMove') {
|
|
3972
4294
|
return { type: 'throttle', limit: 1000 };
|
|
3973
4295
|
}
|
|
4296
|
+
if (source === 'controlMove') {
|
|
4297
|
+
const config = this.noObserver && this.noObserver.cameraControlMoveObserver;
|
|
4298
|
+
const interval =
|
|
4299
|
+
config && Number.isFinite(config.interval) && config.interval > 0
|
|
4300
|
+
? config.interval
|
|
4301
|
+
: 500;
|
|
4302
|
+
return { type: 'throttle', limit: interval };
|
|
4303
|
+
}
|
|
3974
4304
|
return 300; // 默认防抖 300ms
|
|
3975
4305
|
}
|
|
3976
4306
|
);
|
|
@@ -3984,8 +4314,20 @@ export default {
|
|
|
3984
4314
|
this._wheelHandler = res => {
|
|
3985
4315
|
this.$emit('wheelStart', res);
|
|
3986
4316
|
|
|
3987
|
-
|
|
3988
|
-
this.
|
|
4317
|
+
const cameraControlMoveEnabled = this.isCameraControlMoveObserverEnabled();
|
|
4318
|
+
const loadingState = this.noObserver
|
|
4319
|
+
? this.noObserver.batchLoadingState
|
|
4320
|
+
: this.batchLoadingState;
|
|
4321
|
+
const wheelAlreadyActive =
|
|
4322
|
+
loadingState &&
|
|
4323
|
+
loadingState.interactionState &&
|
|
4324
|
+
loadingState.interactionState.wheelTimeout;
|
|
4325
|
+
if (cameraControlMoveEnabled && !wheelAlreadyActive) {
|
|
4326
|
+
this.resetCameraControlMoveSnapshot(true);
|
|
4327
|
+
} else if (!cameraControlMoveEnabled) {
|
|
4328
|
+
// 未开启普通视角运动加载时,保持原有交互中断流程。
|
|
4329
|
+
this.beginInteraction('wheel');
|
|
4330
|
+
}
|
|
3989
4331
|
|
|
3990
4332
|
// 设置滚轮交互的结束检测
|
|
3991
4333
|
this.scheduleWheelInteractionEnd(res, 100);
|
|
@@ -4027,9 +4369,12 @@ export default {
|
|
|
4027
4369
|
// 4. 监听器 - Controls (用户拖拽/操作)
|
|
4028
4370
|
this._onControlStart = res => {
|
|
4029
4371
|
this.noObserver.isControlActive = true;
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4372
|
+
if (this.isCameraControlMoveObserverEnabled()) {
|
|
4373
|
+
this.resetCameraControlMoveSnapshot(true);
|
|
4374
|
+
} else {
|
|
4375
|
+
// 未开启普通视角运动加载时,保持原有交互中断流程。
|
|
4376
|
+
this.beginInteraction('this.camera');
|
|
4377
|
+
}
|
|
4033
4378
|
this.$emit('controlStart', res);
|
|
4034
4379
|
|
|
4035
4380
|
// 记录鼠标按下时的位置
|
|
@@ -4046,8 +4391,10 @@ export default {
|
|
|
4046
4391
|
|
|
4047
4392
|
this.$emit('controlEnd', res);
|
|
4048
4393
|
|
|
4049
|
-
|
|
4050
|
-
|
|
4394
|
+
if (!this.isCameraControlMoveObserverEnabled()) {
|
|
4395
|
+
// 未开启普通视角运动加载时,保持原有交互中断流程。
|
|
4396
|
+
this.endInteraction('this.camera', res, { immediateResume: false });
|
|
4397
|
+
}
|
|
4051
4398
|
|
|
4052
4399
|
// 记录鼠标抬起时的位置并计算是否发生了拖拽
|
|
4053
4400
|
const event = res.originalEvent || window.event;
|
|
@@ -4076,6 +4423,9 @@ export default {
|
|
|
4076
4423
|
// 触发观察者
|
|
4077
4424
|
this._cameraChangeObserver('control');
|
|
4078
4425
|
}
|
|
4426
|
+
if (this.isCameraControlMoveObserverEnabled()) {
|
|
4427
|
+
this.resetCameraControlMoveSnapshot(false);
|
|
4428
|
+
}
|
|
4079
4429
|
};
|
|
4080
4430
|
|
|
4081
4431
|
this.cameraControls.addEventListener('controlstart', this._onControlStart);
|
|
@@ -4136,12 +4486,37 @@ export default {
|
|
|
4136
4486
|
},
|
|
4137
4487
|
// 动态设置视角滚轮的距离
|
|
4138
4488
|
setCameraConfig() {
|
|
4489
|
+
if (
|
|
4490
|
+
!this.sceneBoundingBox ||
|
|
4491
|
+
!this.sceneBoundingBox.isBox3 ||
|
|
4492
|
+
this.sceneBoundingBox.isEmpty()
|
|
4493
|
+
) {
|
|
4494
|
+
return;
|
|
4495
|
+
}
|
|
4139
4496
|
// let box3 = new this.THREE.Box3().setFromObject(this.sceneBoundingBox);
|
|
4140
4497
|
let size = new this.THREE.Vector3();
|
|
4141
4498
|
this.sceneBoundingBox.getSize(size);
|
|
4142
4499
|
const maxBorder = Math.max(size.x, size.y, size.z);
|
|
4500
|
+
if (isDebug) {
|
|
4501
|
+
const center = this.sceneBoundingBox.getCenter(new this.THREE.Vector3());
|
|
4502
|
+
console.log('sceneBoundingBox 调试信息', {
|
|
4503
|
+
size: {
|
|
4504
|
+
x: size.x,
|
|
4505
|
+
y: size.y,
|
|
4506
|
+
z: size.z,
|
|
4507
|
+
},
|
|
4508
|
+
center: {
|
|
4509
|
+
x: center.x,
|
|
4510
|
+
y: center.y,
|
|
4511
|
+
z: center.z,
|
|
4512
|
+
},
|
|
4513
|
+
maxBorder,
|
|
4514
|
+
});
|
|
4515
|
+
this.refreshSceneBoundingBoxHelper(this.sceneBoundingBoxDebugEnabled);
|
|
4516
|
+
}
|
|
4143
4517
|
// this.cameraControls.this.camera.far = maxBorder * 10; // 设置相机的远裁剪面
|
|
4144
4518
|
this.cameraControls.minDistance = maxBorder * 0.2; // 动态设置视角滚轮的距离
|
|
4519
|
+
// this.cameraControls.minDistance = 1000; // 动态设置视角滚轮的距离
|
|
4145
4520
|
this.camera.updateProjectionMatrix();
|
|
4146
4521
|
},
|
|
4147
4522
|
// 获取mesh的中心点
|
|
@@ -4469,8 +4844,19 @@ export default {
|
|
|
4469
4844
|
if (this.threeMeasure) {
|
|
4470
4845
|
this.threeMeasure.updateParams(width, height);
|
|
4471
4846
|
}
|
|
4847
|
+
this.updateMeshLineResolution(width, height);
|
|
4472
4848
|
}
|
|
4473
4849
|
},
|
|
4850
|
+
updateMeshLineResolution(width, height) {
|
|
4851
|
+
if (!this.modelGroup || !width || !height) return;
|
|
4852
|
+
this.modelGroup.traverse(child => {
|
|
4853
|
+
const material = child && child.material;
|
|
4854
|
+
if (material && material.type === 'MeshLineMaterial' && material.resolution) {
|
|
4855
|
+
material.resolution.set(width, height);
|
|
4856
|
+
material.needsUpdate = true;
|
|
4857
|
+
}
|
|
4858
|
+
});
|
|
4859
|
+
},
|
|
4474
4860
|
setModelCenter(target, options = {}) {
|
|
4475
4861
|
// return
|
|
4476
4862
|
let box3;
|
|
@@ -6545,6 +6931,7 @@ export default {
|
|
|
6545
6931
|
});
|
|
6546
6932
|
}
|
|
6547
6933
|
this.cameraControls.enabled && this.cameraControls.update(this.timeStamp);
|
|
6934
|
+
this.observeCameraControlMove();
|
|
6548
6935
|
// 计算相机近裁面到包围盒当前面的距离
|
|
6549
6936
|
this.cameraTrack();
|
|
6550
6937
|
this.firstPerspective();
|
|
@@ -6755,6 +7142,13 @@ export default {
|
|
|
6755
7142
|
const loadingState = this.noObserver
|
|
6756
7143
|
? this.noObserver.batchLoadingState
|
|
6757
7144
|
: this.batchLoadingState;
|
|
7145
|
+
const rect = this.renderer
|
|
7146
|
+
? this.renderer.domElement.getBoundingClientRect()
|
|
7147
|
+
: { width: window.innerWidth, height: window.innerHeight };
|
|
7148
|
+
options.meshLineResolution = {
|
|
7149
|
+
width: rect.width || window.innerWidth || 1,
|
|
7150
|
+
height: rect.height || window.innerHeight || 1,
|
|
7151
|
+
};
|
|
6758
7152
|
// 如果已经在加载中,先停止之前的加载
|
|
6759
7153
|
if (loadingState.isLoading) {
|
|
6760
7154
|
this.stopBatchLoading('restart');
|
|
@@ -7318,8 +7712,15 @@ export default {
|
|
|
7318
7712
|
|
|
7319
7713
|
loadingState.interactionState.wheelTimeout = setTimeout(() => {
|
|
7320
7714
|
this.$emit('wheelEnd', event);
|
|
7321
|
-
this.endInteraction('wheel', event, { immediateResume: false });
|
|
7322
7715
|
loadingState.interactionState.wheelTimeout = null;
|
|
7716
|
+
if (this.isCameraControlMoveObserverEnabled()) {
|
|
7717
|
+
this.resetCameraControlMoveSnapshot(false);
|
|
7718
|
+
if (typeof this._cameraChangeObserver === 'function') {
|
|
7719
|
+
this._cameraChangeObserver('wheel');
|
|
7720
|
+
}
|
|
7721
|
+
} else {
|
|
7722
|
+
this.endInteraction('wheel', event, { immediateResume: false });
|
|
7723
|
+
}
|
|
7323
7724
|
}, delay);
|
|
7324
7725
|
},
|
|
7325
7726
|
|
|
@@ -7347,9 +7748,87 @@ export default {
|
|
|
7347
7748
|
/**
|
|
7348
7749
|
* 隐藏场景包围盒
|
|
7349
7750
|
*/
|
|
7751
|
+
refreshSceneBoundingBoxHelper(forceVisible = this.boundingBoxVisible) {
|
|
7752
|
+
if (this.sceneBoundingBoxHelper && this.scene) {
|
|
7753
|
+
this.scene.remove(this.sceneBoundingBoxHelper);
|
|
7754
|
+
this.sceneBoundingBoxHelper.geometry && this.sceneBoundingBoxHelper.geometry.dispose();
|
|
7755
|
+
if (Array.isArray(this.sceneBoundingBoxHelper.material)) {
|
|
7756
|
+
this.sceneBoundingBoxHelper.material.forEach(material => {
|
|
7757
|
+
material && material.dispose && material.dispose();
|
|
7758
|
+
});
|
|
7759
|
+
} else if (
|
|
7760
|
+
this.sceneBoundingBoxHelper.material &&
|
|
7761
|
+
this.sceneBoundingBoxHelper.material.dispose
|
|
7762
|
+
) {
|
|
7763
|
+
this.sceneBoundingBoxHelper.material.dispose();
|
|
7764
|
+
}
|
|
7765
|
+
this.sceneBoundingBoxHelper = null;
|
|
7766
|
+
}
|
|
7767
|
+
|
|
7768
|
+
if (
|
|
7769
|
+
!forceVisible ||
|
|
7770
|
+
!this.scene ||
|
|
7771
|
+
!this.sceneBoundingBox ||
|
|
7772
|
+
!this.sceneBoundingBox.isBox3 ||
|
|
7773
|
+
this.sceneBoundingBox.isEmpty()
|
|
7774
|
+
) {
|
|
7775
|
+
this.boundingBoxVisible = false;
|
|
7776
|
+
return false;
|
|
7777
|
+
}
|
|
7778
|
+
|
|
7779
|
+
const helper = new this.THREE.Box3Helper(this.sceneBoundingBox, 0x00ff88);
|
|
7780
|
+
helper.name = 'sceneBoundingBoxHelper';
|
|
7781
|
+
helper.renderOrder = 999;
|
|
7782
|
+
if (helper.material) {
|
|
7783
|
+
helper.material.depthTest = false;
|
|
7784
|
+
helper.material.transparent = true;
|
|
7785
|
+
helper.material.opacity = 0.9;
|
|
7786
|
+
}
|
|
7787
|
+
this.scene.add(helper);
|
|
7788
|
+
this.sceneBoundingBoxHelper = helper;
|
|
7789
|
+
this.boundingBoxVisible = true;
|
|
7790
|
+
return true;
|
|
7791
|
+
},
|
|
7792
|
+
|
|
7793
|
+
showSceneBoundingBox() {
|
|
7794
|
+
this.sceneBoundingBoxDebugEnabled = true;
|
|
7795
|
+
const shown = this.refreshSceneBoundingBoxHelper(true);
|
|
7796
|
+
if (!shown) {
|
|
7797
|
+
console.warn('sceneBoundingBox 无有效范围,无法绘制调试线框');
|
|
7798
|
+
return;
|
|
7799
|
+
}
|
|
7800
|
+
|
|
7801
|
+
const size = this.sceneBoundingBox.getSize(new this.THREE.Vector3());
|
|
7802
|
+
const center = this.sceneBoundingBox.getCenter(new this.THREE.Vector3());
|
|
7803
|
+
console.log('sceneBoundingBox 线框已绘制', {
|
|
7804
|
+
size: {
|
|
7805
|
+
x: size.x,
|
|
7806
|
+
y: size.y,
|
|
7807
|
+
z: size.z,
|
|
7808
|
+
},
|
|
7809
|
+
center: {
|
|
7810
|
+
x: center.x,
|
|
7811
|
+
y: center.y,
|
|
7812
|
+
z: center.z,
|
|
7813
|
+
},
|
|
7814
|
+
});
|
|
7815
|
+
},
|
|
7816
|
+
|
|
7350
7817
|
hideSceneBoundingBox() {
|
|
7818
|
+
this.sceneBoundingBoxDebugEnabled = false;
|
|
7351
7819
|
if (this.sceneBoundingBoxHelper && this.scene) {
|
|
7352
7820
|
this.scene.remove(this.sceneBoundingBoxHelper);
|
|
7821
|
+
this.sceneBoundingBoxHelper.geometry && this.sceneBoundingBoxHelper.geometry.dispose();
|
|
7822
|
+
if (Array.isArray(this.sceneBoundingBoxHelper.material)) {
|
|
7823
|
+
this.sceneBoundingBoxHelper.material.forEach(material => {
|
|
7824
|
+
material && material.dispose && material.dispose();
|
|
7825
|
+
});
|
|
7826
|
+
} else if (
|
|
7827
|
+
this.sceneBoundingBoxHelper.material &&
|
|
7828
|
+
this.sceneBoundingBoxHelper.material.dispose
|
|
7829
|
+
) {
|
|
7830
|
+
this.sceneBoundingBoxHelper.material.dispose();
|
|
7831
|
+
}
|
|
7353
7832
|
this.sceneBoundingBoxHelper = null;
|
|
7354
7833
|
this.boundingBoxVisible = false;
|
|
7355
7834
|
console.log('场景包围盒已隐藏');
|
|
@@ -7360,7 +7839,7 @@ export default {
|
|
|
7360
7839
|
* 切换场景包围盒显示状态
|
|
7361
7840
|
*/
|
|
7362
7841
|
toggleSceneBoundingBox() {
|
|
7363
|
-
if (this.
|
|
7842
|
+
if (this.sceneBoundingBoxDebugEnabled) {
|
|
7364
7843
|
this.hideSceneBoundingBox();
|
|
7365
7844
|
} else {
|
|
7366
7845
|
this.showSceneBoundingBox();
|