177 lines
6.1 KiB
JavaScript
177 lines
6.1 KiB
JavaScript
|
|
import { ref } from 'vue'
|
|||
|
|
import * as Cesium from 'cesium'
|
|||
|
|
import { getModelCompareConfig } from '../config/modelCompare.config'
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 3D Tiles加载管理 Composable
|
|||
|
|
* 负责加载灾前/灾后3D模型数据,并支持分屏对比功能
|
|||
|
|
*/
|
|||
|
|
export function use3DTiles() {
|
|||
|
|
const beforeTileset = ref(null)
|
|||
|
|
const afterTileset = ref(null)
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 加载3D Tileset
|
|||
|
|
* @param {Cesium.Viewer} viewer - Cesium Viewer 实例
|
|||
|
|
* @param {string} sceneType - 场景类型:'before' 或 'after'
|
|||
|
|
* @param {boolean} autoZoom - 是否自动缩放到模型(默认false,保持用户设置的相机位置)
|
|||
|
|
* @param {Cesium.SplitDirection} splitDirection - 分割方向(默认为 NONE)
|
|||
|
|
*/
|
|||
|
|
const load3DTileset = async (viewer, sceneType = 'after', autoZoom = false, splitDirection = Cesium.SplitDirection.NONE) => {
|
|||
|
|
if (!viewer) return null
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
console.log(`[use3DTiles] 正在加载${sceneType === 'after' ? '灾后' : '灾前'}3D模型...`)
|
|||
|
|
|
|||
|
|
// 从配置中获取 URL
|
|||
|
|
const config = getModelCompareConfig()
|
|||
|
|
const tilesetConfig = sceneType === 'after' ? config.after3DTiles : config.before3DTiles
|
|||
|
|
const url = tilesetConfig.url
|
|||
|
|
|
|||
|
|
const tileset = await Cesium.Cesium3DTileset.fromUrl(url, {
|
|||
|
|
skipLevelOfDetail: true,
|
|||
|
|
baseScreenSpaceError: 100,
|
|||
|
|
skipScreenSpaceErrorFactor: 16,
|
|||
|
|
skipLevels: 1,
|
|||
|
|
immediatelyLoadDesiredLevelOfDetail: false,
|
|||
|
|
loadSiblings: false,
|
|||
|
|
maximumScreenSpaceError: 16.0, // 进一步增大,最大限度减少瓦片细化(之前是8.0)
|
|||
|
|
dynamicScreenSpaceError: false, // 禁用动态屏幕空间误差调整
|
|||
|
|
dynamicScreenSpaceErrorDensity: 0, // 禁用密度调整
|
|||
|
|
dynamicScreenSpaceErrorFactor: 1, // 禁用动态因子
|
|||
|
|
foveatedScreenSpaceError: false, // 禁用视锥细化
|
|||
|
|
foveatedConeSize: 0.1, // 减小视锥大小
|
|||
|
|
foveatedMinimumScreenSpaceErrorRelaxation: 0 // 禁用放松
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 将tileset添加到viewer的primitives
|
|||
|
|
viewer.scene.primitives.add(tileset)
|
|||
|
|
|
|||
|
|
// 设置splitDirection(用于对比模式)
|
|||
|
|
tileset.splitDirection = splitDirection
|
|||
|
|
|
|||
|
|
if (sceneType === 'after') {
|
|||
|
|
afterTileset.value = tileset
|
|||
|
|
} else {
|
|||
|
|
beforeTileset.value = tileset
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`[use3DTiles] ${sceneType === 'after' ? '灾后' : '灾前'}3D模型加载成功,splitDirection: ${splitDirection}`)
|
|||
|
|
|
|||
|
|
// 只有明确要求时才自动缩放到tileset
|
|||
|
|
if (autoZoom) {
|
|||
|
|
await viewer.zoomTo(tileset)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return tileset
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error(`[use3DTiles] 加载${sceneType === 'after' ? '灾后' : '灾前'}3D模型失败:`, error)
|
|||
|
|
return null
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 等待 Tileset 完全就绪(包括首批瓦片加载)
|
|||
|
|
*
|
|||
|
|
* 这个函数确保:
|
|||
|
|
* 1. Tileset 本身已就绪(readyPromise)
|
|||
|
|
* 2. 初始视图的所有瓦片已加载完成(initialTilesLoaded)
|
|||
|
|
*
|
|||
|
|
* @param {Cesium.Cesium3DTileset} tileset - 要等待的 Tileset
|
|||
|
|
* @param {number} timeout - 超时时间(毫秒),默认10秒
|
|||
|
|
* @returns {Promise<void>}
|
|||
|
|
*/
|
|||
|
|
const waitForTilesetReady = async (tileset, timeout = 10000) => {
|
|||
|
|
if (!tileset) {
|
|||
|
|
console.warn('[use3DTiles] waitForTilesetReady: tileset 为空')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 步骤1:等待 Tileset 基础就绪
|
|||
|
|
await tileset.readyPromise
|
|||
|
|
console.log('[use3DTiles] Tileset readyPromise 已完成')
|
|||
|
|
|
|||
|
|
// 步骤2:等待初始瓦片加载完成(带超时)
|
|||
|
|
console.log('[use3DTiles] 等待初始瓦片加载...')
|
|||
|
|
|
|||
|
|
await Promise.race([
|
|||
|
|
// 等待initialTilesLoaded事件
|
|||
|
|
new Promise((resolve) => {
|
|||
|
|
const handleInitialTilesLoaded = () => {
|
|||
|
|
console.log('[use3DTiles] 初始瓦片加载完成(事件触发)')
|
|||
|
|
tileset.initialTilesLoaded.removeEventListener(handleInitialTilesLoaded)
|
|||
|
|
resolve()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
tileset.initialTilesLoaded.addEventListener(handleInitialTilesLoaded)
|
|||
|
|
|
|||
|
|
// 如果已经加载完成,可能事件已经触发过了,直接resolve
|
|||
|
|
// 通过检查tileset.tilesLoaded来判断
|
|||
|
|
if (tileset.tilesLoaded) {
|
|||
|
|
console.log('[use3DTiles] 瓦片已加载,直接继续')
|
|||
|
|
tileset.initialTilesLoaded.removeEventListener(handleInitialTilesLoaded)
|
|||
|
|
resolve()
|
|||
|
|
}
|
|||
|
|
}),
|
|||
|
|
// 超时机制
|
|||
|
|
new Promise((resolve) => {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
console.warn(`[use3DTiles] 等待瓦片加载超时(${timeout}ms),继续执行`)
|
|||
|
|
resolve()
|
|||
|
|
}, timeout)
|
|||
|
|
})
|
|||
|
|
])
|
|||
|
|
|
|||
|
|
console.log('[use3DTiles] Tileset 已完全就绪')
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('[use3DTiles] 等待 Tileset 就绪失败:', error)
|
|||
|
|
// 即使失败也不抛出异常,允许程序继续执行
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 移除3D Tileset
|
|||
|
|
*/
|
|||
|
|
const remove3DTileset = (viewer, sceneType) => {
|
|||
|
|
if (!viewer) return
|
|||
|
|
|
|||
|
|
const tileset = sceneType === 'after' ? afterTileset.value : beforeTileset.value
|
|||
|
|
if (tileset) {
|
|||
|
|
viewer.scene.primitives.remove(tileset)
|
|||
|
|
if (sceneType === 'after') {
|
|||
|
|
afterTileset.value = null
|
|||
|
|
} else {
|
|||
|
|
beforeTileset.value = null
|
|||
|
|
}
|
|||
|
|
console.log(`[use3DTiles] ${sceneType === 'after' ? '灾后' : '灾前'}3D模型已移除`)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新 Tileset 的 splitDirection
|
|||
|
|
* @param {string} sceneType - 场景类型:'before' 或 'after'
|
|||
|
|
* @param {Cesium.SplitDirection} splitDirection - 新的分割方向
|
|||
|
|
*/
|
|||
|
|
const updateTilesetSplitDirection = (sceneType, splitDirection) => {
|
|||
|
|
const tileset = sceneType === 'after' ? afterTileset.value : beforeTileset.value
|
|||
|
|
if (tileset) {
|
|||
|
|
tileset.splitDirection = splitDirection
|
|||
|
|
console.log(`[use3DTiles] ${sceneType === 'after' ? '灾后' : '灾前'}3D模型 splitDirection 已更新为: ${splitDirection}`)
|
|||
|
|
} else {
|
|||
|
|
console.warn(`[use3DTiles] ${sceneType === 'after' ? '灾后' : '灾前'}3D模型不存在,无法更新 splitDirection`)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
beforeTileset,
|
|||
|
|
afterTileset,
|
|||
|
|
load3DTileset,
|
|||
|
|
waitForTilesetReady,
|
|||
|
|
remove3DTileset,
|
|||
|
|
updateTilesetSplitDirection
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default use3DTiles
|