88 lines
2.4 KiB
TypeScript
88 lines
2.4 KiB
TypeScript
/**
|
|
* Advanced Model Loaders
|
|
*/
|
|
|
|
import * as THREE from 'three'
|
|
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
|
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
|
|
|
|
export interface CachedModel {
|
|
scene: THREE.Group
|
|
animations: THREE.AnimationClip[]
|
|
cameras: THREE.Camera[]
|
|
timestamp: number
|
|
}
|
|
|
|
export class ModelLoader {
|
|
private static instance: ModelLoader
|
|
private gltfLoader: GLTFLoader
|
|
private dracoLoader: DRACOLoader
|
|
private modelCache: Map<string, CachedModel> = new Map()
|
|
private loadingPromises: Map<string, Promise<CachedModel>> = new Map()
|
|
|
|
private constructor() {
|
|
this.gltfLoader = new GLTFLoader()
|
|
this.dracoLoader = new DRACOLoader()
|
|
this.dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/')
|
|
this.gltfLoader.setDRACOLoader(this.dracoLoader)
|
|
}
|
|
|
|
static getInstance(): ModelLoader {
|
|
if (!ModelLoader.instance) {
|
|
ModelLoader.instance = new ModelLoader()
|
|
}
|
|
return ModelLoader.instance
|
|
}
|
|
|
|
async loadModel(url: string, options?: { onProgress?: (progress: number) => void }): Promise<CachedModel> {
|
|
if (this.modelCache.has(url)) {
|
|
const cached = this.modelCache.get(url)!
|
|
return { ...cached, scene: cached.scene.clone() }
|
|
}
|
|
|
|
if (this.loadingPromises.has(url)) {
|
|
return this.loadingPromises.get(url)!
|
|
}
|
|
|
|
const loadPromise = new Promise<CachedModel>((resolve, reject) => {
|
|
this.gltfLoader.load(
|
|
url,
|
|
(gltf) => {
|
|
const processed: CachedModel = {
|
|
scene: gltf.scene,
|
|
animations: gltf.animations || [],
|
|
cameras: gltf.cameras || [],
|
|
timestamp: Date.now()
|
|
}
|
|
|
|
this.modelCache.set(url, processed)
|
|
this.loadingPromises.delete(url)
|
|
resolve({ ...processed, scene: processed.scene.clone() })
|
|
},
|
|
(progress) => {
|
|
if (options?.onProgress) {
|
|
const percent = (progress.loaded / progress.total) * 100
|
|
options.onProgress(percent)
|
|
}
|
|
},
|
|
reject
|
|
)
|
|
})
|
|
|
|
this.loadingPromises.set(url, loadPromise)
|
|
return loadPromise
|
|
}
|
|
|
|
getCacheStats() {
|
|
return {
|
|
size: this.modelCache.size,
|
|
urls: Array.from(this.modelCache.keys())
|
|
}
|
|
}
|
|
|
|
dispose(): void {
|
|
this.dracoLoader.dispose()
|
|
this.modelCache.clear()
|
|
}
|
|
}
|