Files
pihkaal-me/app/composables/useAssets.ts.in
2026-02-16 16:27:36 +01:00

105 lines
2.2 KiB
TypeScript

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<string, THREE.Group>();
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?v={{ATLAS_HASH}}';
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,
};
type Assets = typeof assets;
export const useAssets = <T = Assets>(selector: (assets: Assets) => T = (a) => a as T) => {
return {
assets: selector(assets),
loaded,
total,
isReady,
};
};