feat: screen manager
This commit is contained in:
45
src/nds.ts
45
src/nds.ts
@@ -1,5 +1,7 @@
|
|||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
||||||
|
import { ScreenManager } from "./screen-manager";
|
||||||
|
import { HomeScreen } from "./screens/home-screen";
|
||||||
|
|
||||||
const SCREEN_SOURCE_TEX_SIZE = 1024;
|
const SCREEN_SOURCE_TEX_SIZE = 1024;
|
||||||
const TOP_SCREEN_SOURCE_TEX_HEIGHT = SCREEN_SOURCE_TEX_SIZE / 404;
|
const TOP_SCREEN_SOURCE_TEX_HEIGHT = SCREEN_SOURCE_TEX_SIZE / 404;
|
||||||
@@ -21,19 +23,23 @@ const createScreenCanvas = () => {
|
|||||||
return canvas;
|
return canvas;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Screen = {
|
type PhysicalScreen = {
|
||||||
mesh: THREE.Mesh;
|
mesh: THREE.Mesh;
|
||||||
canvas: HTMLCanvasElement;
|
canvas: HTMLCanvasElement;
|
||||||
texture: THREE.CanvasTexture;
|
texture: THREE.CanvasTexture;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class NDS extends THREE.Object3D {
|
export class NDS extends THREE.Object3D {
|
||||||
private botScreen: Screen | null = null;
|
private botScreen: PhysicalScreen | null = null;
|
||||||
private topScreen: Screen | null = null;
|
private topScreen: PhysicalScreen | null = null;
|
||||||
|
private screenManager: ScreenManager;
|
||||||
|
|
||||||
public constructor(camera: THREE.Camera, domElement: HTMLCanvasElement) {
|
public constructor(camera: THREE.Camera, domElement: HTMLCanvasElement) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
// Initialize screen manager
|
||||||
|
this.screenManager = new ScreenManager(new HomeScreen());
|
||||||
|
|
||||||
const loader = new GLTFLoader();
|
const loader = new GLTFLoader();
|
||||||
// load model
|
// load model
|
||||||
loader.load("/nintendo-ds/scene.gltf", ({ scene: model }) => {
|
loader.load("/nintendo-ds/scene.gltf", ({ scene: model }) => {
|
||||||
@@ -101,10 +107,10 @@ export class NDS extends THREE.Object3D {
|
|||||||
emissiveIntensity: 0.5,
|
emissiveIntensity: 0.5,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.botScreen = { mesh: topScreenMesh, canvas, texture };
|
this.botScreen = { mesh: botScreenMesh, canvas, texture };
|
||||||
}
|
}
|
||||||
|
|
||||||
domElement.addEventListener("mousemove", (event) => {
|
domElement.addEventListener("click", (event) => {
|
||||||
const rect = domElement.getBoundingClientRect();
|
const rect = domElement.getBoundingClientRect();
|
||||||
|
|
||||||
const raycaster = new THREE.Raycaster();
|
const raycaster = new THREE.Raycaster();
|
||||||
@@ -116,27 +122,17 @@ export class NDS extends THREE.Object3D {
|
|||||||
camera,
|
camera,
|
||||||
);
|
);
|
||||||
|
|
||||||
const intersects = raycaster.intersectObjects([
|
const intersects = raycaster.intersectObject(botScreenMesh);
|
||||||
topScreenMesh,
|
|
||||||
botScreenMesh,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (intersects.length > 0) {
|
if (intersects.length > 0) {
|
||||||
const intersection = intersects[0];
|
const uv = intersects[0].uv;
|
||||||
const mesh = intersection.object as THREE.Mesh;
|
|
||||||
const uv = intersection.uv;
|
|
||||||
|
|
||||||
if (uv) {
|
if (uv) {
|
||||||
const x = Math.floor(uv.x * 256);
|
const x = Math.floor(uv.x * 256);
|
||||||
const y = Math.floor(
|
const y = Math.floor(
|
||||||
mesh === topScreenMesh
|
192 - (1 - uv.y) * BOT_SCREEN_SOURCE_TEX_HEIGHT * 192,
|
||||||
? uv.y * TOP_SCREEN_SOURCE_TEX_HEIGHT * 192
|
|
||||||
: // invert coords only for bottom screen
|
|
||||||
192 - (1 - uv.y) * BOT_SCREEN_SOURCE_TEX_HEIGHT * 192,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
x;
|
this.screenManager.handleTouch(x, y);
|
||||||
y;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -146,10 +142,17 @@ export class NDS extends THREE.Object3D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public update(): void {
|
public update(): void {
|
||||||
if (this.topScreen) {
|
if (!this.topScreen || !this.botScreen) return;
|
||||||
|
|
||||||
|
{
|
||||||
|
const ctx = this.topScreen.canvas.getContext("2d")!;
|
||||||
|
this.screenManager.renderTop(ctx);
|
||||||
this.topScreen.texture.needsUpdate = true;
|
this.topScreen.texture.needsUpdate = true;
|
||||||
}
|
}
|
||||||
if (this.botScreen) {
|
|
||||||
|
{
|
||||||
|
const ctx = this.botScreen.canvas.getContext("2d")!;
|
||||||
|
this.screenManager.renderBottom(ctx);
|
||||||
this.botScreen.texture.needsUpdate = true;
|
this.botScreen.texture.needsUpdate = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
35
src/screen-manager.ts
Normal file
35
src/screen-manager.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import type { Screen, ScreenContext } from "./screen";
|
||||||
|
|
||||||
|
export class ScreenManager {
|
||||||
|
private currentScreen: Screen;
|
||||||
|
private context: ScreenContext;
|
||||||
|
|
||||||
|
constructor(initialScreen: Screen) {
|
||||||
|
this.currentScreen = initialScreen;
|
||||||
|
this.context = {
|
||||||
|
navigate: (screen: Screen) => {
|
||||||
|
this.currentScreen = screen;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTop(ctx: CanvasRenderingContext2D) {
|
||||||
|
ctx.clearRect(0, 0, 256, 192);
|
||||||
|
ctx.save();
|
||||||
|
this.currentScreen.renderTop(ctx);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBottom(ctx: CanvasRenderingContext2D) {
|
||||||
|
ctx.clearRect(0, 0, 256, 192);
|
||||||
|
ctx.save();
|
||||||
|
this.currentScreen.renderBottom(ctx);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTouch(x: number, y: number) {
|
||||||
|
if (this.currentScreen.handleTouch) {
|
||||||
|
this.currentScreen.handleTouch(x, y, this.context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/screen.ts
Normal file
9
src/screen.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export interface ScreenContext {
|
||||||
|
navigate: (screen: Screen) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Screen {
|
||||||
|
renderTop(ctx: CanvasRenderingContext2D): void;
|
||||||
|
renderBottom(ctx: CanvasRenderingContext2D): void;
|
||||||
|
handleTouch?(x: number, y: number, context: ScreenContext): void;
|
||||||
|
}
|
||||||
25
src/screens/contact-screen.ts
Normal file
25
src/screens/contact-screen.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import type { Screen, ScreenContext } from "../screen";
|
||||||
|
import { HomeScreen } from "./home-screen";
|
||||||
|
|
||||||
|
export class ContactScreen implements Screen {
|
||||||
|
renderTop(ctx: CanvasRenderingContext2D) {
|
||||||
|
ctx.fillStyle = "#ffffff";
|
||||||
|
ctx.font = "10px NDS10";
|
||||||
|
ctx.textBaseline = "top";
|
||||||
|
ctx.fillText("CONTACT", 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBottom(ctx: CanvasRenderingContext2D) {
|
||||||
|
ctx.fillStyle = "#ffffff";
|
||||||
|
ctx.font = "10px NDS10";
|
||||||
|
ctx.textAlign = "left";
|
||||||
|
ctx.textBaseline = "bottom";
|
||||||
|
ctx.fillText("< Back", 1, 191);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTouch(x: number, y: number, context: ScreenContext): void {
|
||||||
|
if (x >= 0 && x <= 51 && y >= 178 && y <= 192) {
|
||||||
|
context.navigate(new HomeScreen());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/screens/home-screen.ts
Normal file
25
src/screens/home-screen.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import type { Screen, ScreenContext } from "../screen";
|
||||||
|
import { ContactScreen } from "./contact-screen";
|
||||||
|
|
||||||
|
export class HomeScreen implements Screen {
|
||||||
|
renderTop(ctx: CanvasRenderingContext2D) {
|
||||||
|
ctx.fillStyle = "#ffffff";
|
||||||
|
ctx.font = "10px NDS10";
|
||||||
|
ctx.textBaseline = "top";
|
||||||
|
ctx.fillText("HOME", 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBottom(ctx: CanvasRenderingContext2D) {
|
||||||
|
ctx.fillStyle = "#ffffff";
|
||||||
|
ctx.font = "10px NDS10";
|
||||||
|
ctx.textAlign = "right";
|
||||||
|
ctx.textBaseline = "bottom";
|
||||||
|
ctx.fillText("Contact >", 255, 191);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTouch(x: number, y: number, context: ScreenContext): void {
|
||||||
|
if (x >= 205 && x <= 256 && y >= 178 && y <= 192) {
|
||||||
|
context.navigate(new ContactScreen());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user