UNPKG

@blooooork/three-optimization

Version:

High-performance optimization tools for Three.js including texture atlas management, asset loading, and memory optimization

150 lines (149 loc) 5.39 kB
import * as g from "three"; import { GLTFLoader as x } from "three/examples/jsm/loaders/GLTFLoader.js"; class A { constructor(t = {}) { this.maxAtlasSize = t.maxAtlasSize || 4096, this.padding = t.padding || 2, this.textureMap = /* @__PURE__ */ new Map(), this.atlases = /* @__PURE__ */ new Map(); } async createAtlas(t, e = "diffuse") { const s = [...t].sort((r, o) => { const n = r.image ? r.image.width * r.image.height : 0; return (o.image ? o.image.width * o.image.height : 0) - n; }), a = document.createElement("canvas"), d = a.getContext("2d"); let i = 0, m = 0, h = 0, l = 0, f = 0; for (const r of s) { if (!r.image) continue; const o = r.image.width + this.padding * 2, n = r.image.height + this.padding * 2; i + o > this.maxAtlasSize && (i = 0, m += h, h = 0), h = Math.max(h, n), l = Math.max(l, i + o), f = Math.max(f, m + n), i += o; } a.width = g.MathUtils.ceilPowerOfTwo(l), a.height = g.MathUtils.ceilPowerOfTwo(f), i = 0, m = 0, h = 0; for (const r of s) { if (!r.image) continue; const o = r.image.width, n = r.image.height; i + o + this.padding * 2 > this.maxAtlasSize && (i = 0, m += h, h = 0), d.drawImage( r.image, i + this.padding, m + this.padding, o, n ); const u = { x: (i + this.padding) / a.width, y: (m + this.padding) / a.height, width: o / a.width, height: n / a.height }; this.textureMap.set(r.uuid, u), h = Math.max(h, n + this.padding * 2), i += o + this.padding * 2; } const c = new g.CanvasTexture(a); return c.flipY = !1, this.atlases.set(e, c), c; } getTextureUVs(t) { return this.textureMap.get(t); } getAtlas(t = "diffuse") { return this.atlases.get(t); } dispose() { this.atlases.forEach((t) => t.dispose()), this.atlases.clear(), this.textureMap.clear(); } } class M { constructor() { this.loadedAssets = /* @__PURE__ */ new Map(), this.loadingPromises = /* @__PURE__ */ new Map(), this.materialCache = /* @__PURE__ */ new Map(), this.gltfLoader = new x(), this.textureLoader = new g.TextureLoader(); } async loadTexture(t) { if (this.loadedAssets.has(t)) return this.loadedAssets.get(t); if (this.loadingPromises.has(t)) return this.loadingPromises.get(t); const e = new Promise((s, a) => { this.textureLoader.load( t, (d) => { this.loadedAssets.set(t, d), this.loadingPromises.delete(t), s(d); }, void 0, a ); }); return this.loadingPromises.set(t, e), e; } async loadModel(t) { if (this.loadedAssets.has(t)) return this.loadedAssets.get(t).scene.clone(); if (this.loadingPromises.has(t)) return (await this.loadingPromises.get(t)).scene.clone(); const e = new Promise((a, d) => { this.gltfLoader.load( t, (i) => { this.loadedAssets.set(t, i), this.loadingPromises.delete(t), a(i); }, void 0, d ); }); return this.loadingPromises.set(t, e), (await e).scene.clone(); } getMaterial(t, e) { if (!this.materialCache.has(t)) { const s = new g.MeshStandardMaterial({ map: e.map, color: e.color, transparent: e.transparent, opacity: e.opacity, side: e.side, roughness: e.roughness || 1, metalness: e.metalness || 0 }); this.materialCache.set(t, s); } return this.materialCache.get(t).clone(); } dispose() { this.loadedAssets.forEach((t) => { t.dispose && t.dispose(), t.scene && t.scene.traverse((e) => { e.geometry && e.geometry.dispose(), e.material && (Array.isArray(e.material) ? e.material.forEach((s) => s.dispose()) : e.material.dispose()); }); }), this.materialCache.forEach((t) => t.dispose()), this.loadedAssets.clear(), this.loadingPromises.clear(), this.materialCache.clear(); } } class y { constructor() { this.tracked = /* @__PURE__ */ new Set(), this.stats = { geometries: 0, textures: 0, materials: 0, objects: 0 }; } track(t) { this.tracked.has(t) || (this.tracked.add(t), this.stats.objects++, t.traverse((e) => { e.geometry && this.stats.geometries++, e.material && (Array.isArray(e.material) ? (this.stats.materials += e.material.length, e.material.forEach((s) => { s.map && this.stats.textures++; })) : (this.stats.materials++, e.material.map && this.stats.textures++)); })); } untrack(t) { this.tracked.has(t) && (this.tracked.delete(t), this.stats.objects--, t.traverse((e) => { e.geometry && this.stats.geometries--, e.material && (Array.isArray(e.material) ? (this.stats.materials -= e.material.length, e.material.forEach((s) => { s.map && this.stats.textures--; })) : (this.stats.materials--, e.material.map && this.stats.textures--)); })); } getStats() { return { ...this.stats }; } dispose(t) { this.untrack(t), t.traverse((e) => { e.geometry && e.geometry.dispose(), e.material && (Array.isArray(e.material) ? e.material.forEach((s) => { s.map && s.map.dispose(), s.dispose(); }) : (e.material.map && e.material.map.dispose(), e.material.dispose())); }); } } export { M as AssetManager, y as MemoryManager, A as TextureAtlasManager };