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