From 8d45b76944707162aacafa05a076223ca96220f8 Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Sat, 13 Dec 2025 17:00:42 +0100 Subject: [PATCH] feat: compute mouse position for each screen --- index.html | 1 + src/main.ts | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/style.css | 13 +++++++++++ 3 files changed, 75 insertions(+) diff --git a/index.html b/index.html index 9357f1c..cd65f8a 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,7 @@
+
diff --git a/src/main.ts b/src/main.ts index 3f4c8da..9555a12 100644 --- a/src/main.ts +++ b/src/main.ts @@ -33,6 +33,23 @@ const nds = await loader.loadAsync("/nintendo-ds/scene.gltf"); nds.scene.scale.set(50, 50, 50); scene.add(nds.scene); +let topScreenMesh: THREE.Mesh | undefined = undefined; +let bottomScreenMesh: THREE.Mesh | undefined = undefined; + +nds.scene.traverse((child) => { + if (child instanceof THREE.Mesh) { + const material = child.material as THREE.Material; + if (material.name?.includes("screen_up")) { + topScreenMesh = child; + } else if (material.name?.includes("screen_down")) { + bottomScreenMesh = child; + } + } +}); + +if (!topScreenMesh) throw new Error(`Top screen mesh not found`); +if (!bottomScreenMesh) throw new Error(`Bottom screen mesh not found`); + // create light const color = 0xffffff; const intensity = 3; @@ -44,6 +61,50 @@ directional.target = nds.scene; directional.position.set(0, 100, 0); scene.add(directional); +const coordDisplay = document.getElementById("coord-display")!; + +renderer.domElement.addEventListener("mousemove", (event) => { + const rect = renderer.domElement.getBoundingClientRect(); + + const raycaster = new THREE.Raycaster(); + raycaster.setFromCamera( + new THREE.Vector2( + ((event.clientX - rect.left) / rect.width) * 2 - 1, + -((event.clientY - rect.top) / rect.height) * 2 + 1, + ), + camera, + ); + + const intersects = raycaster.intersectObjects([ + topScreenMesh!, + bottomScreenMesh!, + ]); + + if (intersects.length > 0) { + const intersection = intersects[0]; + const mesh = intersection.object as THREE.Mesh; + const uv = intersection.uv; + + if (uv) { + coordDisplay.hidden = false; + + const x = Math.floor(uv.x * 256); + const y = + mesh === topScreenMesh + ? Math.floor(uv.y * (1024 / 404) * 192) + : // invert coords only for bottom screen + Math.floor(192 - (1 - uv.y) * (1024 / 532) * 192); + + const screenName = mesh === topScreenMesh ? "top" : "bottom"; + coordDisplay.textContent = `${screenName} | pos: (${x}, ${y})`; + } else { + coordDisplay.hidden = true; + } + } else { + coordDisplay.hidden = true; + } +}); + // main loop renderer.setAnimationLoop(() => { controls.update(); diff --git a/src/style.css b/src/style.css index 293d3b1..5d88312 100644 --- a/src/style.css +++ b/src/style.css @@ -1,3 +1,16 @@ body { margin: 0; } + +#coord-display { + position: fixed; + top: 5px; + left: 5px; + color: white; + font-family: monospace; + font-size: 16px; + background-color: #000000; + padding: 10px; + pointer-events: none; + z-index: 1000; +}