feat(assets): load 3d models as well

This commit is contained in:
2025-12-17 18:00:11 +01:00
parent b85899617b
commit e190636544
7 changed files with 113 additions and 78 deletions

View File

@@ -0,0 +1,7 @@
<script setup lang="ts">
const { loaded, total } = useAssets();
</script>
<template>
<p>{{ loaded }} / {{ total }}</p>
</template>

View File

@@ -1,5 +1,4 @@
<script setup lang="ts">
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { useLoop, useTresContext } from "@tresjs/core";
import * as THREE from "three";
@@ -8,12 +7,9 @@ const props = defineProps<{
bottomScreenCanvas: HTMLCanvasElement | null;
}>();
const { state: model } = useLoader(
GLTFLoader,
"/models/nintendo-ds/scene.gltf",
);
const { assets } = useAssets();
const scene = computed(() => model.value?.scene);
const model = assets.nintendoDs.scene.clone(true);
let topScreenTexture: THREE.CanvasTexture | null = null;
let bottomScreenTexture: THREE.CanvasTexture | null = null;
@@ -43,6 +39,15 @@ const requireMesh = (key: string): THREE.Mesh => {
const { camera, renderer } = useTresContext();
model.scale.set(100, 100, 100);
meshes.clear();
model.traverse((child) => {
if (child instanceof THREE.Mesh) {
meshes.set(child.name, child);
}
});
watch(
() => [props.topScreenCanvas, props.bottomScreenCanvas],
() => {
@@ -61,41 +66,22 @@ watch(
bottomScreenTexture.flipY = false;
bottomScreenTexture.repeat.set(1, 1024 / 532);
bottomScreenTexture.offset.set(0, -1024 / 532 + 1);
requireMesh(TOP_SCREEN).material = new THREE.MeshStandardMaterial({
map: topScreenTexture,
emissive: new THREE.Color(0x222222),
emissiveIntensity: 0.5,
});
requireMesh(BOTTOM_SCREEN).material = new THREE.MeshStandardMaterial({
map: bottomScreenTexture,
emissive: new THREE.Color(0x222222),
emissiveIntensity: 0.5,
});
},
{ immediate: true },
);
watch(scene, () => {
if (!scene.value) return;
meshes.clear();
scene.value.scale.set(100, 100, 100);
scene.value.traverse((child) => {
if (child instanceof THREE.Mesh) {
meshes.set(child.name, child);
}
});
if (!topScreenTexture || !bottomScreenTexture)
throw new Error(
"topScreenTexture and bottomScreenTexture should be initialized",
);
requireMesh(TOP_SCREEN).material = new THREE.MeshStandardMaterial({
map: topScreenTexture,
emissive: new THREE.Color(0x222222),
emissiveIntensity: 0.5,
});
requireMesh(BOTTOM_SCREEN).material = new THREE.MeshStandardMaterial({
map: bottomScreenTexture,
emissive: new THREE.Color(0x222222),
emissiveIntensity: 0.5,
});
});
const { onRender } = useLoop();
const physicalButtonsDown = new Set<string>();
@@ -192,8 +178,6 @@ const pressButton = (button: string) => {
};
const handleClick = (event: MouseEvent) => {
if (!scene.value) return;
const domElement = renderer.instance.domElement;
const rect = domElement.getBoundingClientRect();
@@ -206,7 +190,7 @@ const handleClick = (event: MouseEvent) => {
camera.activeCamera.value,
);
const intersects = raycaster.intersectObjects(scene.value.children, true);
const intersects = raycaster.intersectObjects(model.children, true);
const intersection = intersects[0];
if (!intersection?.uv) return;
@@ -311,5 +295,5 @@ onUnmounted(() => {
</script>
<template>
<primitive v-if="scene" :object="scene" />
<primitive :object="model" />
</template>

View File

@@ -1,4 +1,8 @@
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
const imageCache = new Map<string, HTMLImageElement>();
const modelCache = new Map<string, THREE.Group>();
const loaded = ref(0);
const total = ref({{TOTAL}});
@@ -22,6 +26,34 @@ const createImage = (path: string) => {
return img;
};
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;
};
const assets = {{ASSETS}};
export const useAssets = () => {

View File

@@ -3,6 +3,8 @@ import type { Screen as NDSScreen } from "#components";
type ScreenInstance = InstanceType<typeof NDSScreen>;
const { isReady } = useAssets();
const route = useRoute();
const screen = computed(() => route.query.screen as string | undefined);
@@ -14,7 +16,8 @@ const bottomScreenCanvas = computed(() => bottomScreen.value?.canvas ?? null);
</script>
<template>
<div>
<LoadingScreen v-if="!isReady" />
<div v-else>
<TresCanvas window-size clear-color="#181818">
<TresPerspectiveCamera
:args="[45, 1, 0.001, 1000]"

View File

@@ -1,8 +0,0 @@
<script setup lang="ts">
const { isReady } = useAssets();
</script>
<template>
<LoadingScreen v-if="!isReady" />
<p v-else>ok</p>
</template>