154 lines
4.9 KiB
JavaScript
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);
|
|
}
|