177 lines
6.1 KiB
JavaScript
Raw Normal View History

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