fl-web-component 2.0.19 → 2.1.1-beta.0
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.1.js.map +1 -1
- package/dist/fl-web-component.common.2.js.map +1 -1
- package/dist/fl-web-component.common.js +5871 -1872
- package/dist/fl-web-component.common.js.map +1 -1
- package/dist/fl-web-component.css +1 -1
- package/package.json +4 -3
- package/packages/components/com-flcanvas/components/entityFormatting.js +134 -1
- package/packages/components/com-flcanvas/index.vue +335 -239
- package/packages/components/com-graphics/index.vue +433 -35
- package/packages/components/com-graphics/mock.json +115 -0
- package/packages/utils/StreamLoader.js +250 -107
- package/packages/utils/StreamLoaderParser.worker.js +180 -72
- package/src/utils/dxf-parser/AutoCadColorIndex.js +265 -0
- package/src/utils/dxf-parser/DimStyleCodes.js +33 -0
- package/src/utils/dxf-parser/DxfArrayScanner.js +151 -0
- package/src/utils/dxf-parser/DxfParser.js +918 -0
- package/src/utils/dxf-parser/ExtendedDataParser.js +117 -0
- package/src/utils/dxf-parser/LICENSE +21 -0
- package/src/utils/dxf-parser/ParseHelpers.js +140 -0
- package/src/utils/dxf-parser/README.md +8 -0
- package/src/utils/dxf-parser/entities/3dface.js +83 -0
- package/src/utils/dxf-parser/entities/arc.js +38 -0
- package/src/utils/dxf-parser/entities/attdef.js +89 -0
- package/src/utils/dxf-parser/entities/attribute.js +109 -0
- package/src/utils/dxf-parser/entities/circle.js +43 -0
- package/src/utils/dxf-parser/entities/dimension.js +71 -0
- package/src/utils/dxf-parser/entities/ellipse.js +48 -0
- package/src/utils/dxf-parser/entities/hatch.js +348 -0
- package/src/utils/dxf-parser/entities/insert.js +57 -0
- package/src/utils/dxf-parser/entities/line.js +34 -0
- package/src/utils/dxf-parser/entities/lwpolyline.js +103 -0
- package/src/utils/dxf-parser/entities/mtext.js +57 -0
- package/src/utils/dxf-parser/entities/point.js +35 -0
- package/src/utils/dxf-parser/entities/polyline.js +92 -0
- package/src/utils/dxf-parser/entities/solid.js +40 -0
- package/src/utils/dxf-parser/entities/spline.js +70 -0
- package/src/utils/dxf-parser/entities/text.js +50 -0
- package/src/utils/dxf-parser/entities/vertex.js +62 -0
- package/src/utils/flgltf-parser.js +21 -9
- package/src/utils/instance-parser.js +59 -59
- package/src/utils/threejs/measure-clear-distance.js +149 -123
- package/packages/components/com-graphics/box.json +0 -77
|
@@ -138,6 +138,7 @@ import MeasureHeight from '@/utils/threejs/measure-height.js';
|
|
|
138
138
|
import MeasureClearDistance from '@/utils/threejs/measure-clear-distance.js';
|
|
139
139
|
import { parseData, processMeshData, processNodeData } from '@/utils/flgltf-parser';
|
|
140
140
|
import {
|
|
141
|
+
GEOM_TYPES,
|
|
141
142
|
handleInstancedMeshModel,
|
|
142
143
|
resetProcessingState,
|
|
143
144
|
PRIMITIVE_TYPE,
|
|
@@ -154,7 +155,7 @@ import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js';
|
|
|
154
155
|
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js';
|
|
155
156
|
import Stats from 'three/examples/jsm/libs/stats.module.js';
|
|
156
157
|
import { OBB } from 'three/examples/jsm/math/OBB.js';
|
|
157
|
-
import boxJson from './box.json';
|
|
158
|
+
// import boxJson from './box.json';
|
|
158
159
|
import { StreamLoader } from '../../utils/StreamLoader.js';
|
|
159
160
|
import StreamLoaderParserWorker from '../../utils/StreamLoaderParser.worker.js';
|
|
160
161
|
import SceneCommandService from '@/utils/threejs/editor/scene-command-service.js';
|
|
@@ -164,7 +165,9 @@ import { onContextHandle } from './component/context';
|
|
|
164
165
|
|
|
165
166
|
const isDebug = process.env.NODE_ENV !== 'production' || process.env.VUE_APP_IS_WATCH === true;
|
|
166
167
|
// const isDebug = false;
|
|
167
|
-
const COLLISION_PENETRATION_EPSILON =
|
|
168
|
+
const COLLISION_PENETRATION_EPSILON = 10;
|
|
169
|
+
const COLLISION_OBB_AXIS_EPSILON = 1e-8;
|
|
170
|
+
const COLLISION_DEBUG_GROUP_NAME = '__collision_obb_debug_group__';
|
|
168
171
|
|
|
169
172
|
export default {
|
|
170
173
|
name: 'FlModel',
|
|
@@ -212,6 +215,7 @@ export default {
|
|
|
212
215
|
isPaused: false,
|
|
213
216
|
},
|
|
214
217
|
isolateMode: false,
|
|
218
|
+
collisionObbDebugEnabled: true,
|
|
215
219
|
};
|
|
216
220
|
},
|
|
217
221
|
watch: {
|
|
@@ -253,6 +257,7 @@ export default {
|
|
|
253
257
|
'centeringDebounceTimer',
|
|
254
258
|
'sceneBoundingBox',
|
|
255
259
|
'sceneBoundingBoxHelper',
|
|
260
|
+
'collisionObbDebugGroup',
|
|
256
261
|
];
|
|
257
262
|
arr.forEach(item => {
|
|
258
263
|
this[item] = null;
|
|
@@ -273,6 +278,7 @@ export default {
|
|
|
273
278
|
'needsCenteringAfterInteraction',
|
|
274
279
|
'isDragging',
|
|
275
280
|
'boundingBoxVisible',
|
|
281
|
+
'collisionObbDebugEnabled',
|
|
276
282
|
].forEach(item => {
|
|
277
283
|
this[item] = false;
|
|
278
284
|
});
|
|
@@ -1536,8 +1542,12 @@ export default {
|
|
|
1536
1542
|
if (isAdd && boxJson) {
|
|
1537
1543
|
const arr = boxJson ? boxJson : [];
|
|
1538
1544
|
const modelIds = new Set();
|
|
1545
|
+
const validGeomTypes = new Set(Object.values(GEOM_TYPES));
|
|
1539
1546
|
for (let i = 0; i < arr.length; i++) {
|
|
1540
1547
|
const it = arr[i];
|
|
1548
|
+
if (!validGeomTypes.has(it.geomType)) {
|
|
1549
|
+
continue;
|
|
1550
|
+
}
|
|
1541
1551
|
if (it.obb.length == 0) {
|
|
1542
1552
|
continue;
|
|
1543
1553
|
}
|
|
@@ -1546,32 +1556,52 @@ export default {
|
|
|
1546
1556
|
const min = new this.THREE.Vector3(it.min[0], it.min[1], it.min[2]);
|
|
1547
1557
|
const max = new this.THREE.Vector3(it.max[0], it.max[1], it.max[2]);
|
|
1548
1558
|
|
|
1549
|
-
|
|
1550
|
-
|
|
1559
|
+
const sourceBox = new this.THREE.Box3(min.clone(), max.clone());
|
|
1560
|
+
// 八叉树仍使用 AABB 粗筛,最终碰撞使用 sourceBox 派生的 OBB 精筛
|
|
1561
|
+
const boxThree = sourceBox.clone();
|
|
1562
|
+
|
|
1563
|
+
const hasMatrix = Array.isArray(it.matrix) && it.matrix.length >= 16;
|
|
1564
|
+
const hasMeshMatrix = Array.isArray(it.meshMatrix) && it.meshMatrix.length >= 16;
|
|
1565
|
+
let localMatrix = null;
|
|
1566
|
+
let collisionMatrix = null;
|
|
1567
|
+
if (hasMatrix || hasMeshMatrix) {
|
|
1568
|
+
localMatrix = new this.THREE.Matrix4();
|
|
1569
|
+
if (hasMeshMatrix) {
|
|
1570
|
+
localMatrix.fromArray(it.meshMatrix);
|
|
1571
|
+
} else {
|
|
1572
|
+
localMatrix.identity();
|
|
1573
|
+
}
|
|
1574
|
+
if (hasMatrix) {
|
|
1575
|
+
const matrix = new this.THREE.Matrix4().fromArray(it.matrix);
|
|
1576
|
+
localMatrix.multiply(matrix);
|
|
1577
|
+
}
|
|
1551
1578
|
|
|
1552
|
-
|
|
1553
|
-
if (hasMatrix && it.matrix.length >= 16) {
|
|
1554
|
-
const matrix = new this.THREE.Matrix4().fromArray(it.matrix);
|
|
1555
|
-
const worldMatrix = matrix.clone();
|
|
1579
|
+
const worldMatrix = localMatrix.clone();
|
|
1556
1580
|
if (this.bizToThreeMatrix) {
|
|
1557
1581
|
worldMatrix.premultiply(this.bizToThreeMatrix);
|
|
1558
1582
|
}
|
|
1583
|
+
collisionMatrix = worldMatrix;
|
|
1559
1584
|
boxThree.applyMatrix4(worldMatrix);
|
|
1560
1585
|
} else if (this.bizToThreeMatrix) {
|
|
1561
1586
|
// 注意:applyMatrix4 会重新计算 AABB
|
|
1562
|
-
|
|
1587
|
+
collisionMatrix = this.bizToThreeMatrix.clone();
|
|
1588
|
+
boxThree.applyMatrix4(collisionMatrix);
|
|
1563
1589
|
}
|
|
1564
1590
|
|
|
1565
1591
|
const userData = {
|
|
1566
1592
|
flag: it.flag || 1, // 默认为 1
|
|
1593
|
+
geomType: it.geomType,
|
|
1567
1594
|
obbData: it.obb, // 存储原始 OBB 数据
|
|
1568
1595
|
transparent: it.transp > 0,
|
|
1569
1596
|
sourceVisible: it.visible === false ? false : true,
|
|
1570
1597
|
visible: it.visible === false ? false : true, // 默认显示
|
|
1598
|
+
occlusionEnabled:
|
|
1599
|
+
it.geomType === GEOM_TYPES.geom_3d || it.geomType === GEOM_TYPES.geom_3d_obj,
|
|
1571
1600
|
};
|
|
1572
1601
|
if (it.flag === 1) {
|
|
1573
1602
|
userData.indices = it.indices;
|
|
1574
|
-
userData.matrix = it.matrix;
|
|
1603
|
+
userData.matrix = localMatrix ? localMatrix.toArray() : it.matrix;
|
|
1604
|
+
userData.meshMatrix = it.meshMatrix;
|
|
1575
1605
|
// userData.matrix = new this.THREE.Matrix4().identity();
|
|
1576
1606
|
}
|
|
1577
1607
|
// 如果是 flag=3,解析并存储中心点、半轴长、旋转矩阵
|
|
@@ -1589,24 +1619,52 @@ export default {
|
|
|
1589
1619
|
center.z
|
|
1590
1620
|
);
|
|
1591
1621
|
obbMatrix.multiply(rotationMatrix4);
|
|
1622
|
+
if (localMatrix) {
|
|
1623
|
+
obbMatrix.premultiply(localMatrix);
|
|
1624
|
+
}
|
|
1592
1625
|
|
|
1593
1626
|
userData.obb = {
|
|
1594
1627
|
matrix: obbMatrix,
|
|
1595
1628
|
center: center,
|
|
1596
1629
|
halfSize: halfSize,
|
|
1597
1630
|
};
|
|
1631
|
+
|
|
1632
|
+
const collisionObb = new OBB(center.clone(), halfSize.clone(), rotation.clone());
|
|
1633
|
+
if (collisionMatrix) {
|
|
1634
|
+
collisionObb.applyMatrix4(collisionMatrix);
|
|
1635
|
+
}
|
|
1636
|
+
userData.collisionObbs = [collisionObb];
|
|
1598
1637
|
}
|
|
1599
1638
|
// 如果是 flag=2,解析并存储多个子包围盒 (min, max)
|
|
1600
1639
|
else if (it.flag === 2 && it.obb && it.obb.length > 0) {
|
|
1601
1640
|
const subBoxes = [];
|
|
1641
|
+
const collisionObbs = [];
|
|
1602
1642
|
for (let k = 0; k < it.obb.length; k += 6) {
|
|
1603
1643
|
if (k + 5 < it.obb.length) {
|
|
1604
1644
|
const min = new this.THREE.Vector3(it.obb[k], it.obb[k + 1], it.obb[k + 2]);
|
|
1605
1645
|
const max = new this.THREE.Vector3(it.obb[k + 3], it.obb[k + 4], it.obb[k + 5]);
|
|
1606
1646
|
subBoxes.push({ min, max });
|
|
1647
|
+
|
|
1648
|
+
const subBox = new this.THREE.Box3(min.clone(), max.clone());
|
|
1649
|
+
const collisionObb = new OBB().fromBox3(subBox);
|
|
1650
|
+
if (collisionMatrix) {
|
|
1651
|
+
collisionObb.applyMatrix4(collisionMatrix);
|
|
1652
|
+
}
|
|
1653
|
+
collisionObbs.push(collisionObb);
|
|
1607
1654
|
}
|
|
1608
1655
|
}
|
|
1609
1656
|
userData.subBoxes = subBoxes;
|
|
1657
|
+
if (collisionObbs.length > 0) {
|
|
1658
|
+
userData.collisionObbs = collisionObbs;
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
if (!userData.collisionObbs || userData.collisionObbs.length === 0) {
|
|
1663
|
+
const collisionObb = new OBB().fromBox3(sourceBox);
|
|
1664
|
+
if (collisionMatrix) {
|
|
1665
|
+
collisionObb.applyMatrix4(collisionMatrix);
|
|
1666
|
+
}
|
|
1667
|
+
userData.collisionObbs = [collisionObb];
|
|
1610
1668
|
}
|
|
1611
1669
|
|
|
1612
1670
|
boxThree.userData = userData;
|
|
@@ -1631,8 +1689,12 @@ export default {
|
|
|
1631
1689
|
}
|
|
1632
1690
|
}
|
|
1633
1691
|
this.buildOctreeFromBoxIndex();
|
|
1692
|
+
this.refreshCollisionObbDebug();
|
|
1634
1693
|
console.log('time end', Date.now());
|
|
1635
1694
|
},
|
|
1695
|
+
shouldApplyOcclusionByBox(box) {
|
|
1696
|
+
return !!(box && box.userData && box.userData.occlusionEnabled);
|
|
1697
|
+
},
|
|
1636
1698
|
tryInitialCenterAfterBoundsReady() {
|
|
1637
1699
|
if (this.hasExecutedCentering || this.userInteracting) return;
|
|
1638
1700
|
if (
|
|
@@ -1753,17 +1815,261 @@ export default {
|
|
|
1753
1815
|
const numberValue = Number(value);
|
|
1754
1816
|
return Number.isFinite(numberValue) ? numberValue : id;
|
|
1755
1817
|
},
|
|
1756
|
-
|
|
1757
|
-
const x = Math.min(boxA.max.x - boxB.min.x, boxB.max.x - boxA.min.x);
|
|
1758
|
-
const y = Math.min(boxA.max.y - boxB.min.y, boxB.max.y - boxA.min.y);
|
|
1759
|
-
const z = Math.min(boxA.max.z - boxB.min.z, boxB.max.z - boxA.min.z);
|
|
1760
|
-
return Math.min(x, y, z);
|
|
1761
|
-
},
|
|
1762
|
-
_isCollisionBoxMatched(boxA, boxB) {
|
|
1818
|
+
_isCollisionAabbOverlapped(boxA, boxB) {
|
|
1763
1819
|
if (!this._isCollisionBoxAvailable(boxA) || !this._isCollisionBoxAvailable(boxB))
|
|
1764
1820
|
return false;
|
|
1765
|
-
|
|
1766
|
-
|
|
1821
|
+
return boxA.intersectsBox(boxB);
|
|
1822
|
+
},
|
|
1823
|
+
_getCollisionObbs(box) {
|
|
1824
|
+
if (!this._isCollisionBoxAvailable(box)) return [];
|
|
1825
|
+
const userData = box.userData || {};
|
|
1826
|
+
if (Array.isArray(userData.collisionObbs) && userData.collisionObbs.length > 0) {
|
|
1827
|
+
return userData.collisionObbs;
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
const fallbackCollisionObb = new OBB().fromBox3(box);
|
|
1831
|
+
userData.collisionObbs = [fallbackCollisionObb];
|
|
1832
|
+
box.userData = userData;
|
|
1833
|
+
return userData.collisionObbs;
|
|
1834
|
+
},
|
|
1835
|
+
_getCollisionDebugGroup() {
|
|
1836
|
+
if (!this.scene) return null;
|
|
1837
|
+
if (this.collisionObbDebugGroup && this.collisionObbDebugGroup.parent === this.scene) {
|
|
1838
|
+
return this.collisionObbDebugGroup;
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
const group = new this.THREE.Group();
|
|
1842
|
+
group.name = COLLISION_DEBUG_GROUP_NAME;
|
|
1843
|
+
group.userData.transformControlHelper = true;
|
|
1844
|
+
group.userData.collisionObbDebugHelper = true;
|
|
1845
|
+
this.scene.add(group);
|
|
1846
|
+
this.collisionObbDebugGroup = group;
|
|
1847
|
+
return group;
|
|
1848
|
+
},
|
|
1849
|
+
_createCollisionObbLineSegments(obb, color) {
|
|
1850
|
+
if (!obb) return null;
|
|
1851
|
+
|
|
1852
|
+
const axisInfo = this._getObbAxisInfo(obb);
|
|
1853
|
+
const center = axisInfo.center;
|
|
1854
|
+
const axes = axisInfo.axes;
|
|
1855
|
+
const halfSizes = axisInfo.halfSizes;
|
|
1856
|
+
|
|
1857
|
+
const corners = [];
|
|
1858
|
+
for (let x = -1; x <= 1; x += 2) {
|
|
1859
|
+
for (let y = -1; y <= 1; y += 2) {
|
|
1860
|
+
for (let z = -1; z <= 1; z += 2) {
|
|
1861
|
+
const corner = new this.THREE.Vector3().copy(center);
|
|
1862
|
+
corner.add(axes[0].clone().multiplyScalar(halfSizes[0] * x));
|
|
1863
|
+
corner.add(axes[1].clone().multiplyScalar(halfSizes[1] * y));
|
|
1864
|
+
corner.add(axes[2].clone().multiplyScalar(halfSizes[2] * z));
|
|
1865
|
+
corners.push(corner);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
const edgeIndices = [
|
|
1871
|
+
[0, 1],
|
|
1872
|
+
[0, 2],
|
|
1873
|
+
[0, 4],
|
|
1874
|
+
[1, 3],
|
|
1875
|
+
[1, 5],
|
|
1876
|
+
[2, 3],
|
|
1877
|
+
[2, 6],
|
|
1878
|
+
[3, 7],
|
|
1879
|
+
[4, 5],
|
|
1880
|
+
[4, 6],
|
|
1881
|
+
[5, 7],
|
|
1882
|
+
[6, 7],
|
|
1883
|
+
];
|
|
1884
|
+
const positions = new Float32Array(edgeIndices.length * 2 * 3);
|
|
1885
|
+
let offset = 0;
|
|
1886
|
+
for (let i = 0; i < edgeIndices.length; i++) {
|
|
1887
|
+
const start = corners[edgeIndices[i][0]];
|
|
1888
|
+
const end = corners[edgeIndices[i][1]];
|
|
1889
|
+
positions[offset++] = start.x;
|
|
1890
|
+
positions[offset++] = start.y;
|
|
1891
|
+
positions[offset++] = start.z;
|
|
1892
|
+
positions[offset++] = end.x;
|
|
1893
|
+
positions[offset++] = end.y;
|
|
1894
|
+
positions[offset++] = end.z;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
const geometry = new this.THREE.BufferGeometry();
|
|
1898
|
+
geometry.setAttribute('position', new this.THREE.BufferAttribute(positions, 3));
|
|
1899
|
+
const material = new this.THREE.LineBasicMaterial({
|
|
1900
|
+
color,
|
|
1901
|
+
transparent: true,
|
|
1902
|
+
opacity: 0.95,
|
|
1903
|
+
depthTest: false,
|
|
1904
|
+
depthWrite: false,
|
|
1905
|
+
toneMapped: false,
|
|
1906
|
+
});
|
|
1907
|
+
const lineSegments = new this.THREE.LineSegments(geometry, material);
|
|
1908
|
+
lineSegments.renderOrder = 999;
|
|
1909
|
+
lineSegments.userData.transformControlHelper = true;
|
|
1910
|
+
lineSegments.userData.collisionObbDebugHelper = true;
|
|
1911
|
+
return lineSegments;
|
|
1912
|
+
},
|
|
1913
|
+
_clearCollisionObbDebugGroup(removeGroup = false) {
|
|
1914
|
+
const group = this.collisionObbDebugGroup;
|
|
1915
|
+
if (!group) return;
|
|
1916
|
+
|
|
1917
|
+
while (group.children.length > 0) {
|
|
1918
|
+
const child = group.children[group.children.length - 1];
|
|
1919
|
+
group.remove(child);
|
|
1920
|
+
if (child.geometry) child.geometry.dispose();
|
|
1921
|
+
if (child.material) child.material.dispose();
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
if (removeGroup && this.scene && group.parent === this.scene) {
|
|
1925
|
+
this.scene.remove(group);
|
|
1926
|
+
this.collisionObbDebugGroup = null;
|
|
1927
|
+
} else if (!this.scene) {
|
|
1928
|
+
this.collisionObbDebugGroup = null;
|
|
1929
|
+
}
|
|
1930
|
+
},
|
|
1931
|
+
hideCollisionObbDebug() {
|
|
1932
|
+
this._clearCollisionObbDebugGroup();
|
|
1933
|
+
this.collisionObbDebugEnabled = false;
|
|
1934
|
+
},
|
|
1935
|
+
_buildCollisionPairMap() {
|
|
1936
|
+
const pairMap = new Map();
|
|
1937
|
+
const collisionPairs = this.getOctreeCollisionPairs();
|
|
1938
|
+
const pairs = collisionPairs && Array.isArray(collisionPairs.pairs) ? collisionPairs.pairs : [];
|
|
1939
|
+
for (let i = 0; i < pairs.length; i++) {
|
|
1940
|
+
const pair = pairs[i];
|
|
1941
|
+
if (!pair) continue;
|
|
1942
|
+
const idA = String(pair.idA);
|
|
1943
|
+
const idB = String(pair.idB);
|
|
1944
|
+
if (!idA || !idB) continue;
|
|
1945
|
+
if (!pairMap.has(idA)) pairMap.set(idA, new Set());
|
|
1946
|
+
if (!pairMap.has(idB)) pairMap.set(idB, new Set());
|
|
1947
|
+
pairMap.get(idA).add(idB);
|
|
1948
|
+
pairMap.get(idB).add(idA);
|
|
1949
|
+
}
|
|
1950
|
+
return pairMap;
|
|
1951
|
+
},
|
|
1952
|
+
refreshCollisionObbDebug() {
|
|
1953
|
+
if (!this.collisionObbDebugEnabled) return;
|
|
1954
|
+
const group = this._getCollisionDebugGroup();
|
|
1955
|
+
if (!group) return;
|
|
1956
|
+
|
|
1957
|
+
this._clearCollisionObbDebugGroup(false);
|
|
1958
|
+
const pairMap = this._buildCollisionPairMap();
|
|
1959
|
+
if (pairMap.size === 0) return;
|
|
1960
|
+
|
|
1961
|
+
pairMap.forEach((relatedIds, modelId) => {
|
|
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;
|
|
1968
|
+
if (!this._isCollisionBoxAvailable(box)) return;
|
|
1969
|
+
|
|
1970
|
+
const obbs = this._getCollisionObbs(box);
|
|
1971
|
+
if (!obbs.length) return;
|
|
1972
|
+
|
|
1973
|
+
const isColliding = relatedIds && relatedIds.size > 0;
|
|
1974
|
+
const color = isColliding ? 0xff3b30 : 0x36cfc9;
|
|
1975
|
+
for (let i = 0; i < obbs.length; i++) {
|
|
1976
|
+
const lineSegments = this._createCollisionObbLineSegments(obbs[i], color);
|
|
1977
|
+
if (!lineSegments) continue;
|
|
1978
|
+
lineSegments.name = `${COLLISION_DEBUG_GROUP_NAME}:${modelId}:${i}`;
|
|
1979
|
+
lineSegments.userData.modelId = modelId;
|
|
1980
|
+
lineSegments.userData.colliding = isColliding;
|
|
1981
|
+
group.add(lineSegments);
|
|
1982
|
+
}
|
|
1983
|
+
});
|
|
1984
|
+
},
|
|
1985
|
+
showCollisionObbDebug() {
|
|
1986
|
+
this.collisionObbDebugEnabled = true;
|
|
1987
|
+
this.refreshCollisionObbDebug();
|
|
1988
|
+
},
|
|
1989
|
+
toggleCollisionObbDebug(forceVisible) {
|
|
1990
|
+
const nextVisible =
|
|
1991
|
+
typeof forceVisible === 'boolean' ? forceVisible : !this.collisionObbDebugEnabled;
|
|
1992
|
+
if (nextVisible) {
|
|
1993
|
+
this.showCollisionObbDebug();
|
|
1994
|
+
} else {
|
|
1995
|
+
this.hideCollisionObbDebug();
|
|
1996
|
+
}
|
|
1997
|
+
},
|
|
1998
|
+
_getObbAxisInfo(obb) {
|
|
1999
|
+
const xAxis = new this.THREE.Vector3();
|
|
2000
|
+
const yAxis = new this.THREE.Vector3();
|
|
2001
|
+
const zAxis = new this.THREE.Vector3();
|
|
2002
|
+
obb.rotation.extractBasis(xAxis, yAxis, zAxis);
|
|
2003
|
+
return {
|
|
2004
|
+
center: obb.center,
|
|
2005
|
+
halfSizes: [obb.halfSize.x, obb.halfSize.y, obb.halfSize.z],
|
|
2006
|
+
axes: [xAxis, yAxis, zAxis],
|
|
2007
|
+
};
|
|
2008
|
+
},
|
|
2009
|
+
_getObbProjectionRadius(axisInfo, axis) {
|
|
2010
|
+
return (
|
|
2011
|
+
axisInfo.halfSizes[0] * Math.abs(axis.dot(axisInfo.axes[0])) +
|
|
2012
|
+
axisInfo.halfSizes[1] * Math.abs(axis.dot(axisInfo.axes[1])) +
|
|
2013
|
+
axisInfo.halfSizes[2] * Math.abs(axis.dot(axisInfo.axes[2]))
|
|
2014
|
+
);
|
|
2015
|
+
},
|
|
2016
|
+
_getObbMinPenetration(obbA, obbB) {
|
|
2017
|
+
const axisInfoA = this._getObbAxisInfo(obbA);
|
|
2018
|
+
const axisInfoB = this._getObbAxisInfo(obbB);
|
|
2019
|
+
const centerDelta = new this.THREE.Vector3().subVectors(axisInfoB.center, axisInfoA.center);
|
|
2020
|
+
let minPenetration = Infinity;
|
|
2021
|
+
|
|
2022
|
+
const testAxis = axis => {
|
|
2023
|
+
const lengthSq = axis.lengthSq();
|
|
2024
|
+
if (lengthSq <= COLLISION_OBB_AXIS_EPSILON) return true;
|
|
2025
|
+
|
|
2026
|
+
const normalizedAxis = axis.clone().multiplyScalar(1 / Math.sqrt(lengthSq));
|
|
2027
|
+
const projectionDistance = Math.abs(centerDelta.dot(normalizedAxis));
|
|
2028
|
+
const penetration =
|
|
2029
|
+
this._getObbProjectionRadius(axisInfoA, normalizedAxis) +
|
|
2030
|
+
this._getObbProjectionRadius(axisInfoB, normalizedAxis) -
|
|
2031
|
+
projectionDistance;
|
|
2032
|
+
|
|
2033
|
+
if (penetration <= 0) {
|
|
2034
|
+
minPenetration = penetration;
|
|
2035
|
+
return false;
|
|
2036
|
+
}
|
|
2037
|
+
if (penetration < minPenetration) {
|
|
2038
|
+
minPenetration = penetration;
|
|
2039
|
+
}
|
|
2040
|
+
return true;
|
|
2041
|
+
};
|
|
2042
|
+
|
|
2043
|
+
for (let i = 0; i < 3; i++) {
|
|
2044
|
+
if (!testAxis(axisInfoA.axes[i]) || !testAxis(axisInfoB.axes[i])) {
|
|
2045
|
+
return minPenetration;
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
for (let i = 0; i < 3; i++) {
|
|
2050
|
+
for (let j = 0; j < 3; j++) {
|
|
2051
|
+
const crossAxis = axisInfoA.axes[i].clone().cross(axisInfoB.axes[j]);
|
|
2052
|
+
if (!testAxis(crossAxis)) {
|
|
2053
|
+
return minPenetration;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
return minPenetration === Infinity ? 0 : minPenetration;
|
|
2059
|
+
},
|
|
2060
|
+
_isCollisionBoxMatched(boxA, boxB) {
|
|
2061
|
+
if (!this._isCollisionAabbOverlapped(boxA, boxB)) return false;
|
|
2062
|
+
|
|
2063
|
+
const obbsA = this._getCollisionObbs(boxA);
|
|
2064
|
+
const obbsB = this._getCollisionObbs(boxB);
|
|
2065
|
+
for (let i = 0; i < obbsA.length; i++) {
|
|
2066
|
+
for (let j = 0; j < obbsB.length; j++) {
|
|
2067
|
+
if (this._getObbMinPenetration(obbsA[i], obbsB[j]) > COLLISION_PENETRATION_EPSILON) {
|
|
2068
|
+
return true;
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
return false;
|
|
1767
2073
|
},
|
|
1768
2074
|
_queryOctreeByBox(box) {
|
|
1769
2075
|
const results = [];
|
|
@@ -1773,7 +2079,7 @@ export default {
|
|
|
1773
2079
|
const stack = [root];
|
|
1774
2080
|
while (stack.length) {
|
|
1775
2081
|
const node = stack.pop();
|
|
1776
|
-
if (!node || !node.box || !this.
|
|
2082
|
+
if (!node || !node.box || !this._isCollisionAabbOverlapped(node.box, box)) continue;
|
|
1777
2083
|
|
|
1778
2084
|
if (node.children) {
|
|
1779
2085
|
for (let i = 0; i < 8; i++) {
|
|
@@ -1784,7 +2090,7 @@ export default {
|
|
|
1784
2090
|
if (node.items && node.items.length) {
|
|
1785
2091
|
for (let i = 0; i < node.items.length; i++) {
|
|
1786
2092
|
const item = node.items[i];
|
|
1787
|
-
if (item && this.
|
|
2093
|
+
if (item && this._isCollisionAabbOverlapped(item.box, box)) {
|
|
1788
2094
|
results.push(item);
|
|
1789
2095
|
}
|
|
1790
2096
|
}
|
|
@@ -2303,6 +2609,10 @@ export default {
|
|
|
2303
2609
|
for (let i = 0; i < hits.length; i++) {
|
|
2304
2610
|
if (hits[i].box && hits[i].box.userData && hits[i].box.userData.visible === false)
|
|
2305
2611
|
continue;
|
|
2612
|
+
if (!this.shouldApplyOcclusionByBox(hits[i].box)) {
|
|
2613
|
+
visibleIdSet.add(this.formatModelId(hits[i].modelId));
|
|
2614
|
+
continue;
|
|
2615
|
+
}
|
|
2306
2616
|
candidates.push(hits[i]);
|
|
2307
2617
|
}
|
|
2308
2618
|
} else if (this._boxIndex && this._boxIndex.size > 0) {
|
|
@@ -2310,6 +2620,10 @@ export default {
|
|
|
2310
2620
|
if (bypassList && bypassList.size > 0 && bypassList.has(modelId)) return;
|
|
2311
2621
|
if (box.userData && box.userData.visible === false) return;
|
|
2312
2622
|
if (this.isBoxInFrustum(box)) {
|
|
2623
|
+
if (!this.shouldApplyOcclusionByBox(box)) {
|
|
2624
|
+
visibleIdSet.add(this.formatModelId(modelId));
|
|
2625
|
+
return;
|
|
2626
|
+
}
|
|
2313
2627
|
candidates.push({ modelId, box });
|
|
2314
2628
|
}
|
|
2315
2629
|
});
|
|
@@ -2985,6 +3299,16 @@ export default {
|
|
|
2985
3299
|
instanceInfo && typeof instanceInfo.instanceIndex === 'number'
|
|
2986
3300
|
? instanceInfo.instanceIndex
|
|
2987
3301
|
: null;
|
|
3302
|
+
const box =
|
|
3303
|
+
this._boxIndex && this._boxIndex.has(String(modelId))
|
|
3304
|
+
? this._boxIndex.get(String(modelId))
|
|
3305
|
+
: this._boxIndex && this._boxIndex.has(modelId)
|
|
3306
|
+
? this._boxIndex.get(modelId)
|
|
3307
|
+
: null;
|
|
3308
|
+
if (!this.shouldApplyOcclusionByBox(box)) {
|
|
3309
|
+
allInactive = false;
|
|
3310
|
+
continue;
|
|
3311
|
+
}
|
|
2988
3312
|
|
|
2989
3313
|
const inFrustum = this.isModelInFrustum(child, instanceIndex, globalFrustum);
|
|
2990
3314
|
const modelInVisible = toLoadSet.has(modelId);
|
|
@@ -2998,6 +3322,18 @@ export default {
|
|
|
2998
3322
|
|
|
2999
3323
|
// 第二遍遍历:同步状态并决定是否卸载
|
|
3000
3324
|
for (const modelId of modelIds) {
|
|
3325
|
+
const box =
|
|
3326
|
+
this._boxIndex && this._boxIndex.has(String(modelId))
|
|
3327
|
+
? this._boxIndex.get(String(modelId))
|
|
3328
|
+
: this._boxIndex && this._boxIndex.has(modelId)
|
|
3329
|
+
? this._boxIndex.get(modelId)
|
|
3330
|
+
: null;
|
|
3331
|
+
if (!this.shouldApplyOcclusionByBox(box)) {
|
|
3332
|
+
if (toLoadSet.has(modelId)) {
|
|
3333
|
+
toLoadSet.delete(modelId);
|
|
3334
|
+
}
|
|
3335
|
+
continue;
|
|
3336
|
+
}
|
|
3001
3337
|
// 无论是否卸载,只要模型已在场景中,就从待加载集合中移除
|
|
3002
3338
|
if (toLoadSet.has(modelId)) {
|
|
3003
3339
|
toLoadSet.delete(modelId);
|
|
@@ -3019,6 +3355,18 @@ export default {
|
|
|
3019
3355
|
? child.parent.userData.instanceId
|
|
3020
3356
|
: child.uuid;
|
|
3021
3357
|
if (bypassList && bypassList.size > 0 && bypassList.has(modelId)) return;
|
|
3358
|
+
const box =
|
|
3359
|
+
this._boxIndex && this._boxIndex.has(String(modelId))
|
|
3360
|
+
? this._boxIndex.get(String(modelId))
|
|
3361
|
+
: this._boxIndex && this._boxIndex.has(modelId)
|
|
3362
|
+
? this._boxIndex.get(modelId)
|
|
3363
|
+
: null;
|
|
3364
|
+
if (!this.shouldApplyOcclusionByBox(box)) {
|
|
3365
|
+
if (toLoadSet.has(modelId)) {
|
|
3366
|
+
toLoadSet.delete(modelId);
|
|
3367
|
+
}
|
|
3368
|
+
return;
|
|
3369
|
+
}
|
|
3022
3370
|
|
|
3023
3371
|
const inFrustum = this.isModelInFrustum(child, null, globalFrustum);
|
|
3024
3372
|
let modelInVisible = toLoadSet.has(modelId);
|
|
@@ -3814,6 +4162,7 @@ export default {
|
|
|
3814
4162
|
this.skipNextRenderFrame = true;
|
|
3815
4163
|
|
|
3816
4164
|
const intersects = this.getRaycasterObjects(event);
|
|
4165
|
+
console.log(intersects);
|
|
3817
4166
|
const shouldBlockCamera = intersects.some(item =>
|
|
3818
4167
|
this.isActiveTransformControlIntersection(item)
|
|
3819
4168
|
);
|
|
@@ -3821,6 +4170,9 @@ export default {
|
|
|
3821
4170
|
this.setPointerCameraGuard(true);
|
|
3822
4171
|
} else {
|
|
3823
4172
|
this.setPointerCameraGuard(false);
|
|
4173
|
+
if (event.button === 0) {
|
|
4174
|
+
this.setOrbitPointByIntersection(this.getPrimaryIntersection(intersects));
|
|
4175
|
+
}
|
|
3824
4176
|
}
|
|
3825
4177
|
this.firstTime = new Date().getTime();
|
|
3826
4178
|
let params = {
|
|
@@ -3848,9 +4200,31 @@ export default {
|
|
|
3848
4200
|
this.mouse.x = (x / rect.width) * 2 - 1;
|
|
3849
4201
|
this.mouse.y = -(y / rect.height) * 2 + 1;
|
|
3850
4202
|
this.raycaster.setFromCamera(this.mouse, this.camera);
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
4203
|
+
if (this.scene && this.scene.children) {
|
|
4204
|
+
let intersects = this.raycaster.intersectObjects(this.scene.children, true);
|
|
4205
|
+
return intersects.filter(item => item.object.type === 'Mesh');
|
|
4206
|
+
}
|
|
4207
|
+
return [];
|
|
4208
|
+
},
|
|
4209
|
+
setOrbitPointByIntersection(intersection) {
|
|
4210
|
+
if (
|
|
4211
|
+
!intersection ||
|
|
4212
|
+
!intersection.point ||
|
|
4213
|
+
!this.cameraControls ||
|
|
4214
|
+
typeof this.cameraControls.setOrbitPoint !== 'function'
|
|
4215
|
+
) {
|
|
4216
|
+
return;
|
|
4217
|
+
}
|
|
4218
|
+
const { x, y, z } = intersection.point;
|
|
4219
|
+
if (![x, y, z].every(Number.isFinite)) return;
|
|
4220
|
+
|
|
4221
|
+
// 左键旋转前先将旋转中心切到鼠标命中点,避免继续围绕默认目标点旋转。
|
|
4222
|
+
this.cameraControls.setOrbitPoint(x, y, z);
|
|
4223
|
+
this.cameraControls.update(0);
|
|
4224
|
+
},
|
|
4225
|
+
resetCameraFocalOffset(enableTransition = false) {
|
|
4226
|
+
if (!this.cameraControls || typeof this.cameraControls.setFocalOffset !== 'function') return;
|
|
4227
|
+
this.cameraControls.setFocalOffset(0, 0, 0, enableTransition);
|
|
3854
4228
|
},
|
|
3855
4229
|
isTransformControlIntersection(intersection) {
|
|
3856
4230
|
let current = intersection && intersection.object ? intersection.object : null;
|
|
@@ -4269,6 +4643,7 @@ export default {
|
|
|
4269
4643
|
}
|
|
4270
4644
|
children.instanceMatrix.needsUpdate = true;
|
|
4271
4645
|
});
|
|
4646
|
+
this.refreshCollisionObbDebug();
|
|
4272
4647
|
this.notifyCameraChange(); // 触发场景更新
|
|
4273
4648
|
break;
|
|
4274
4649
|
}
|
|
@@ -4298,6 +4673,23 @@ export default {
|
|
|
4298
4673
|
this.buildOctreeFromBoxIndex();
|
|
4299
4674
|
}
|
|
4300
4675
|
},
|
|
4676
|
+
safeTraverse(object, callback) {
|
|
4677
|
+
const stack = [object];
|
|
4678
|
+
|
|
4679
|
+
while (stack.length > 0) {
|
|
4680
|
+
const current = stack.pop();
|
|
4681
|
+
if (!current) continue;
|
|
4682
|
+
|
|
4683
|
+
callback(current);
|
|
4684
|
+
|
|
4685
|
+
const children = Array.isArray(current.children) ? current.children : [];
|
|
4686
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
4687
|
+
if (children[i]) {
|
|
4688
|
+
stack.push(children[i]);
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
},
|
|
4301
4693
|
setAllModelVisible(visible) {
|
|
4302
4694
|
const isVisible = Boolean(visible);
|
|
4303
4695
|
const matrixCache = new this.THREE.Matrix4();
|
|
@@ -4310,7 +4702,7 @@ export default {
|
|
|
4310
4702
|
}
|
|
4311
4703
|
|
|
4312
4704
|
if (this.scene) {
|
|
4313
|
-
this.scene
|
|
4705
|
+
this.safeTraverse(this.scene, child => {
|
|
4314
4706
|
if (!child || !child.isInstancedMesh) return;
|
|
4315
4707
|
const userData = child.userData || {};
|
|
4316
4708
|
if (!(userData.instancesMap instanceof Map) || userData.instancesMap.size === 0) return;
|
|
@@ -4346,7 +4738,7 @@ export default {
|
|
|
4346
4738
|
return nodeType === 'custom-root' || rootType === 'custom-root';
|
|
4347
4739
|
});
|
|
4348
4740
|
if (customRoot) {
|
|
4349
|
-
|
|
4741
|
+
this.safeTraverse(customRoot, object => {
|
|
4350
4742
|
object.visible = isVisible;
|
|
4351
4743
|
});
|
|
4352
4744
|
}
|
|
@@ -4356,6 +4748,7 @@ export default {
|
|
|
4356
4748
|
if (this._boxIndex) {
|
|
4357
4749
|
this.buildOctreeFromBoxIndex();
|
|
4358
4750
|
}
|
|
4751
|
+
this.refreshCollisionObbDebug();
|
|
4359
4752
|
},
|
|
4360
4753
|
showOutlinePass(instanceId) {
|
|
4361
4754
|
let targetObj = this.getObjectByName(instanceId);
|
|
@@ -4502,6 +4895,7 @@ export default {
|
|
|
4502
4895
|
}
|
|
4503
4896
|
children.instanceMatrix.needsUpdate = true;
|
|
4504
4897
|
});
|
|
4898
|
+
this.refreshCollisionObbDebug();
|
|
4505
4899
|
break;
|
|
4506
4900
|
}
|
|
4507
4901
|
case 'opacity':
|
|
@@ -4515,6 +4909,7 @@ export default {
|
|
|
4515
4909
|
children.geometry.attributes.opacity.needsUpdate = true;
|
|
4516
4910
|
}
|
|
4517
4911
|
});
|
|
4912
|
+
break;
|
|
4518
4913
|
}
|
|
4519
4914
|
}
|
|
4520
4915
|
}
|
|
@@ -4632,6 +5027,7 @@ export default {
|
|
|
4632
5027
|
*/
|
|
4633
5028
|
cameraLocation(params) {
|
|
4634
5029
|
const { enableTransition = true } = params;
|
|
5030
|
+
this.resetCameraFocalOffset(enableTransition);
|
|
4635
5031
|
this.cameraControls.setLookAt(
|
|
4636
5032
|
params.x,
|
|
4637
5033
|
params.y,
|
|
@@ -4668,15 +5064,15 @@ export default {
|
|
|
4668
5064
|
let cameraCenter = viewAll
|
|
4669
5065
|
? new this.THREE.Vector3(p.x, p.y, p.z)
|
|
4670
5066
|
: new this.THREE.Vector3(p.x, p.y, p.z).addScalar(Math.max(size.x, size.y, size.z));
|
|
4671
|
-
this.
|
|
4672
|
-
cameraCenter.x,
|
|
4673
|
-
cameraCenter.y,
|
|
4674
|
-
cameraCenter.z,
|
|
4675
|
-
center.x,
|
|
4676
|
-
center.y,
|
|
4677
|
-
center.z,
|
|
4678
|
-
true
|
|
4679
|
-
);
|
|
5067
|
+
this.cameraLocation({
|
|
5068
|
+
x: cameraCenter.x,
|
|
5069
|
+
y: cameraCenter.y,
|
|
5070
|
+
z: cameraCenter.z,
|
|
5071
|
+
heading: center.x,
|
|
5072
|
+
pitch: center.y,
|
|
5073
|
+
roll: center.z,
|
|
5074
|
+
enableTransition: true,
|
|
5075
|
+
});
|
|
4680
5076
|
},
|
|
4681
5077
|
// 添加广告牌
|
|
4682
5078
|
/*
|
|
@@ -4920,6 +5316,7 @@ export default {
|
|
|
4920
5316
|
this.modelStateManager.debounceTimer = null;
|
|
4921
5317
|
}
|
|
4922
5318
|
this.clearBypassCullingModelIds();
|
|
5319
|
+
this.hideCollisionObbDebug();
|
|
4923
5320
|
|
|
4924
5321
|
if (this.scene) {
|
|
4925
5322
|
this.removeTraverse();
|
|
@@ -5184,6 +5581,7 @@ export default {
|
|
|
5184
5581
|
const nodeType = userData.nodeType || userData.rootType || '';
|
|
5185
5582
|
if (nodeType === 'custom-root') return true;
|
|
5186
5583
|
if (userData.transformControlHelper === true) return true;
|
|
5584
|
+
if (userData.collisionObbDebugHelper === true) return true;
|
|
5187
5585
|
if (object.isCamera || object.isLight) return true;
|
|
5188
5586
|
if (typeof object.type === 'string' && /Helper$/i.test(object.type)) return true;
|
|
5189
5587
|
return object.type === 'CSS2DObject' || object.type === 'TransformControlsRoot';
|