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.
Files changed (42) hide show
  1. package/dist/fl-web-component.common.1.js.map +1 -1
  2. package/dist/fl-web-component.common.2.js.map +1 -1
  3. package/dist/fl-web-component.common.js +5871 -1872
  4. package/dist/fl-web-component.common.js.map +1 -1
  5. package/dist/fl-web-component.css +1 -1
  6. package/package.json +4 -3
  7. package/packages/components/com-flcanvas/components/entityFormatting.js +134 -1
  8. package/packages/components/com-flcanvas/index.vue +335 -239
  9. package/packages/components/com-graphics/index.vue +433 -35
  10. package/packages/components/com-graphics/mock.json +115 -0
  11. package/packages/utils/StreamLoader.js +250 -107
  12. package/packages/utils/StreamLoaderParser.worker.js +180 -72
  13. package/src/utils/dxf-parser/AutoCadColorIndex.js +265 -0
  14. package/src/utils/dxf-parser/DimStyleCodes.js +33 -0
  15. package/src/utils/dxf-parser/DxfArrayScanner.js +151 -0
  16. package/src/utils/dxf-parser/DxfParser.js +918 -0
  17. package/src/utils/dxf-parser/ExtendedDataParser.js +117 -0
  18. package/src/utils/dxf-parser/LICENSE +21 -0
  19. package/src/utils/dxf-parser/ParseHelpers.js +140 -0
  20. package/src/utils/dxf-parser/README.md +8 -0
  21. package/src/utils/dxf-parser/entities/3dface.js +83 -0
  22. package/src/utils/dxf-parser/entities/arc.js +38 -0
  23. package/src/utils/dxf-parser/entities/attdef.js +89 -0
  24. package/src/utils/dxf-parser/entities/attribute.js +109 -0
  25. package/src/utils/dxf-parser/entities/circle.js +43 -0
  26. package/src/utils/dxf-parser/entities/dimension.js +71 -0
  27. package/src/utils/dxf-parser/entities/ellipse.js +48 -0
  28. package/src/utils/dxf-parser/entities/hatch.js +348 -0
  29. package/src/utils/dxf-parser/entities/insert.js +57 -0
  30. package/src/utils/dxf-parser/entities/line.js +34 -0
  31. package/src/utils/dxf-parser/entities/lwpolyline.js +103 -0
  32. package/src/utils/dxf-parser/entities/mtext.js +57 -0
  33. package/src/utils/dxf-parser/entities/point.js +35 -0
  34. package/src/utils/dxf-parser/entities/polyline.js +92 -0
  35. package/src/utils/dxf-parser/entities/solid.js +40 -0
  36. package/src/utils/dxf-parser/entities/spline.js +70 -0
  37. package/src/utils/dxf-parser/entities/text.js +50 -0
  38. package/src/utils/dxf-parser/entities/vertex.js +62 -0
  39. package/src/utils/flgltf-parser.js +21 -9
  40. package/src/utils/instance-parser.js +59 -59
  41. package/src/utils/threejs/measure-clear-distance.js +149 -123
  42. 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 = 0.1;
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
- // 构造 AABB
1550
- const boxThree = new this.THREE.Box3(min, max);
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
- const hasMatrix = Array.isArray(it.matrix);
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
- boxThree.applyMatrix4(this.bizToThreeMatrix);
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
- _getBoxMinPenetration(boxA, boxB) {
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
- if (!boxA.intersectsBox(boxB)) return false;
1766
- return this._getBoxMinPenetration(boxA, boxB) > COLLISION_PENETRATION_EPSILON;
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._isCollisionBoxMatched(node.box, box)) continue;
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._isCollisionBoxMatched(item.box, box)) {
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
- return this.scene && this.scene.children
3852
- ? this.raycaster.intersectObjects(this.scene.children, true)
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.traverse(child => {
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
- customRoot.traverse(object => {
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.cameraControls.setLookAt(
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';