import * as THREE from "three"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"; type Rect = [number, number, number, number]; let atlasImage: HTMLImageElement | null = null; const modelCache = new Map(); const loaded = ref(0); const total = ref({{TOTAL}}); const isReady = computed(() => loaded.value === total.value); if (import.meta.client) { atlasImage = document.createElement('img'); atlasImage.src = '/nds/atlas.webp'; if (atlasImage.complete) { loaded.value += 1; } else { atlasImage.onload = () => { loaded.value += 1; }; } } const drawAtlasImage = ( ctx: CanvasRenderingContext2D, [sx, sy, sw, sh]: Rect, [dx, dy, dw, dh]: Rect, opts?: Partial<{ colored: boolean }>, ): void => { if (!atlasImage) return; if (opts?.colored) { const app = useAppStore(); sh /= 16; sy += (app.color.row * 4 + app.color.col) * sh; dh = sh; } ctx.drawImage(atlasImage, sx, sy, sw, sh, dx, dy, dw, dh); }; const createModel = (path: string) => { const cached = modelCache.get(path); if (cached) { return cached; } const model = new THREE.Group(); modelCache.set(path, model); new GLTFLoader().load( path, (gltf) => { for (const child of [...gltf.scene.children]) { model.add(child); } loaded.value += 1; }, undefined, (error) => { console.error(`Error loading model ${path}:`, error); loaded.value += 1; } ); return model; }; export type AtlasImage = { draw: ( ctx: CanvasRenderingContext2D, x: number, y: number, opts?: Partial<{ colored: boolean }>, ) => void; rect: { x: number; y: number; width: number; height: number }; }; type ImageTree = { [key: string]: AtlasImage | ImageTree; }; type ModelTree = { [key: string]: THREE.Group | ModelTree; }; const assets = { images: {{IMAGES}} as const satisfies ImageTree, models: {{MODELS}} as const satisfies ModelTree, }; export const useAssets = () => { return { assets, loaded, total, isReady, }; };