Files
fabrikanabytok/apps/fabrikanabytok/lib/three/loaders.ts
2025-11-28 20:48:15 +01:00

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()
}
}