import { AnimationMixer, Group, Object3D } from 'three' import { GLTF, GLTFLoader } from '/@/loaders/GLTFLoader' import { OBJLoader } from '/@/loaders/OBJLoader' import { DRACOLoader } from '/@/loaders/DRACOLoader' export enum ModelType { GLTF, GLB, OBJ, } export type Model = Object3D | GLTF | Group | null export type ModelLoader = GLTFLoader | OBJLoader const gltfLoader = new GLTFLoader() export interface ModelOptions { draco?: boolean animated: boolean } const MapLoaders: { [key: number]: ModelLoader } = { [ModelType.GLTF]: gltfLoader, [ModelType.GLB]: gltfLoader, [ModelType.OBJ]: new OBJLoader(), } export function useModel() { const { mixer, generateActionsFromAnimations } = useAnimationMixer() const dracoLoader = new DRACOLoader() dracoLoader.setDecoderPath('/draco/') function loadModel(url: string, options?: ModelOptions): Promise { if (!url) { console.error('[useModelLoader] - No url provided') return Promise.resolve(null) } const extension = url.split('.').pop() as string const loader = MapLoaders[ModelType[extension.toUpperCase() as keyof typeof ModelType]] if (options?.draco) { if (extension === 'gltf' || extension === 'glb') { ;(loader as GLTFLoader).setDRACOLoader(dracoLoader) } } return new Promise((resolve, reject) => { loader.load( url, (model: GLTF | Group) => { if (extension === 'gltf' || extension === 'glb') { const gltfModel = (model as GLTF).scene if (options?.animated) { mixer.value = new AnimationMixer(gltfModel) gltfModel.actions = generateActionsFromAnimations((model as GLTF).animations) gltfModel.currentAction = Object.values(gltfModel.actions)[0] } resolve(gltfModel) } else if (extension === 'obj') { resolve(model as Group) } }, () => {}, error => { console.error(error) reject(new Error('[useModelLoader] - Failed to load model')) }, ) }) } function loadAnimatedModel(url: string, options?: ModelOptions): Promise { return loadModel(url, { ...options, animated: true }) } return { loadModel, loadAnimatedModel, mixer, } }