Files
DianZhanDemo/app/composables/powerStation/modes/useSimplifiedMode.ts

220 lines
6.5 KiB
TypeScript
Raw Normal View History

2025-12-11 01:29:41 +08:00
/**
* -
* 退
*/
import { ElMessage } from 'element-plus'
import type * as THREE from 'three'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { markRaw } from 'vue'
import type { ModelModeContext } from '../types'
// ============================================================
// 配置常量
// ============================================================
/** DRACO解码器路径 */
const DRACO_DECODER_PATH = 'https://www.gstatic.com/draco/v1/decoders/'
/** 简化模式配置 */
const SIMPLIFIED_MODE_CONFIG = {
/** 层级显示的最大深度 */
maxHierarchyDepth: 2,
/** 模型文件扩展名 */
fileExtension: '.glb',
} as const
// ============================================================
// 主钩子函数
// ============================================================
/**
*
* @param context -
*/
export function useSimplifiedMode(context: ModelModeContext) {
const {
store,
isLoading,
archetypeModel,
modelGroup,
objectMap,
modelCache,
replaceWithPBRMaterial,
generateHierarchy,
initEnvironment,
applyLightingMode,
fitView,
rebuildObjectMap,
generateBreadcrumbs,
} = context
// ============================================================
// 模型设置
// ============================================================
/**
*
* @param model -
* @param fileName -
*/
const setupModelFromCache = (model: THREE.Object3D, fileName: string) => {
const clonedModel = model.clone()
const nameWithoutExt = fileName.replace(SIMPLIFIED_MODE_CONFIG.fileExtension, '')
// 设置模型名称
clonedModel.name = nameWithoutExt
// 更新原型模型
archetypeModel.value = markRaw(clonedModel.clone())
// 清空并添加到模型组
modelGroup.clear()
modelGroup.add(clonedModel)
// 生成层级结构(只显示指定深度的节点)
let hierarchy = []
if (clonedModel.children.length > 0) {
// 如果模型有子节点,将每个子节点作为一级节点
clonedModel.children.forEach((child, index) => {
const childPath = index === 0 ? nameWithoutExt : `${nameWithoutExt}~child${index}`
const childHierarchy = generateHierarchy(
child,
childPath,
1,
SIMPLIFIED_MODE_CONFIG.maxHierarchyDepth
)
hierarchy.push(childHierarchy)
})
} else {
// 如果模型没有子节点,使用模型本身作为一级节点
const modelHierarchy = generateHierarchy(
clonedModel,
nameWithoutExt,
1,
SIMPLIFIED_MODE_CONFIG.maxHierarchyDepth
)
hierarchy = [modelHierarchy]
}
store.modelHierarchy = hierarchy
// 生成面包屑(返回供调用者处理)
const breadcrumbs = generateBreadcrumbs(nameWithoutExt)
// 重建物体映射
objectMap.clear()
rebuildObjectMap(clonedModel)
// 初始化环境和光照
initEnvironment()
applyLightingMode()
// 适配视图
fitView(clonedModel)
}
// ============================================================
// 模型加载
// ============================================================
/**
* URL
* @param fileName -
* @returns URL路径
*/
const buildModelUrl = (fileName: string): string => {
// 格式: Root~Parent~Child.glb -> /3DModels/Root/Root~Parent~Child.glb
const nameWithoutExt = fileName.slice(0, -4)
const rootFolder = nameWithoutExt.split('~')[0]
return `/3DModels/${rootFolder}/${fileName}`
}
/**
*
* @param fileName - .glb扩展名
* @param onLoaded -
*/
const loadModelByFileName = (fileName: string, onLoaded?: () => void) => {
// 确保有.glb扩展名
if (!fileName.endsWith(SIMPLIFIED_MODE_CONFIG.fileExtension)) {
fileName += SIMPLIFIED_MODE_CONFIG.fileExtension
}
// 检查缓存
if (store.modelCacheEnabled && modelCache.has(fileName)) {
const cached = modelCache.get(fileName)!
setupModelFromCache(cached, fileName)
if (onLoaded) onLoaded()
return
}
// 开始加载
isLoading.value = true
store.isModelLoading = true
const url = buildModelUrl(fileName)
console.log(`${url} 加载模型`)
// 创建加载器
const loader = new GLTFLoader()
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath(DRACO_DECODER_PATH)
loader.setDRACOLoader(dracoLoader)
loader.load(
url,
gltf => {
const rawModel = gltf.scene
replaceWithPBRMaterial(rawModel)
// 缓存模型
if (store.modelCacheEnabled) {
modelCache.set(fileName, rawModel.clone())
}
// 设置模型
setupModelFromCache(rawModel, fileName)
isLoading.value = false
store.isModelLoading = false
dracoLoader.dispose()
if (onLoaded) onLoaded()
},
undefined,
error => {
console.warn('加载模型失败:', fileName, error)
ElMessage.error(`模型加载失败: ${fileName},请检查网络连接或模型文件是否存在`)
// 回退逻辑:尝试加载父级模型
const nameWithoutExt = fileName.slice(0, -4)
const parts = nameWithoutExt.split('~')
if (parts.length > 1) {
// 移除最后一级,尝试加载父级
parts.pop()
const parentName = parts.join('~')
console.log('回退到父级:', parentName)
loadModelByFileName(parentName, onLoaded)
} else {
// 没有父级可回退
console.error('没有父级可以回退,或根节点加载失败。')
isLoading.value = false
store.isModelLoading = false
dracoLoader.dispose()
}
}
)
}
// ============================================================
// 导出接口
// ============================================================
return {
/** 按文件名加载模型 */
loadModelByFileName,
}
}