feat: 3d nds model
This commit is contained in:
85
app/components/NDS.vue
Normal file
85
app/components/NDS.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<script setup lang="ts">
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
||||
import { useLoop } from "@tresjs/core";
|
||||
import * as THREE from "three";
|
||||
|
||||
const props = defineProps<{
|
||||
topScreenCanvas: HTMLCanvasElement | null;
|
||||
bottomScreenCanvas: HTMLCanvasElement | null;
|
||||
}>();
|
||||
|
||||
const { state: model } = useLoader(
|
||||
GLTFLoader,
|
||||
"/models/nintendo-ds/scene.gltf",
|
||||
);
|
||||
|
||||
const scene = computed(() => model.value?.scene);
|
||||
|
||||
let topScreenTexture: THREE.CanvasTexture | null = null;
|
||||
let bottomScreenTexture: THREE.CanvasTexture | null = null;
|
||||
|
||||
watch(
|
||||
() => [props.topScreenCanvas, props.bottomScreenCanvas],
|
||||
() => {
|
||||
if (!props.topScreenCanvas || !props.bottomScreenCanvas) return;
|
||||
|
||||
topScreenTexture = new THREE.CanvasTexture(props.topScreenCanvas);
|
||||
topScreenTexture.minFilter = THREE.NearestFilter;
|
||||
topScreenTexture.magFilter = THREE.NearestFilter;
|
||||
topScreenTexture.flipY = false;
|
||||
|
||||
bottomScreenTexture = new THREE.CanvasTexture(props.bottomScreenCanvas);
|
||||
bottomScreenTexture.minFilter = THREE.NearestFilter;
|
||||
bottomScreenTexture.magFilter = THREE.NearestFilter;
|
||||
bottomScreenTexture.flipY = false;
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(scene, () => {
|
||||
if (!scene.value) return;
|
||||
|
||||
scene.value.traverse((child) => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
const material = child.material as THREE.Material;
|
||||
|
||||
if (material.name?.includes("screen_up") && topScreenTexture) {
|
||||
topScreenTexture.repeat.set(1, 1024 / 404);
|
||||
topScreenTexture.offset.set(0, -4 / 1024);
|
||||
child.material = new THREE.MeshStandardMaterial({
|
||||
map: topScreenTexture,
|
||||
emissive: new THREE.Color(0x222222),
|
||||
emissiveIntensity: 0.5,
|
||||
});
|
||||
} else if (
|
||||
material.name?.includes("screen_down") &&
|
||||
bottomScreenTexture
|
||||
) {
|
||||
bottomScreenTexture.repeat.set(1, 1024 / 532);
|
||||
bottomScreenTexture.offset.set(0, -1024 / 532 + 1);
|
||||
child.material = new THREE.MeshStandardMaterial({
|
||||
map: bottomScreenTexture,
|
||||
emissive: new THREE.Color(0x222222),
|
||||
emissiveIntensity: 0.5,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const { onRender } = useLoop();
|
||||
|
||||
onRender(() => {
|
||||
if (topScreenTexture) topScreenTexture.needsUpdate = true;
|
||||
if (bottomScreenTexture) bottomScreenTexture.needsUpdate = true;
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
topScreenTexture?.dispose();
|
||||
bottomScreenTexture?.dispose();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<primitive v-if="scene" :object="scene" />
|
||||
</template>
|
||||
@@ -110,6 +110,10 @@ onUnmounted(() => {
|
||||
canvas.value.removeEventListener("wheel", handleCanvasWheel);
|
||||
}
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
canvas,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
const showStats = useState("showStats", () => false);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:style="{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '4px',
|
||||
}"
|
||||
>
|
||||
<div :style="{ display: 'flex', alignItems: 'center', gap: '4px' }">
|
||||
<input id="statsCheckbox" v-model="showStats" type="checkbox" />
|
||||
<label for="statsCheckbox">Stats</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Screen>
|
||||
<slot name="top">
|
||||
<slot />
|
||||
</slot>
|
||||
<Stats v-if="showStats" />
|
||||
</Screen>
|
||||
</div>
|
||||
<div>
|
||||
<Screen>
|
||||
<slot name="bottom">
|
||||
<slot />
|
||||
</slot>
|
||||
<Stats v-if="showStats" />
|
||||
</Screen>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,26 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: false,
|
||||
});
|
||||
import { OrbitControls } from "@tresjs/cientos";
|
||||
|
||||
const route = useRoute();
|
||||
const screen = computed(() => route.query.screen as string | undefined);
|
||||
|
||||
const topScreen = useTemplateRef("topScreen");
|
||||
const bottomScreen = useTemplateRef("bottomScreen");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLayout name="default">
|
||||
<template #top>
|
||||
<div>
|
||||
<TresCanvas window-size clear-color="#181818">
|
||||
<TresPerspectiveCamera :args="[45, 1, 0.001, 1000]" />
|
||||
<OrbitControls />
|
||||
|
||||
<TresAmbientLight />
|
||||
<TresDirectionalLight />
|
||||
|
||||
<NDS
|
||||
v-if="topScreen && bottomScreen"
|
||||
:top-screen-canvas="topScreen.canvas"
|
||||
:bottom-screen-canvas="bottomScreen.canvas"
|
||||
/>
|
||||
</TresCanvas>
|
||||
|
||||
<Screen ref="topScreen">
|
||||
<HomeTopScreen v-if="!screen" />
|
||||
<ContactTopScreen v-else-if="screen === 'contact'" />
|
||||
<ProjectsTopScreen v-else-if="screen === 'projects'" />
|
||||
<SettingsTopScreen v-else-if="screen === 'settings'" />
|
||||
</template>
|
||||
|
||||
<template #bottom>
|
||||
</Screen>
|
||||
<Screen ref="bottomScreen">
|
||||
<HomeBottomScreen v-if="!screen" />
|
||||
<ContactBottomScreen v-else-if="screen === 'contact'" />
|
||||
<ProjectsBottomScreen v-else-if="screen === 'projects'" />
|
||||
<SettingsBottomScreen v-else-if="screen === 'settings'" />
|
||||
</template>
|
||||
</NuxtLayout>
|
||||
</Screen>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user