fl-web-component 2.1.1-beta.10 → 2.1.1-beta.12
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 +825 -588
- 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 +0 -2
- package/packages/components/com-flcanvas/index.vue +29 -26
- package/packages/components/com-graphics/index.vue +389 -89
- package/packages/utils/StreamLoader.js +1 -1
- package/src/utils/instance-parser.js +16 -16
|
@@ -165,9 +165,15 @@ import { onContextHandle } from './component/context';
|
|
|
165
165
|
|
|
166
166
|
const isDebug = process.env.NODE_ENV !== 'production' || process.env.VUE_APP_IS_WATCH === true;
|
|
167
167
|
// const isDebug = false;
|
|
168
|
-
const
|
|
168
|
+
const COLLISION_CONTACT_EPSILON = 1;
|
|
169
169
|
const COLLISION_OBB_AXIS_EPSILON = 1e-8;
|
|
170
170
|
const COLLISION_DEBUG_GROUP_NAME = '__collision_obb_debug_group__';
|
|
171
|
+
const OCCLUSION_DEBUG_GROUP_NAME = '__occlusion_bounds_debug_group__';
|
|
172
|
+
const OCCLUSION_DEBUG_FLAG_COLORS = {
|
|
173
|
+
1: 0xffc107,
|
|
174
|
+
2: 0x409eff,
|
|
175
|
+
3: 0xff4dff,
|
|
176
|
+
};
|
|
171
177
|
|
|
172
178
|
export default {
|
|
173
179
|
name: 'FlModel',
|
|
@@ -198,8 +204,8 @@ export default {
|
|
|
198
204
|
occlusionState: {
|
|
199
205
|
// enabled: false,
|
|
200
206
|
enabled: true,
|
|
201
|
-
|
|
202
|
-
previewEnabled: isDebug,
|
|
207
|
+
previewEnabled: false,
|
|
208
|
+
// previewEnabled: isDebug,
|
|
203
209
|
bufferWidth: 1700,
|
|
204
210
|
sampleStride: 1,
|
|
205
211
|
minSampleCount: 5,
|
|
@@ -216,8 +222,10 @@ export default {
|
|
|
216
222
|
},
|
|
217
223
|
isolateMode: false,
|
|
218
224
|
sceneBoundingBoxDebugEnabled: false,
|
|
219
|
-
|
|
220
|
-
collisionObbDebugEnabled: false,
|
|
225
|
+
collisionObbDebugEnabled: isDebug,
|
|
226
|
+
// collisionObbDebugEnabled: false,
|
|
227
|
+
// occlusionBoundsDebugEnabled: isDebug,
|
|
228
|
+
occlusionBoundsDebugEnabled: false,
|
|
221
229
|
};
|
|
222
230
|
},
|
|
223
231
|
watch: {
|
|
@@ -267,6 +275,7 @@ export default {
|
|
|
267
275
|
'sceneBoundingBox',
|
|
268
276
|
'sceneBoundingBoxHelper',
|
|
269
277
|
'collisionObbDebugGroup',
|
|
278
|
+
'occlusionBoundsDebugGroup',
|
|
270
279
|
];
|
|
271
280
|
arr.forEach(item => {
|
|
272
281
|
this[item] = null;
|
|
@@ -288,6 +297,7 @@ export default {
|
|
|
288
297
|
'isDragging',
|
|
289
298
|
'boundingBoxVisible',
|
|
290
299
|
'collisionObbDebugEnabled',
|
|
300
|
+
'occlusionBoundsDebugEnabled',
|
|
291
301
|
].forEach(item => {
|
|
292
302
|
this[item] = false;
|
|
293
303
|
});
|
|
@@ -1218,6 +1228,28 @@ export default {
|
|
|
1218
1228
|
if (instancedMesh && instancedMesh.userData) return instancedMesh.userData.copyMatrix || null;
|
|
1219
1229
|
return null;
|
|
1220
1230
|
},
|
|
1231
|
+
setInstanceCullingVisible(instancedMesh, instanceId, visible) {
|
|
1232
|
+
const instanceState = this.getInstanceState(instancedMesh, instanceId);
|
|
1233
|
+
if (!instanceState || typeof instanceState.instanceIndex !== 'number') return;
|
|
1234
|
+
|
|
1235
|
+
const copyMatrix =
|
|
1236
|
+
instanceState.copyMatrix || this.getInstanceCopyMatrix(instancedMesh, instanceId);
|
|
1237
|
+
if (!copyMatrix) return;
|
|
1238
|
+
|
|
1239
|
+
const shouldShow =
|
|
1240
|
+
visible &&
|
|
1241
|
+
instanceState.visible !== false &&
|
|
1242
|
+
this.isSourceVisible(instanceId, instancedMesh);
|
|
1243
|
+
const matrix = new this.THREE.Matrix4().copy(copyMatrix);
|
|
1244
|
+
if (!shouldShow) {
|
|
1245
|
+
matrix.makeTranslation(9999999, 9999999, 9999999);
|
|
1246
|
+
this.removeOutlineObject(instancedMesh, instanceState.instanceIndex);
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
instancedMesh.setMatrixAt(instanceState.instanceIndex, matrix);
|
|
1250
|
+
instancedMesh.instanceMatrix.needsUpdate = true;
|
|
1251
|
+
instanceState.cullingVisible = !!visible;
|
|
1252
|
+
},
|
|
1221
1253
|
isSourceHiddenInstance(instancedMesh, instanceId) {
|
|
1222
1254
|
const instanceState = this.getInstanceState(instancedMesh, instanceId);
|
|
1223
1255
|
if (instanceState && instanceState.sourceVisible !== undefined) {
|
|
@@ -1536,21 +1568,37 @@ export default {
|
|
|
1536
1568
|
existingBox.union(box);
|
|
1537
1569
|
return existingBox;
|
|
1538
1570
|
},
|
|
1539
|
-
|
|
1540
|
-
if (!
|
|
1571
|
+
createOcclusionProxyBox(box) {
|
|
1572
|
+
if (!box || !box.isBox3) return null;
|
|
1573
|
+
const proxyBox = box.clone();
|
|
1574
|
+
proxyBox.userData = { ...(box.userData || {}) };
|
|
1575
|
+
delete proxyBox.userData.occlusionProxyBoxes;
|
|
1576
|
+
return proxyBox;
|
|
1577
|
+
},
|
|
1578
|
+
mergeModelBoxUserData(targetBox, sourceBox) {
|
|
1579
|
+
if (!targetBox || !sourceBox || !sourceBox.userData) return;
|
|
1580
|
+
const sourceUserData = sourceBox.userData;
|
|
1541
1581
|
if (!targetBox.userData) {
|
|
1542
1582
|
targetBox.userData = sourceUserData;
|
|
1543
1583
|
return;
|
|
1544
1584
|
}
|
|
1545
1585
|
|
|
1546
1586
|
const targetUserData = targetBox.userData;
|
|
1587
|
+
const targetProxyBoxes = Array.isArray(targetUserData.occlusionProxyBoxes)
|
|
1588
|
+
? targetUserData.occlusionProxyBoxes
|
|
1589
|
+
: [this.createOcclusionProxyBox(targetBox)].filter(Boolean);
|
|
1590
|
+
const sourceProxyBoxes = Array.isArray(sourceUserData.occlusionProxyBoxes)
|
|
1591
|
+
? sourceUserData.occlusionProxyBoxes
|
|
1592
|
+
: [this.createOcclusionProxyBox(sourceBox)].filter(Boolean);
|
|
1547
1593
|
targetUserData.visible = targetUserData.visible !== false || sourceUserData.visible !== false;
|
|
1548
1594
|
targetUserData.sourceVisible =
|
|
1549
1595
|
targetUserData.sourceVisible !== false || sourceUserData.sourceVisible !== false;
|
|
1550
1596
|
targetUserData.transparent = !!targetUserData.transparent && !!sourceUserData.transparent;
|
|
1551
1597
|
targetUserData.mergedBoxCount = (targetUserData.mergedBoxCount || 1) + 1;
|
|
1552
|
-
//
|
|
1553
|
-
targetUserData.occlusionEnabled =
|
|
1598
|
+
// 多图元模型分别参与遮挡测试,任一图元可见时都会保留整个模型。
|
|
1599
|
+
targetUserData.occlusionEnabled =
|
|
1600
|
+
!!targetUserData.occlusionEnabled && !!sourceUserData.occlusionEnabled;
|
|
1601
|
+
targetUserData.occlusionProxyBoxes = targetProxyBoxes.concat(sourceProxyBoxes);
|
|
1554
1602
|
|
|
1555
1603
|
const targetObbs = Array.isArray(targetUserData.collisionObbs)
|
|
1556
1604
|
? targetUserData.collisionObbs
|
|
@@ -1562,6 +1610,60 @@ export default {
|
|
|
1562
1610
|
targetUserData.collisionObbs = targetObbs.concat(sourceObbs);
|
|
1563
1611
|
}
|
|
1564
1612
|
},
|
|
1613
|
+
_createCollisionObb(center, halfSize, rotation, matrix) {
|
|
1614
|
+
const worldCenter = center.clone();
|
|
1615
|
+
const worldHalfSize = halfSize.clone();
|
|
1616
|
+
const worldRotation = new this.THREE.Matrix3();
|
|
1617
|
+
|
|
1618
|
+
if (!matrix) {
|
|
1619
|
+
return new OBB(worldCenter, worldHalfSize, rotation.clone());
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
worldCenter.applyMatrix4(matrix);
|
|
1623
|
+
|
|
1624
|
+
const localAxes = [
|
|
1625
|
+
new this.THREE.Vector3(),
|
|
1626
|
+
new this.THREE.Vector3(),
|
|
1627
|
+
new this.THREE.Vector3(),
|
|
1628
|
+
];
|
|
1629
|
+
rotation.extractBasis(localAxes[0], localAxes[1], localAxes[2]);
|
|
1630
|
+
|
|
1631
|
+
const linearMatrix = new this.THREE.Matrix3().setFromMatrix4(matrix);
|
|
1632
|
+
const worldAxes = localAxes.map(axis => axis.clone().applyMatrix3(linearMatrix));
|
|
1633
|
+
const halfSizes = [halfSize.x, halfSize.y, halfSize.z];
|
|
1634
|
+
|
|
1635
|
+
for (let i = 0; i < 3; i++) {
|
|
1636
|
+
const axisLength = worldAxes[i].length();
|
|
1637
|
+
if (axisLength <= COLLISION_OBB_AXIS_EPSILON) {
|
|
1638
|
+
worldAxes[i].copy(localAxes[i]);
|
|
1639
|
+
continue;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
worldAxes[i].multiplyScalar(1 / axisLength);
|
|
1643
|
+
halfSizes[i] *= axisLength;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
worldHalfSize.set(halfSizes[0], halfSizes[1], halfSizes[2]);
|
|
1647
|
+
worldRotation.set(
|
|
1648
|
+
worldAxes[0].x,
|
|
1649
|
+
worldAxes[1].x,
|
|
1650
|
+
worldAxes[2].x,
|
|
1651
|
+
worldAxes[0].y,
|
|
1652
|
+
worldAxes[1].y,
|
|
1653
|
+
worldAxes[2].y,
|
|
1654
|
+
worldAxes[0].z,
|
|
1655
|
+
worldAxes[1].z,
|
|
1656
|
+
worldAxes[2].z
|
|
1657
|
+
);
|
|
1658
|
+
|
|
1659
|
+
return new OBB(worldCenter, worldHalfSize, worldRotation);
|
|
1660
|
+
},
|
|
1661
|
+
_createCollisionObbFromBox(box, matrix) {
|
|
1662
|
+
const center = box.getCenter(new this.THREE.Vector3());
|
|
1663
|
+
const halfSize = box.getSize(new this.THREE.Vector3()).multiplyScalar(0.5);
|
|
1664
|
+
const rotation = new this.THREE.Matrix3().identity();
|
|
1665
|
+
return this._createCollisionObb(center, halfSize, rotation, matrix);
|
|
1666
|
+
},
|
|
1565
1667
|
upsertModelBox(modelId, box) {
|
|
1566
1668
|
if (!this._boxIndex || !modelId || !box || !box.isBox3) return;
|
|
1567
1669
|
const key = String(modelId);
|
|
@@ -1570,8 +1672,8 @@ export default {
|
|
|
1570
1672
|
this._boxIndex.set(key, box);
|
|
1571
1673
|
return;
|
|
1572
1674
|
}
|
|
1675
|
+
this.mergeModelBoxUserData(existingBox, box);
|
|
1573
1676
|
existingBox.union(box);
|
|
1574
|
-
this.mergeModelBoxUserData(existingBox, box.userData);
|
|
1575
1677
|
},
|
|
1576
1678
|
setBoxIndex(boxJson, documentId, isAdd = true) {
|
|
1577
1679
|
if (!this._boxIndex) this._boxIndex = new Map();
|
|
@@ -1703,10 +1805,12 @@ export default {
|
|
|
1703
1805
|
halfSize: halfSize,
|
|
1704
1806
|
};
|
|
1705
1807
|
|
|
1706
|
-
const collisionObb =
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1808
|
+
const collisionObb = this._createCollisionObb(
|
|
1809
|
+
center,
|
|
1810
|
+
halfSize,
|
|
1811
|
+
rotation,
|
|
1812
|
+
collisionMatrix
|
|
1813
|
+
);
|
|
1710
1814
|
userData.collisionObbs = [collisionObb];
|
|
1711
1815
|
}
|
|
1712
1816
|
// 如果是 flag=2,解析并存储多个子包围盒 (min, max)
|
|
@@ -1720,10 +1824,7 @@ export default {
|
|
|
1720
1824
|
subBoxes.push({ min, max });
|
|
1721
1825
|
|
|
1722
1826
|
const subBox = new this.THREE.Box3(min.clone(), max.clone());
|
|
1723
|
-
const collisionObb =
|
|
1724
|
-
if (collisionMatrix) {
|
|
1725
|
-
collisionObb.applyMatrix4(collisionMatrix);
|
|
1726
|
-
}
|
|
1827
|
+
const collisionObb = this._createCollisionObbFromBox(subBox, collisionMatrix);
|
|
1727
1828
|
collisionObbs.push(collisionObb);
|
|
1728
1829
|
}
|
|
1729
1830
|
}
|
|
@@ -1734,10 +1835,7 @@ export default {
|
|
|
1734
1835
|
}
|
|
1735
1836
|
|
|
1736
1837
|
if (!userData.collisionObbs || userData.collisionObbs.length === 0) {
|
|
1737
|
-
const collisionObb =
|
|
1738
|
-
if (collisionMatrix) {
|
|
1739
|
-
collisionObb.applyMatrix4(collisionMatrix);
|
|
1740
|
-
}
|
|
1838
|
+
const collisionObb = this._createCollisionObbFromBox(sourceBox, collisionMatrix);
|
|
1741
1839
|
userData.collisionObbs = [collisionObb];
|
|
1742
1840
|
}
|
|
1743
1841
|
|
|
@@ -1773,11 +1871,26 @@ export default {
|
|
|
1773
1871
|
}
|
|
1774
1872
|
this.buildOctreeFromBoxIndex();
|
|
1775
1873
|
this.refreshCollisionObbDebug();
|
|
1874
|
+
this.refreshOcclusionBoundsDebug();
|
|
1776
1875
|
console.log('time end', Date.now());
|
|
1777
1876
|
},
|
|
1778
1877
|
shouldApplyOcclusionByBox(box) {
|
|
1779
1878
|
return !!(box && box.userData && box.userData.occlusionEnabled);
|
|
1780
1879
|
},
|
|
1880
|
+
appendOcclusionCandidates(candidates, modelId, box, frustum) {
|
|
1881
|
+
if (!box) return;
|
|
1882
|
+
const userData = box.userData || {};
|
|
1883
|
+
const proxyBoxes = Array.isArray(userData.occlusionProxyBoxes)
|
|
1884
|
+
? userData.occlusionProxyBoxes
|
|
1885
|
+
: [box];
|
|
1886
|
+
|
|
1887
|
+
for (let i = 0; i < proxyBoxes.length; i++) {
|
|
1888
|
+
const proxyBox = proxyBoxes[i];
|
|
1889
|
+
if (!proxyBox || (proxyBox.userData && proxyBox.userData.visible === false)) continue;
|
|
1890
|
+
if (frustum && !frustum.intersectsBox(proxyBox)) continue;
|
|
1891
|
+
candidates.push({ modelId, box: proxyBox });
|
|
1892
|
+
}
|
|
1893
|
+
},
|
|
1781
1894
|
tryInitialCenterAfterBoundsReady() {
|
|
1782
1895
|
if (this.hasExecutedCentering || this.userInteracting) return;
|
|
1783
1896
|
if (
|
|
@@ -2089,20 +2202,15 @@ export default {
|
|
|
2089
2202
|
|
|
2090
2203
|
this._clearCollisionObbDebugGroup(false);
|
|
2091
2204
|
const pairMap = this._buildCollisionPairMap();
|
|
2092
|
-
if (
|
|
2205
|
+
if (!this._boxIndex || this._boxIndex.size === 0) return;
|
|
2093
2206
|
|
|
2094
|
-
|
|
2095
|
-
const box =
|
|
2096
|
-
this._boxIndex && this._boxIndex.has(String(modelId))
|
|
2097
|
-
? this._boxIndex.get(String(modelId))
|
|
2098
|
-
: this._boxIndex && this._boxIndex.has(modelId)
|
|
2099
|
-
? this._boxIndex.get(modelId)
|
|
2100
|
-
: null;
|
|
2207
|
+
this._boxIndex.forEach((box, modelId) => {
|
|
2101
2208
|
if (!this._isCollisionBoxAvailable(box)) return;
|
|
2102
2209
|
|
|
2103
2210
|
const obbs = this._getCollisionObbs(box);
|
|
2104
2211
|
if (!obbs.length) return;
|
|
2105
2212
|
|
|
2213
|
+
const relatedIds = pairMap.get(String(modelId));
|
|
2106
2214
|
const isColliding = relatedIds && relatedIds.size > 0;
|
|
2107
2215
|
const color = isColliding ? 0xff3b30 : 0x36cfc9;
|
|
2108
2216
|
for (let i = 0; i < obbs.length; i++) {
|
|
@@ -2128,6 +2236,165 @@ export default {
|
|
|
2128
2236
|
this.hideCollisionObbDebug();
|
|
2129
2237
|
}
|
|
2130
2238
|
},
|
|
2239
|
+
_getOcclusionDebugGroup() {
|
|
2240
|
+
if (!this.scene) return null;
|
|
2241
|
+
if (this.occlusionBoundsDebugGroup && this.occlusionBoundsDebugGroup.parent === this.scene) {
|
|
2242
|
+
return this.occlusionBoundsDebugGroup;
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
const group = new this.THREE.Group();
|
|
2246
|
+
group.name = OCCLUSION_DEBUG_GROUP_NAME;
|
|
2247
|
+
group.userData.transformControlHelper = true;
|
|
2248
|
+
group.userData.occlusionBoundsDebugHelper = true;
|
|
2249
|
+
this.scene.add(group);
|
|
2250
|
+
this.occlusionBoundsDebugGroup = group;
|
|
2251
|
+
return group;
|
|
2252
|
+
},
|
|
2253
|
+
_createOcclusionFlag1LineSegments(box, color) {
|
|
2254
|
+
const userData = box && box.userData ? box.userData : {};
|
|
2255
|
+
const vertices = userData.obbData;
|
|
2256
|
+
if (!vertices || vertices.length < 9) return null;
|
|
2257
|
+
|
|
2258
|
+
const sourceGeometry = new this.THREE.BufferGeometry();
|
|
2259
|
+
sourceGeometry.setAttribute(
|
|
2260
|
+
'position',
|
|
2261
|
+
new this.THREE.BufferAttribute(new Float32Array(vertices), 3)
|
|
2262
|
+
);
|
|
2263
|
+
|
|
2264
|
+
const indices = userData.indices;
|
|
2265
|
+
if (indices && indices.length >= 3) {
|
|
2266
|
+
if (indices.isBufferAttribute) {
|
|
2267
|
+
sourceGeometry.setIndex(indices);
|
|
2268
|
+
} else if (ArrayBuffer.isView(indices)) {
|
|
2269
|
+
sourceGeometry.setIndex(new this.THREE.BufferAttribute(indices, 1));
|
|
2270
|
+
} else {
|
|
2271
|
+
sourceGeometry.setIndex(indices);
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
let wireframeGeometry = null;
|
|
2276
|
+
try {
|
|
2277
|
+
// flag=1 对应真实遮挡网格,必须按顶点和三角形索引绘制,不能退化成 AABB/OBB。
|
|
2278
|
+
wireframeGeometry = new this.THREE.WireframeGeometry(sourceGeometry);
|
|
2279
|
+
} catch (error) {
|
|
2280
|
+
console.warn('flag=1 遮挡调试线框生成失败', error);
|
|
2281
|
+
}
|
|
2282
|
+
sourceGeometry.dispose();
|
|
2283
|
+
if (!wireframeGeometry || wireframeGeometry.getAttribute('position').count === 0) {
|
|
2284
|
+
if (wireframeGeometry) wireframeGeometry.dispose();
|
|
2285
|
+
return null;
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
const material = new this.THREE.LineBasicMaterial({
|
|
2289
|
+
color,
|
|
2290
|
+
transparent: true,
|
|
2291
|
+
opacity: 0.95,
|
|
2292
|
+
depthTest: false,
|
|
2293
|
+
depthWrite: false,
|
|
2294
|
+
toneMapped: false,
|
|
2295
|
+
});
|
|
2296
|
+
const lineSegments = new this.THREE.LineSegments(wireframeGeometry, material);
|
|
2297
|
+
const transformMatrix = new this.THREE.Matrix4();
|
|
2298
|
+
const matrixArr = userData.matrix;
|
|
2299
|
+
if (matrixArr && matrixArr.length >= 16) {
|
|
2300
|
+
transformMatrix.fromArray(matrixArr);
|
|
2301
|
+
}
|
|
2302
|
+
if (this.bizToThreeMatrix) {
|
|
2303
|
+
transformMatrix.premultiply(this.bizToThreeMatrix);
|
|
2304
|
+
}
|
|
2305
|
+
lineSegments.matrixAutoUpdate = false;
|
|
2306
|
+
lineSegments.matrix.copy(transformMatrix);
|
|
2307
|
+
lineSegments.matrixWorldNeedsUpdate = true;
|
|
2308
|
+
lineSegments.renderOrder = 998;
|
|
2309
|
+
lineSegments.userData.transformControlHelper = true;
|
|
2310
|
+
lineSegments.userData.occlusionBoundsDebugHelper = true;
|
|
2311
|
+
lineSegments.userData.occlusionFlag = 1;
|
|
2312
|
+
return lineSegments;
|
|
2313
|
+
},
|
|
2314
|
+
_createOcclusionBoxLineSegments(obb, color, flag) {
|
|
2315
|
+
const lineSegments = this._createCollisionObbLineSegments(obb, color);
|
|
2316
|
+
if (!lineSegments) return null;
|
|
2317
|
+
|
|
2318
|
+
lineSegments.renderOrder = 998;
|
|
2319
|
+
lineSegments.userData.collisionObbDebugHelper = false;
|
|
2320
|
+
lineSegments.userData.occlusionBoundsDebugHelper = true;
|
|
2321
|
+
lineSegments.userData.occlusionFlag = flag;
|
|
2322
|
+
return lineSegments;
|
|
2323
|
+
},
|
|
2324
|
+
_clearOcclusionBoundsDebugGroup(removeGroup = false) {
|
|
2325
|
+
const group = this.occlusionBoundsDebugGroup;
|
|
2326
|
+
if (!group) return;
|
|
2327
|
+
|
|
2328
|
+
while (group.children.length > 0) {
|
|
2329
|
+
const child = group.children[group.children.length - 1];
|
|
2330
|
+
group.remove(child);
|
|
2331
|
+
if (child.geometry) child.geometry.dispose();
|
|
2332
|
+
if (Array.isArray(child.material)) {
|
|
2333
|
+
child.material.forEach(material => material && material.dispose());
|
|
2334
|
+
} else if (child.material) {
|
|
2335
|
+
child.material.dispose();
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
if (removeGroup && this.scene && group.parent === this.scene) {
|
|
2340
|
+
this.scene.remove(group);
|
|
2341
|
+
this.occlusionBoundsDebugGroup = null;
|
|
2342
|
+
} else if (!this.scene) {
|
|
2343
|
+
this.occlusionBoundsDebugGroup = null;
|
|
2344
|
+
}
|
|
2345
|
+
},
|
|
2346
|
+
hideOcclusionBoundsDebug() {
|
|
2347
|
+
this._clearOcclusionBoundsDebugGroup();
|
|
2348
|
+
this.occlusionBoundsDebugEnabled = false;
|
|
2349
|
+
},
|
|
2350
|
+
refreshOcclusionBoundsDebug() {
|
|
2351
|
+
if (!this.occlusionBoundsDebugEnabled) return;
|
|
2352
|
+
const group = this._getOcclusionDebugGroup();
|
|
2353
|
+
if (!group) return;
|
|
2354
|
+
|
|
2355
|
+
this._clearOcclusionBoundsDebugGroup(false);
|
|
2356
|
+
if (!this._boxIndex || this._boxIndex.size === 0) return;
|
|
2357
|
+
|
|
2358
|
+
this._boxIndex.forEach((box, modelId) => {
|
|
2359
|
+
if (!this._isCollisionBoxAvailable(box) || !this.shouldApplyOcclusionByBox(box)) return;
|
|
2360
|
+
|
|
2361
|
+
const userData = box.userData || {};
|
|
2362
|
+
const flag = Number(userData.flag) || 1;
|
|
2363
|
+
const color = OCCLUSION_DEBUG_FLAG_COLORS[flag];
|
|
2364
|
+
if (!color) return;
|
|
2365
|
+
|
|
2366
|
+
if (flag === 1) {
|
|
2367
|
+
const lineSegments = this._createOcclusionFlag1LineSegments(box, color);
|
|
2368
|
+
if (!lineSegments) return;
|
|
2369
|
+
lineSegments.name = `${OCCLUSION_DEBUG_GROUP_NAME}:${modelId}:flag-1`;
|
|
2370
|
+
lineSegments.userData.modelId = modelId;
|
|
2371
|
+
group.add(lineSegments);
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
const obbs = this._getCollisionObbs(box);
|
|
2376
|
+
for (let i = 0; i < obbs.length; i++) {
|
|
2377
|
+
const lineSegments = this._createOcclusionBoxLineSegments(obbs[i], color, flag);
|
|
2378
|
+
if (!lineSegments) continue;
|
|
2379
|
+
lineSegments.name = `${OCCLUSION_DEBUG_GROUP_NAME}:${modelId}:flag-${flag}:${i}`;
|
|
2380
|
+
lineSegments.userData.modelId = modelId;
|
|
2381
|
+
group.add(lineSegments);
|
|
2382
|
+
}
|
|
2383
|
+
});
|
|
2384
|
+
},
|
|
2385
|
+
showOcclusionBoundsDebug() {
|
|
2386
|
+
this.occlusionBoundsDebugEnabled = true;
|
|
2387
|
+
this.refreshOcclusionBoundsDebug();
|
|
2388
|
+
},
|
|
2389
|
+
toggleOcclusionBoundsDebug(forceVisible) {
|
|
2390
|
+
const nextVisible =
|
|
2391
|
+
typeof forceVisible === 'boolean' ? forceVisible : !this.occlusionBoundsDebugEnabled;
|
|
2392
|
+
if (nextVisible) {
|
|
2393
|
+
this.showOcclusionBoundsDebug();
|
|
2394
|
+
} else {
|
|
2395
|
+
this.hideOcclusionBoundsDebug();
|
|
2396
|
+
}
|
|
2397
|
+
},
|
|
2131
2398
|
_getObbAxisInfo(obb) {
|
|
2132
2399
|
const xAxis = new this.THREE.Vector3();
|
|
2133
2400
|
const yAxis = new this.THREE.Vector3();
|
|
@@ -2197,7 +2464,7 @@ export default {
|
|
|
2197
2464
|
const obbsB = this._getCollisionObbs(boxB);
|
|
2198
2465
|
for (let i = 0; i < obbsA.length; i++) {
|
|
2199
2466
|
for (let j = 0; j < obbsB.length; j++) {
|
|
2200
|
-
if (this._getObbMinPenetration(obbsA[i], obbsB[j])
|
|
2467
|
+
if (this._getObbMinPenetration(obbsA[i], obbsB[j]) >= COLLISION_CONTACT_EPSILON) {
|
|
2201
2468
|
return true;
|
|
2202
2469
|
}
|
|
2203
2470
|
}
|
|
@@ -2809,7 +3076,7 @@ export default {
|
|
|
2809
3076
|
visibleIdSet.add(this.formatModelId(hits[i].modelId));
|
|
2810
3077
|
continue;
|
|
2811
3078
|
}
|
|
2812
|
-
|
|
3079
|
+
this.appendOcclusionCandidates(candidates, hits[i].modelId, hits[i].box, globalFrustum);
|
|
2813
3080
|
}
|
|
2814
3081
|
} else if (this._boxIndex && this._boxIndex.size > 0) {
|
|
2815
3082
|
this._boxIndex.forEach((box, modelId) => {
|
|
@@ -2820,7 +3087,7 @@ export default {
|
|
|
2820
3087
|
visibleIdSet.add(this.formatModelId(modelId));
|
|
2821
3088
|
return;
|
|
2822
3089
|
}
|
|
2823
|
-
|
|
3090
|
+
this.appendOcclusionCandidates(candidates, modelId, box, globalFrustum);
|
|
2824
3091
|
}
|
|
2825
3092
|
});
|
|
2826
3093
|
}
|
|
@@ -2936,6 +3203,7 @@ export default {
|
|
|
2936
3203
|
const tBuild0 = performance.now();
|
|
2937
3204
|
|
|
2938
3205
|
const flag1Items = [];
|
|
3206
|
+
const flag2Items = [];
|
|
2939
3207
|
const flag3Items = [];
|
|
2940
3208
|
|
|
2941
3209
|
let globalIdx = 1;
|
|
@@ -2949,6 +3217,8 @@ export default {
|
|
|
2949
3217
|
const flag = (c.box.userData && c.box.userData.flag) || 1;
|
|
2950
3218
|
if (flag === 3 && c.box.userData && c.box.userData.obb) {
|
|
2951
3219
|
flag3Items.push({ c, idx });
|
|
3220
|
+
} else if (flag === 2) {
|
|
3221
|
+
flag2Items.push({ c, idx });
|
|
2952
3222
|
} else if (flag === 1) {
|
|
2953
3223
|
flag1Items.push({ c, idx });
|
|
2954
3224
|
}
|
|
@@ -2965,7 +3235,8 @@ export default {
|
|
|
2965
3235
|
const _tempMatA = occObjs._tempMatA || (occObjs._tempMatA = new this.THREE.Matrix4());
|
|
2966
3236
|
|
|
2967
3237
|
let boxes = occObjs.boxes;
|
|
2968
|
-
const
|
|
3238
|
+
const boxItems = flag3Items.concat(flag2Items);
|
|
3239
|
+
const boxCount = boxItems.length;
|
|
2969
3240
|
if (boxCount > 0) {
|
|
2970
3241
|
const needCapacity = boxCount;
|
|
2971
3242
|
const capacity = boxes && typeof boxes.capacity === 'number' ? boxes.capacity : 0;
|
|
@@ -2995,19 +3266,26 @@ export default {
|
|
|
2995
3266
|
}
|
|
2996
3267
|
boxes.mesh.count = boxCount;
|
|
2997
3268
|
for (let i = 0; i < boxCount; i++) {
|
|
2998
|
-
const item =
|
|
2999
|
-
const { matrix, halfSize } = item.c.box.userData.obb;
|
|
3269
|
+
const item = boxItems[i];
|
|
3000
3270
|
const idx = item.idx;
|
|
3001
3271
|
const r = (idx & 255) / 255;
|
|
3002
3272
|
const g = ((idx >> 8) & 255) / 255;
|
|
3003
3273
|
const b = ((idx >> 16) & 255) / 255;
|
|
3004
3274
|
_tempColor.setRGB(r, g, b);
|
|
3005
3275
|
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
_tempMatrix.
|
|
3276
|
+
const obb = item.c.box.userData && item.c.box.userData.obb;
|
|
3277
|
+
if (obb) {
|
|
3278
|
+
_tempScale.copy(obb.halfSize).multiplyScalar(2);
|
|
3279
|
+
_tempMatrix.copy(obb.matrix);
|
|
3280
|
+
_tempMatrix.scale(_tempScale);
|
|
3281
|
+
if (typeof this.bizToThreeMatrix !== 'undefined' && this.bizToThreeMatrix) {
|
|
3282
|
+
_tempMatrix.premultiply(this.bizToThreeMatrix);
|
|
3283
|
+
}
|
|
3284
|
+
} else {
|
|
3285
|
+
item.c.box.getSize(_tempScale);
|
|
3286
|
+
item.c.box.getCenter(_tempVec3);
|
|
3287
|
+
_tempMatrix.makeTranslation(_tempVec3.x, _tempVec3.y, _tempVec3.z);
|
|
3288
|
+
_tempMatrix.scale(_tempScale);
|
|
3011
3289
|
}
|
|
3012
3290
|
|
|
3013
3291
|
boxes.mesh.setMatrixAt(i, _tempMatrix);
|
|
@@ -3450,12 +3728,12 @@ export default {
|
|
|
3450
3728
|
this.renderer.toneMapping = prevToneMapping;
|
|
3451
3729
|
} catch (e) {
|
|
3452
3730
|
for (let i = 0; i < candidates.length; i++) {
|
|
3453
|
-
visibleIdSet.add(candidates[i].modelId);
|
|
3731
|
+
visibleIdSet.add(this.formatModelId(candidates[i].modelId));
|
|
3454
3732
|
}
|
|
3455
3733
|
}
|
|
3456
3734
|
} else {
|
|
3457
3735
|
for (let i = 0; i < candidates.length; i++) {
|
|
3458
|
-
visibleIdSet.add(candidates[i].modelId);
|
|
3736
|
+
visibleIdSet.add(this.formatModelId(candidates[i].modelId));
|
|
3459
3737
|
}
|
|
3460
3738
|
}
|
|
3461
3739
|
visibleIds.length = 0;
|
|
@@ -3464,37 +3742,43 @@ export default {
|
|
|
3464
3742
|
const parentsToCheck = new Set();
|
|
3465
3743
|
this.scene.traverse(child => {
|
|
3466
3744
|
if (!child.isInstancedMesh) return;
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
? child.parent.userData.instancesMap
|
|
3476
|
-
: null;
|
|
3745
|
+
const instancesMap =
|
|
3746
|
+
child && child.userData && child.userData.instancesMap instanceof Map
|
|
3747
|
+
? child.userData.instancesMap
|
|
3748
|
+
: child.parent &&
|
|
3749
|
+
child.parent.userData &&
|
|
3750
|
+
child.parent.userData.instancesMap instanceof Map
|
|
3751
|
+
? child.parent.userData.instancesMap
|
|
3752
|
+
: null;
|
|
3477
3753
|
|
|
3754
|
+
// 多实例复用同一个 InstancedMesh 时,仅在全部实例都不可见时整体卸载。
|
|
3755
|
+
if (child.count >= 2) {
|
|
3478
3756
|
if (instancesMap) {
|
|
3479
3757
|
let allInactive = true;
|
|
3480
3758
|
const modelIds = Array.from(instancesMap.keys());
|
|
3759
|
+
let unloadModelId = null;
|
|
3481
3760
|
|
|
3482
3761
|
// 第一遍遍历:检查是否有任何一个实例处于活跃状态(在视锥体内且可见)
|
|
3483
3762
|
for (const modelId of modelIds) {
|
|
3484
3763
|
// 检查已取消勾选模型进行卸载
|
|
3485
|
-
const documentId = modelId.split(':')[1];
|
|
3486
|
-
if (!this.noObserver.documentModelIds.get(documentId)) {
|
|
3487
|
-
|
|
3488
|
-
|
|
3764
|
+
const documentId = String(modelId).split(':')[1];
|
|
3765
|
+
if (documentId && !this.noObserver.documentModelIds.get(documentId)) {
|
|
3766
|
+
if (!skipUnload) this.setInstanceCullingVisible(child, modelId, false);
|
|
3767
|
+
unloadModelId = modelId;
|
|
3768
|
+
continue;
|
|
3489
3769
|
}
|
|
3490
3770
|
|
|
3491
|
-
|
|
3771
|
+
const formattedModelId = this.formatModelId(modelId);
|
|
3772
|
+
if (
|
|
3773
|
+
bypassList &&
|
|
3774
|
+
bypassList.size > 0 &&
|
|
3775
|
+
(bypassList.has(modelId) || bypassList.has(formattedModelId))
|
|
3776
|
+
) {
|
|
3777
|
+
if (!skipUnload) this.setInstanceCullingVisible(child, modelId, true);
|
|
3778
|
+
allInactive = false;
|
|
3779
|
+
continue;
|
|
3780
|
+
}
|
|
3492
3781
|
|
|
3493
|
-
const instanceInfo = instancesMap.get(modelId);
|
|
3494
|
-
const instanceIndex =
|
|
3495
|
-
instanceInfo && typeof instanceInfo.instanceIndex === 'number'
|
|
3496
|
-
? instanceInfo.instanceIndex
|
|
3497
|
-
: null;
|
|
3498
3782
|
const box =
|
|
3499
3783
|
this._boxIndex && this._boxIndex.has(String(modelId))
|
|
3500
3784
|
? this._boxIndex.get(String(modelId))
|
|
@@ -3502,16 +3786,19 @@ export default {
|
|
|
3502
3786
|
? this._boxIndex.get(modelId)
|
|
3503
3787
|
: null;
|
|
3504
3788
|
if (!this.shouldApplyOcclusionByBox(box)) {
|
|
3789
|
+
if (!skipUnload) this.setInstanceCullingVisible(child, modelId, true);
|
|
3505
3790
|
allInactive = false;
|
|
3791
|
+
toLoadSet.delete(formattedModelId);
|
|
3506
3792
|
continue;
|
|
3507
3793
|
}
|
|
3508
3794
|
|
|
3509
|
-
const
|
|
3510
|
-
|
|
3511
|
-
// console.log('modelId', modelId, 'inFrustum', inFrustum, 'modelInVisible', modelInVisible)
|
|
3795
|
+
const modelInVisible = visibleIdSet.has(formattedModelId);
|
|
3796
|
+
toLoadSet.delete(formattedModelId);
|
|
3512
3797
|
|
|
3513
|
-
//
|
|
3514
|
-
|
|
3798
|
+
// 可见集合已经聚合了同一模型的全部图元,任一图元可见时都保留该模型实例。
|
|
3799
|
+
const isActive = modelInVisible;
|
|
3800
|
+
if (!skipUnload) this.setInstanceCullingVisible(child, modelId, isActive);
|
|
3801
|
+
if (isActive) {
|
|
3515
3802
|
allInactive = false;
|
|
3516
3803
|
}
|
|
3517
3804
|
}
|
|
@@ -3525,20 +3812,24 @@ export default {
|
|
|
3525
3812
|
? this._boxIndex.get(modelId)
|
|
3526
3813
|
: null;
|
|
3527
3814
|
if (!this.shouldApplyOcclusionByBox(box)) {
|
|
3528
|
-
|
|
3529
|
-
toLoadSet.delete(modelId);
|
|
3530
|
-
}
|
|
3815
|
+
toLoadSet.delete(this.formatModelId(modelId));
|
|
3531
3816
|
continue;
|
|
3532
3817
|
}
|
|
3533
3818
|
// 无论是否卸载,只要模型已在场景中,就从待加载集合中移除
|
|
3534
|
-
|
|
3535
|
-
toLoadSet.delete(modelId);
|
|
3536
|
-
}
|
|
3819
|
+
toLoadSet.delete(this.formatModelId(modelId));
|
|
3537
3820
|
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3821
|
+
if (!unloadModelId) unloadModelId = modelId;
|
|
3822
|
+
}
|
|
3823
|
+
|
|
3824
|
+
// 仅当所有实例都不活跃时,才整体卸载一次,避免重复释放同一网格。
|
|
3825
|
+
if (
|
|
3826
|
+
!skipUnload &&
|
|
3827
|
+
!this.firstPerSign &&
|
|
3828
|
+
!this.roaming &&
|
|
3829
|
+
allInactive &&
|
|
3830
|
+
unloadModelId !== null
|
|
3831
|
+
) {
|
|
3832
|
+
toUnload.push({ modelId: unloadModelId, child });
|
|
3542
3833
|
}
|
|
3543
3834
|
}
|
|
3544
3835
|
// if (toLoadSet.has(modelId)) {
|
|
@@ -3547,10 +3838,18 @@ export default {
|
|
|
3547
3838
|
return;
|
|
3548
3839
|
}
|
|
3549
3840
|
const modelId =
|
|
3550
|
-
|
|
3841
|
+
instancesMap && instancesMap.size > 0
|
|
3842
|
+
? instancesMap.keys().next().value
|
|
3843
|
+
: child.parent && child.parent.userData && child.parent.userData.instanceId
|
|
3551
3844
|
? child.parent.userData.instanceId
|
|
3552
3845
|
: child.uuid;
|
|
3553
|
-
|
|
3846
|
+
const formattedModelId = this.formatModelId(modelId);
|
|
3847
|
+
if (
|
|
3848
|
+
bypassList &&
|
|
3849
|
+
bypassList.size > 0 &&
|
|
3850
|
+
(bypassList.has(modelId) || bypassList.has(formattedModelId))
|
|
3851
|
+
)
|
|
3852
|
+
return;
|
|
3554
3853
|
const box =
|
|
3555
3854
|
this._boxIndex && this._boxIndex.has(String(modelId))
|
|
3556
3855
|
? this._boxIndex.get(String(modelId))
|
|
@@ -3558,18 +3857,13 @@ export default {
|
|
|
3558
3857
|
? this._boxIndex.get(modelId)
|
|
3559
3858
|
: null;
|
|
3560
3859
|
if (!this.shouldApplyOcclusionByBox(box)) {
|
|
3561
|
-
|
|
3562
|
-
toLoadSet.delete(modelId);
|
|
3563
|
-
}
|
|
3860
|
+
toLoadSet.delete(formattedModelId);
|
|
3564
3861
|
return;
|
|
3565
3862
|
}
|
|
3566
3863
|
|
|
3567
|
-
const
|
|
3568
|
-
|
|
3569
|
-
if (modelInVisible) {
|
|
3570
|
-
toLoadSet.delete(modelId);
|
|
3571
|
-
}
|
|
3572
|
-
if (!skipUnload && !this.firstPerSign && !this.roaming && (!inFrustum || !modelInVisible)) {
|
|
3864
|
+
const modelInVisible = visibleIdSet.has(formattedModelId);
|
|
3865
|
+
toLoadSet.delete(formattedModelId);
|
|
3866
|
+
if (!skipUnload && !this.firstPerSign && !this.roaming && !modelInVisible) {
|
|
3573
3867
|
toUnload.push({ modelId, child });
|
|
3574
3868
|
}
|
|
3575
3869
|
});
|
|
@@ -5030,6 +5324,7 @@ export default {
|
|
|
5030
5324
|
children.instanceMatrix.needsUpdate = true;
|
|
5031
5325
|
});
|
|
5032
5326
|
this.refreshCollisionObbDebug();
|
|
5327
|
+
this.refreshOcclusionBoundsDebug();
|
|
5033
5328
|
this.notifyCameraChange(); // 触发场景更新
|
|
5034
5329
|
break;
|
|
5035
5330
|
}
|
|
@@ -5135,6 +5430,7 @@ export default {
|
|
|
5135
5430
|
this.buildOctreeFromBoxIndex();
|
|
5136
5431
|
}
|
|
5137
5432
|
this.refreshCollisionObbDebug();
|
|
5433
|
+
this.refreshOcclusionBoundsDebug();
|
|
5138
5434
|
},
|
|
5139
5435
|
showOutlinePass(instanceId) {
|
|
5140
5436
|
let targetObj = this.getObjectByName(instanceId);
|
|
@@ -5282,6 +5578,7 @@ export default {
|
|
|
5282
5578
|
children.instanceMatrix.needsUpdate = true;
|
|
5283
5579
|
});
|
|
5284
5580
|
this.refreshCollisionObbDebug();
|
|
5581
|
+
this.refreshOcclusionBoundsDebug();
|
|
5285
5582
|
break;
|
|
5286
5583
|
}
|
|
5287
5584
|
case 'opacity':
|
|
@@ -5703,6 +6000,7 @@ export default {
|
|
|
5703
6000
|
}
|
|
5704
6001
|
this.clearBypassCullingModelIds();
|
|
5705
6002
|
this.hideCollisionObbDebug();
|
|
6003
|
+
this.hideOcclusionBoundsDebug();
|
|
5706
6004
|
|
|
5707
6005
|
if (this.scene) {
|
|
5708
6006
|
this.removeTraverse();
|
|
@@ -5968,6 +6266,7 @@ export default {
|
|
|
5968
6266
|
if (nodeType === 'custom-root') return true;
|
|
5969
6267
|
if (userData.transformControlHelper === true) return true;
|
|
5970
6268
|
if (userData.collisionObbDebugHelper === true) return true;
|
|
6269
|
+
if (userData.occlusionBoundsDebugHelper === true) return true;
|
|
5971
6270
|
if (object.isCamera || object.isLight) return true;
|
|
5972
6271
|
if (typeof object.type === 'string' && /Helper$/i.test(object.type)) return true;
|
|
5973
6272
|
return object.type === 'CSS2DObject' || object.type === 'TransformControlsRoot';
|
|
@@ -7079,6 +7378,7 @@ export default {
|
|
|
7079
7378
|
object.userData.instancesMap instanceof Map &&
|
|
7080
7379
|
object.userData.instancesMap.get(instanceId);
|
|
7081
7380
|
if (instanceInfo && instanceInfo.visible === false) return false;
|
|
7381
|
+
if (instanceInfo && instanceInfo.cullingVisible === false) return false;
|
|
7082
7382
|
|
|
7083
7383
|
const box =
|
|
7084
7384
|
this._boxIndex && this._boxIndex.has(String(instanceId))
|