Files
timmy-tower/the-matrix/js/world.js

154 lines
4.9 KiB
JavaScript

import * as THREE from 'three';
export let scene, camera, renderer;
const _worldObjects = [];
export function initWorld(existingCanvas) {
_worldObjects.length = 0;
scene = new THREE.Scene();
scene.background = new THREE.Color(0x080610);
scene.fog = new THREE.FogExp2(0x080610, 0.038);
camera = new THREE.PerspectiveCamera(58, window.innerWidth / window.innerHeight, 0.1, 200);
camera.position.set(0, 7, 14);
camera.lookAt(0, 2, -2);
renderer = new THREE.WebGLRenderer({
antialias: true,
canvas: existingCanvas || undefined,
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
if (!existingCanvas) {
document.body.prepend(renderer.domElement);
}
addLights(scene);
buildRoom(scene);
return { scene, camera, renderer };
}
function addLights(scene) {
const ambient = new THREE.AmbientLight(0x0a2010, 0.45);
scene.add(ambient);
const fireplace = new THREE.PointLight(0xff7722, 4.5, 32);
fireplace.position.set(-9, 3.5, 6);
fireplace.castShadow = true;
scene.add(fireplace);
const candle = new THREE.PointLight(0xffcc77, 1.8, 8);
candle.position.set(0.7, 1.9, -3.6);
scene.add(candle);
const fill = new THREE.DirectionalLight(0x001a00, 0.2);
fill.position.set(0, 12, 8);
scene.add(fill);
}
function buildRoom(scene) {
const stoneMat = new THREE.MeshStandardMaterial({ color: 0x181820, roughness: 0.95, metalness: 0.0 });
const woodMat = new THREE.MeshStandardMaterial({ color: 0x3d2506, roughness: 0.92 });
const floor = new THREE.Mesh(new THREE.PlaneGeometry(28, 28), stoneMat.clone());
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
_worldObjects.push(floor);
const grid = new THREE.GridHelper(28, 14, 0x28283a, 0x1c1c28);
grid.position.y = 0.005;
scene.add(grid);
_worldObjects.push(grid);
buildDesk(scene, woodMat);
buildShelves(scene, woodMat);
buildFireplaceGlow(scene);
}
function buildDesk(scene, woodMat) {
const top = new THREE.Mesh(new THREE.BoxGeometry(2.8, 0.1, 1.3), woodMat.clone());
top.position.set(0, 1.05, -4.1);
top.castShadow = true;
top.receiveShadow = true;
scene.add(top);
_worldObjects.push(top);
const legGeo = new THREE.CylinderGeometry(0.055, 0.055, 1.05, 8);
[[-1.25, -3.55], [1.25, -3.55], [-1.25, -4.65], [1.25, -4.65]].forEach(([x, z]) => {
const leg = new THREE.Mesh(legGeo, woodMat.clone());
leg.position.set(x, 0.525, z);
leg.castShadow = true;
scene.add(leg);
_worldObjects.push(leg);
});
const scrollMat = new THREE.MeshStandardMaterial({ color: 0xe8d5a0, roughness: 1.0 });
const scroll = new THREE.Mesh(new THREE.BoxGeometry(0.9, 0.02, 0.65), scrollMat);
scroll.position.set(-0.65, 1.12, -4.1);
scroll.rotation.y = 0.18;
scene.add(scroll);
_worldObjects.push(scroll);
const scroll2 = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.015, 0.4), scrollMat.clone());
scroll2.position.set(0.3, 1.12, -4.3);
scroll2.rotation.y = -0.25;
scene.add(scroll2);
_worldObjects.push(scroll2);
}
function buildShelves(scene, woodMat) {
const bookColors = [0x8b0000, 0x00008b, 0x004400, 0x6b3300, 0x440066, 0x884400, 0x005566, 0x663300];
[2.6, 3.7, 4.8].forEach(y => {
const shelf = new THREE.Mesh(new THREE.BoxGeometry(3.8, 0.07, 0.48), woodMat.clone());
shelf.position.set(-8.5, y, -2.2);
shelf.rotation.y = 0.12;
shelf.castShadow = true;
scene.add(shelf);
_worldObjects.push(shelf);
for (let i = 0; i < 7; i++) {
const h = 0.38 + Math.random() * 0.22;
const bookMat = new THREE.MeshStandardMaterial({ color: bookColors[i % bookColors.length], roughness: 0.95 });
const book = new THREE.Mesh(new THREE.BoxGeometry(0.11, h, 0.34), bookMat);
book.position.set(-10.0 + i * 0.43, y + h / 2 + 0.04, -2.2 + Math.random() * 0.04);
book.rotation.y = 0.12 + (Math.random() - 0.5) * 0.08;
scene.add(book);
_worldObjects.push(book);
}
});
}
function buildFireplaceGlow(scene) {
const glowMat = new THREE.MeshBasicMaterial({ color: 0xff5500, transparent: true, opacity: 0.1 });
const glow = new THREE.Mesh(new THREE.PlaneGeometry(3.5, 3), glowMat);
glow.position.set(-11.5, 2.5, 4);
glow.rotation.y = Math.PI / 2;
scene.add(glow);
_worldObjects.push(glow);
}
export function disposeWorld(renderer, _scene) {
for (const obj of _worldObjects) {
if (obj.geometry) obj.geometry.dispose();
if (obj.material) {
const mats = Array.isArray(obj.material) ? obj.material : [obj.material];
mats.forEach(m => { if (m.map) m.map.dispose(); m.dispose(); });
}
}
_worldObjects.length = 0;
renderer.dispose();
}
export function onWindowResize(camera, renderer) {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}