552 lines
17 KiB
TypeScript
552 lines
17 KiB
TypeScript
|
|
/**
|
|||
|
|
* 模型管理系统
|
|||
|
|
* 处理3D模型的加载、层级结构生成、材质处理和相机适配
|
|||
|
|
*/
|
|||
|
|
import * as TWEEN from '@tweenjs/tween.js'
|
|||
|
|
import * as THREE from 'three'
|
|||
|
|
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js'
|
|||
|
|
import { usePowerStationStore } from '~/stores/powerStation'
|
|||
|
|
import { useFullMode } from './modes/useFullMode'
|
|||
|
|
import { useSimplifiedMode } from './modes/useSimplifiedMode'
|
|||
|
|
import type { BreadcrumbItem, LightingMode, ModelHierarchyNode } from './types'
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 配置常量
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
/** 光源配置 */
|
|||
|
|
const LIGHT_CONFIG = {
|
|||
|
|
/** 环境光颜色 */
|
|||
|
|
ambientColor: 0xffffff,
|
|||
|
|
/** 环境光强度 */
|
|||
|
|
ambientIntensity: 0.6,
|
|||
|
|
/** 方向光颜色 */
|
|||
|
|
directionalColor: 0xffffff,
|
|||
|
|
/** 方向光强度 */
|
|||
|
|
directionalIntensity: 1,
|
|||
|
|
/** 方向光位置 */
|
|||
|
|
directionalPosition: new THREE.Vector3(5, 3, 5),
|
|||
|
|
} as const
|
|||
|
|
|
|||
|
|
/** PBR材质配置 */
|
|||
|
|
const PBR_MATERIAL_CONFIG = {
|
|||
|
|
/** 金属度 */
|
|||
|
|
metalness: 0.6,
|
|||
|
|
/** 粗糙度 */
|
|||
|
|
roughness: 0.6,
|
|||
|
|
/** 环境贴图强度 */
|
|||
|
|
envMapIntensity: 1.5,
|
|||
|
|
} as const
|
|||
|
|
|
|||
|
|
/** 相机动画配置 */
|
|||
|
|
const CAMERA_ANIMATION_CONFIG = {
|
|||
|
|
/** 动画时长(毫秒) */
|
|||
|
|
duration: 900,
|
|||
|
|
/** 距离系数 */
|
|||
|
|
distanceMultiplier: 1.2,
|
|||
|
|
} as const
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 主钩子函数
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 模型管理组合式函数
|
|||
|
|
* @param getScene - 获取场景
|
|||
|
|
* @param getCamera - 获取相机
|
|||
|
|
* @param getControls - 获取控制器
|
|||
|
|
* @param getRenderer - 获取渲染器
|
|||
|
|
*/
|
|||
|
|
export function useModelManager(
|
|||
|
|
getScene: () => THREE.Scene,
|
|||
|
|
getCamera: () => THREE.PerspectiveCamera,
|
|||
|
|
getControls: () => any, // OrbitControls
|
|||
|
|
getRenderer: () => THREE.WebGLRenderer
|
|||
|
|
) {
|
|||
|
|
const store = usePowerStationStore()
|
|||
|
|
const isLoading = ref(true)
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 模型数据
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
// 使用 markRaw 标记 Three.js 对象为非响应式,防止 Vue DevTools 追踪导致性能问题
|
|||
|
|
const modelGroup = markRaw(new THREE.Group())
|
|||
|
|
const archetypeModel = shallowRef<THREE.Object3D | null>(null)
|
|||
|
|
|
|||
|
|
// 使用普通 Map(Three.js 对象已是 markRaw,Map 本身不需要响应式)
|
|||
|
|
const objectMap = new Map<string, THREE.Object3D>()
|
|||
|
|
const modelCache = new Map<string, THREE.Object3D>()
|
|||
|
|
|
|||
|
|
// 环境贴图
|
|||
|
|
let envTexture: THREE.Texture | null = null
|
|||
|
|
|
|||
|
|
// 光照模式
|
|||
|
|
const lightingMode = ref<LightingMode>('basic')
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 初始化
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 初始化模型系统
|
|||
|
|
* 添加模型组和基础光源到场景
|
|||
|
|
*/
|
|||
|
|
const initModelSystem = () => {
|
|||
|
|
const scene = getScene()
|
|||
|
|
if (!scene) return
|
|||
|
|
|
|||
|
|
// 添加模型组
|
|||
|
|
scene.add(modelGroup)
|
|||
|
|
|
|||
|
|
// 添加基础光源
|
|||
|
|
const ambientLight = new THREE.AmbientLight(
|
|||
|
|
LIGHT_CONFIG.ambientColor,
|
|||
|
|
LIGHT_CONFIG.ambientIntensity
|
|||
|
|
)
|
|||
|
|
scene.add(ambientLight)
|
|||
|
|
|
|||
|
|
const dirLight = new THREE.DirectionalLight(
|
|||
|
|
LIGHT_CONFIG.directionalColor,
|
|||
|
|
LIGHT_CONFIG.directionalIntensity
|
|||
|
|
)
|
|||
|
|
dirLight.position.copy(LIGHT_CONFIG.directionalPosition)
|
|||
|
|
scene.add(dirLight)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 初始化环境贴图
|
|||
|
|
*/
|
|||
|
|
const initEnvironment = () => {
|
|||
|
|
const renderer = getRenderer()
|
|||
|
|
const scene = getScene()
|
|||
|
|
if (!renderer || !scene) return
|
|||
|
|
|
|||
|
|
// 避免重复创建
|
|||
|
|
if (envTexture) return
|
|||
|
|
|
|||
|
|
const pmremGenerator = new THREE.PMREMGenerator(renderer)
|
|||
|
|
pmremGenerator.compileEquirectangularShader()
|
|||
|
|
|
|||
|
|
const roomEnvironment = new RoomEnvironment()
|
|||
|
|
envTexture = pmremGenerator.fromScene(roomEnvironment).texture
|
|||
|
|
|
|||
|
|
if (lightingMode.value === 'advanced') {
|
|||
|
|
scene.environment = envTexture
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清理临时对象
|
|||
|
|
roomEnvironment.dispose()
|
|||
|
|
pmremGenerator.dispose()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 层级结构生成
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 生成模型层级结构
|
|||
|
|
* @param object - Three.js 对象
|
|||
|
|
* @param currentPath - 当前路径
|
|||
|
|
* @param depth - 当前深度
|
|||
|
|
* @param maxDepth - 最大深度
|
|||
|
|
* @returns 层级节点
|
|||
|
|
*/
|
|||
|
|
const generateHierarchy = (
|
|||
|
|
object: THREE.Object3D,
|
|||
|
|
currentPath: string,
|
|||
|
|
depth: number = 1,
|
|||
|
|
maxDepth: number = Infinity
|
|||
|
|
): ModelHierarchyNode => {
|
|||
|
|
const isRoot = object === archetypeModel.value
|
|||
|
|
const nodeId = isRoot ? store.currentNodeId || currentPath : currentPath
|
|||
|
|
|
|||
|
|
// 设置 userData.id
|
|||
|
|
object.userData.id = nodeId
|
|||
|
|
|
|||
|
|
const node: ModelHierarchyNode = {
|
|||
|
|
id: nodeId,
|
|||
|
|
label: isRoot
|
|||
|
|
? store.currentNodeId || object.name || `节点 ${nodeId}`
|
|||
|
|
: object.name || `节点 ${nodeId}`,
|
|||
|
|
children: [],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否为完整物体(有名称且包含多个子Mesh)
|
|||
|
|
const isCompleteObject =
|
|||
|
|
object.name && object.children.filter(child => child.type === 'Mesh').length > 1
|
|||
|
|
|
|||
|
|
// 达到最大深度则停止递归
|
|||
|
|
if (depth >= maxDepth) {
|
|||
|
|
return node
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 递归处理子节点
|
|||
|
|
object.children.forEach(child => {
|
|||
|
|
if (child.type === 'Mesh' || child.type === 'Group' || child.type === 'Object3D') {
|
|||
|
|
const childName = child.name || '子对象'
|
|||
|
|
const childPath = `${currentPath}~${childName}`
|
|||
|
|
|
|||
|
|
// 跳过完整物体中的无名Mesh子节点
|
|||
|
|
if (isCompleteObject && child.type === 'Mesh' && !child.name) {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const childNode = generateHierarchy(child, childPath, depth + 1, maxDepth)
|
|||
|
|
node.children.push(childNode)
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
return node
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 节点查找
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 通过ID查找节点
|
|||
|
|
* @param root - 根对象
|
|||
|
|
* @param id - 节点ID
|
|||
|
|
* @returns 找到的节点或null
|
|||
|
|
*/
|
|||
|
|
const findNodeById = (root: THREE.Object3D, id: string): THREE.Object3D | null => {
|
|||
|
|
if (root.userData.id === id) return root
|
|||
|
|
|
|||
|
|
for (const child of root.children) {
|
|||
|
|
const found = findNodeById(child, id)
|
|||
|
|
if (found) return found
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 重建物体映射表
|
|||
|
|
* @param root - 根对象
|
|||
|
|
*/
|
|||
|
|
const rebuildObjectMap = (root: THREE.Object3D) => {
|
|||
|
|
if (root.userData.id) {
|
|||
|
|
objectMap.set(root.userData.id, root)
|
|||
|
|
}
|
|||
|
|
root.children.forEach(child => rebuildObjectMap(child))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 面包屑生成
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 生成面包屑导航数据
|
|||
|
|
* @param nodeId - 节点ID
|
|||
|
|
* @returns 面包屑数组
|
|||
|
|
*/
|
|||
|
|
const generateBreadcrumbs = (nodeId: string): BreadcrumbItem[] => {
|
|||
|
|
if (!archetypeModel.value) return []
|
|||
|
|
|
|||
|
|
const breadcrumbsList: BreadcrumbItem[] = []
|
|||
|
|
const nodeIdParts = nodeId.split('~')
|
|||
|
|
|
|||
|
|
if (nodeIdParts.length > 0) {
|
|||
|
|
let currentPath = ''
|
|||
|
|
|
|||
|
|
for (let i = 0; i < nodeIdParts.length; i++) {
|
|||
|
|
const part = nodeIdParts[i]
|
|||
|
|
currentPath = i === 0 ? part : `${currentPath}~${part}`
|
|||
|
|
|
|||
|
|
const currentObject = findNodeById(archetypeModel.value, currentPath)
|
|||
|
|
const displayText = currentObject
|
|||
|
|
? currentObject.name
|
|||
|
|
: part || findNodeById(archetypeModel.value, currentPath)?.name || part
|
|||
|
|
|
|||
|
|
const item: BreadcrumbItem = { text: displayText }
|
|||
|
|
|
|||
|
|
if (currentPath !== nodeId) {
|
|||
|
|
item.to = `?currentNodeId=${currentPath}`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
breadcrumbsList.push(item)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用当前节点ID的第一部分作为面包屑首项
|
|||
|
|
if (breadcrumbsList.length > 0 && store.currentNodeId) {
|
|||
|
|
const firstPart = store.currentNodeId.split('~')[0]
|
|||
|
|
breadcrumbsList[0].text = firstPart
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return breadcrumbsList
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 相机适配
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 适配视图到目标对象
|
|||
|
|
* @param targetObj - 目标对象(默认为modelGroup)
|
|||
|
|
* @param keepOrientation - 是否保持当前相机朝向
|
|||
|
|
*/
|
|||
|
|
const fitView = (targetObj?: THREE.Object3D, keepOrientation: boolean = true) => {
|
|||
|
|
const obj = targetObj || modelGroup
|
|||
|
|
const camera = getCamera()
|
|||
|
|
const controls = getControls()
|
|||
|
|
|
|||
|
|
console.log('[fitView] 开始执行:', {
|
|||
|
|
hasTargetObj: !!targetObj,
|
|||
|
|
objName: obj?.name,
|
|||
|
|
objChildrenCount: obj?.children?.length,
|
|||
|
|
hasCamera: !!camera,
|
|||
|
|
hasControls: !!controls,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
if (!obj || !camera || !controls) {
|
|||
|
|
console.warn('[fitView] 提前返回: obj/camera/controls 缺失')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算包围盒
|
|||
|
|
const box = new THREE.Box3().setFromObject(obj)
|
|||
|
|
console.log('[fitView] 包围盒:', {
|
|||
|
|
isEmpty: box.isEmpty(),
|
|||
|
|
min: box.min.toArray(),
|
|||
|
|
max: box.max.toArray(),
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
if (box.isEmpty()) {
|
|||
|
|
console.warn('[fitView] 提前返回: 包围盒为空')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const size = box.getSize(new THREE.Vector3())
|
|||
|
|
const center = box.getCenter(new THREE.Vector3())
|
|||
|
|
|
|||
|
|
// 计算适配距离
|
|||
|
|
const maxSize = Math.max(size.x, size.y, size.z)
|
|||
|
|
const fitHeightDistance = maxSize / (2 * Math.atan((Math.PI * camera.fov) / 360))
|
|||
|
|
const fitWidthDistance = fitHeightDistance / camera.aspect
|
|||
|
|
const distance = CAMERA_ANIMATION_CONFIG.distanceMultiplier * Math.max(fitHeightDistance, fitWidthDistance)
|
|||
|
|
|
|||
|
|
// 计算相机方向
|
|||
|
|
let direction: THREE.Vector3
|
|||
|
|
if (keepOrientation) {
|
|||
|
|
direction = new THREE.Vector3().subVectors(camera.position, controls.target).normalize()
|
|||
|
|
if (direction.lengthSq() < 0.0001) {
|
|||
|
|
direction = new THREE.Vector3(1, 1, 1).normalize()
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
direction = new THREE.Vector3(1, 1, 1).normalize()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const targetPosition = center.clone().add(direction.multiplyScalar(distance))
|
|||
|
|
|
|||
|
|
console.log('[fitView] 动画参数:', {
|
|||
|
|
size: size.toArray(),
|
|||
|
|
center: center.toArray(),
|
|||
|
|
distance,
|
|||
|
|
currentCameraPos: camera.position.toArray(),
|
|||
|
|
targetCameraPos: targetPosition.toArray(),
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 相机位置动画
|
|||
|
|
new TWEEN.Tween(camera.position)
|
|||
|
|
.to({ x: targetPosition.x, y: targetPosition.y, z: targetPosition.z }, CAMERA_ANIMATION_CONFIG.duration)
|
|||
|
|
.easing(TWEEN.Easing.Quadratic.InOut)
|
|||
|
|
.onStart(() => console.log('[fitView] 相机位置动画开始'))
|
|||
|
|
.onComplete(() => console.log('[fitView] 相机位置动画完成'))
|
|||
|
|
.start()
|
|||
|
|
|
|||
|
|
// 控制目标动画
|
|||
|
|
new TWEEN.Tween(controls.target)
|
|||
|
|
.to({ x: center.x, y: center.y, z: center.z }, CAMERA_ANIMATION_CONFIG.duration)
|
|||
|
|
.easing(TWEEN.Easing.Quadratic.InOut)
|
|||
|
|
.onStart(() => console.log('[fitView] 控制目标动画开始'))
|
|||
|
|
.onUpdate(() => {
|
|||
|
|
controls.update()
|
|||
|
|
})
|
|||
|
|
.onComplete(() => console.log('[fitView] 控制目标动画完成'))
|
|||
|
|
.start()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 材质处理
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 替换为PBR材质
|
|||
|
|
* @param object - 要处理的对象
|
|||
|
|
*/
|
|||
|
|
const replaceWithPBRMaterial = (object: THREE.Object3D) => {
|
|||
|
|
object.traverse(child => {
|
|||
|
|
if ((child as THREE.Mesh).isMesh) {
|
|||
|
|
const mesh = child as THREE.Mesh
|
|||
|
|
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material]
|
|||
|
|
const newMaterials: THREE.MeshStandardMaterial[] = []
|
|||
|
|
|
|||
|
|
materials.forEach(mat => {
|
|||
|
|
const color =
|
|||
|
|
(mat as THREE.MeshBasicMaterial).color instanceof THREE.Color
|
|||
|
|
? (mat as THREE.MeshBasicMaterial).color.clone()
|
|||
|
|
: new THREE.Color(0xaaaaaa)
|
|||
|
|
|
|||
|
|
// 获取原始贴图
|
|||
|
|
const originalMap = (mat as THREE.MeshBasicMaterial).map || null
|
|||
|
|
const alphaMap = originalMap || null
|
|||
|
|
const useAlpha = originalMap !== null
|
|||
|
|
|
|||
|
|
const pbrMaterial = new THREE.MeshStandardMaterial({
|
|||
|
|
color: color,
|
|||
|
|
metalness: PBR_MATERIAL_CONFIG.metalness,
|
|||
|
|
roughness: PBR_MATERIAL_CONFIG.roughness,
|
|||
|
|
transparent: useAlpha || ((mat as THREE.MeshBasicMaterial).transparent ?? false),
|
|||
|
|
opacity: useAlpha ? 1 : ((mat as THREE.MeshBasicMaterial).opacity ?? 1),
|
|||
|
|
side: mat.side || THREE.FrontSide,
|
|||
|
|
map: originalMap,
|
|||
|
|
alphaMap: alphaMap,
|
|||
|
|
normalMap: (mat as THREE.MeshStandardMaterial).normalMap || null,
|
|||
|
|
envMapIntensity: PBR_MATERIAL_CONFIG.envMapIntensity,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
newMaterials.push(pbrMaterial)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
if (newMaterials.length > 0) {
|
|||
|
|
mesh.material = (Array.isArray(mesh.material) ? newMaterials : newMaterials[0]) as
|
|||
|
|
| THREE.Material
|
|||
|
|
| THREE.Material[]
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新材质模式
|
|||
|
|
* @param root - 根对象
|
|||
|
|
* @param mode - 光照模式
|
|||
|
|
*/
|
|||
|
|
const updateMaterials = (root: THREE.Object3D, mode: LightingMode) => {
|
|||
|
|
root.traverse(child => {
|
|||
|
|
if ((child as THREE.Mesh).isMesh) {
|
|||
|
|
const mesh = child as THREE.Mesh
|
|||
|
|
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material]
|
|||
|
|
|
|||
|
|
const newMaterials = materials.map(mat => {
|
|||
|
|
if (mode === 'basic') {
|
|||
|
|
// 切换到基础模式
|
|||
|
|
if (mat.type === 'MeshLambertMaterial') return mat
|
|||
|
|
if (mat.userData.lambertPartner) return mat.userData.lambertPartner
|
|||
|
|
|
|||
|
|
const source = mat as THREE.MeshStandardMaterial
|
|||
|
|
const lambert = new THREE.MeshLambertMaterial({
|
|||
|
|
color: source.color,
|
|||
|
|
map: source.map,
|
|||
|
|
transparent: source.transparent,
|
|||
|
|
opacity: source.opacity,
|
|||
|
|
side: source.side,
|
|||
|
|
alphaMap: source.alphaMap,
|
|||
|
|
aoMap: source.aoMap,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
mat.userData.lambertPartner = lambert
|
|||
|
|
lambert.userData.pbrPartner = mat
|
|||
|
|
return lambert
|
|||
|
|
} else {
|
|||
|
|
// 切换到高级模式
|
|||
|
|
if (mat.type === 'MeshStandardMaterial') return mat
|
|||
|
|
if (mat.userData.pbrPartner) return mat.userData.pbrPartner
|
|||
|
|
return mat
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
mesh.material = Array.isArray(mesh.material) ? newMaterials : newMaterials[0]
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 光照模式控制
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 切换光照模式
|
|||
|
|
*/
|
|||
|
|
const toggleLighting = () => {
|
|||
|
|
lightingMode.value = lightingMode.value === 'basic' ? 'advanced' : 'basic'
|
|||
|
|
applyLightingMode()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 应用当前光照模式
|
|||
|
|
*/
|
|||
|
|
const applyLightingMode = () => {
|
|||
|
|
const scene = getScene()
|
|||
|
|
if (!scene) return
|
|||
|
|
|
|||
|
|
if (lightingMode.value === 'advanced') {
|
|||
|
|
if (envTexture) scene.environment = envTexture
|
|||
|
|
} else {
|
|||
|
|
scene.environment = null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (modelGroup) updateMaterials(modelGroup, lightingMode.value)
|
|||
|
|
if (archetypeModel.value) updateMaterials(archetypeModel.value, lightingMode.value)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 模式上下文
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
/** 供加载模式使用的共享上下文 */
|
|||
|
|
const context = {
|
|||
|
|
store,
|
|||
|
|
isLoading,
|
|||
|
|
archetypeModel,
|
|||
|
|
modelGroup,
|
|||
|
|
objectMap,
|
|||
|
|
modelCache,
|
|||
|
|
replaceWithPBRMaterial,
|
|||
|
|
generateHierarchy,
|
|||
|
|
initEnvironment,
|
|||
|
|
applyLightingMode,
|
|||
|
|
fitView,
|
|||
|
|
findNodeById,
|
|||
|
|
rebuildObjectMap,
|
|||
|
|
generateBreadcrumbs,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 实例化加载模式
|
|||
|
|
const { loadModel, processModel } = useFullMode(context)
|
|||
|
|
const { loadModelByFileName } = useSimplifiedMode(context)
|
|||
|
|
|
|||
|
|
// ============================================================
|
|||
|
|
// 导出接口
|
|||
|
|
// ============================================================
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
// 数据
|
|||
|
|
modelGroup,
|
|||
|
|
isLoading,
|
|||
|
|
lightingMode,
|
|||
|
|
objectMap,
|
|||
|
|
archetypeModel,
|
|||
|
|
|
|||
|
|
// 初始化
|
|||
|
|
initModelSystem,
|
|||
|
|
|
|||
|
|
// 模型加载
|
|||
|
|
loadModel,
|
|||
|
|
processModel,
|
|||
|
|
loadModelByFileName,
|
|||
|
|
|
|||
|
|
// 视图控制
|
|||
|
|
fitView,
|
|||
|
|
|
|||
|
|
// 光照控制
|
|||
|
|
toggleLighting,
|
|||
|
|
|
|||
|
|
// 工具函数
|
|||
|
|
findNodeById,
|
|||
|
|
generateBreadcrumbs,
|
|||
|
|
}
|
|||
|
|
}
|