← back to Dw War Room
src/scene/Environment.ts
184 lines
// ═══════════════════════════════════════════════
// DW War Room 3D — Room Environment
// Architectural shell: floor grid, pillars, ceiling, walls
// ═══════════════════════════════════════════════
import * as THREE from 'three';
export function createEnvironment(scene: THREE.Scene): void {
addFloorWithGrid(scene);
addPillars(scene);
addCeiling(scene);
addWallSegments(scene);
}
// ─── Floor + Grid ───────────────────────────────
function addFloorWithGrid(scene: THREE.Scene): void {
// Base floor plane — industrial concrete/polished stone (not pure metallic)
const floorGeo = new THREE.PlaneGeometry(60, 60);
const floorMat = new THREE.MeshStandardMaterial({
color: 0x0b0f14,
metalness: 0.2,
roughness: 0.65,
});
const floor = new THREE.Mesh(floorGeo, floorMat);
floor.rotation.x = -Math.PI / 2;
floor.position.y = -0.01;
floor.receiveShadow = true;
scene.add(floor);
// Grid overlay — subtle cool-grey lines, not glowing neon
const grid = new THREE.GridHelper(60, 40, 0x1a2530, 0x0d1520);
grid.position.y = 0.01;
scene.add(grid);
}
// ─── Octagonal Pillar Ring ───────────────────────
function addPillars(scene: THREE.Scene): void {
const COUNT = 8;
const RADIUS = 22;
const PILLAR_HEIGHT = 10;
// Brushed aluminum pillar — high metalness, low roughness
const pillarMat = new THREE.MeshStandardMaterial({
color: 0x141c24,
emissive: new THREE.Color(0x0a1018),
emissiveIntensity: 0.1,
metalness: 0.8,
roughness: 0.2,
});
// Warm gold torus rings at base and top — corporate accent, not neon
const ringMat = new THREE.MeshStandardMaterial({
color: 0xb8860b,
emissive: new THREE.Color(0xc8900c),
emissiveIntensity: 0.6,
metalness: 0.7,
roughness: 0.3,
transparent: true,
opacity: 0.55,
});
for (let i = 0; i < COUNT; i++) {
const angle = (i / COUNT) * Math.PI * 2;
const x = Math.cos(angle) * RADIUS;
const z = Math.sin(angle) * RADIUS;
// Main pillar body — octagonal cross-section (8 radial segments)
const pillarGeo = new THREE.CylinderGeometry(0.3, 0.3, PILLAR_HEIGHT, 8);
const pillar = new THREE.Mesh(pillarGeo, pillarMat);
pillar.position.set(x, PILLAR_HEIGHT / 2, z);
scene.add(pillar);
// Base ring
const baseRingGeo = new THREE.TorusGeometry(0.55, 0.06, 8, 24);
const baseRing = new THREE.Mesh(baseRingGeo, ringMat);
baseRing.rotation.x = Math.PI / 2;
baseRing.position.set(x, 0.12, z);
scene.add(baseRing);
// Top ring
const topRingGeo = new THREE.TorusGeometry(0.55, 0.06, 8, 24);
const topRing = new THREE.Mesh(topRingGeo, ringMat);
topRing.rotation.x = Math.PI / 2;
topRing.position.set(x, PILLAR_HEIGHT - 0.12, z);
scene.add(topRing);
}
}
// ─── Ceiling + Recessed Light Slots ─────────────
function addCeiling(scene: THREE.Scene): void {
const CEILING_Y = 10;
// Ceiling plane — matte dark panel (not metallic — ceilings are typically matte)
const ceilGeo = new THREE.PlaneGeometry(50, 50);
const ceilMat = new THREE.MeshStandardMaterial({
color: 0x090d12,
metalness: 0.0,
roughness: 0.85,
transparent: true,
opacity: 0.45,
side: THREE.BackSide, // render from below
});
const ceiling = new THREE.Mesh(ceilGeo, ceilMat);
ceiling.rotation.x = Math.PI / 2;
ceiling.position.y = CEILING_Y;
scene.add(ceiling);
// 4 recessed light slots — warm white architectural lighting, not neon blue
const slotMat = new THREE.MeshStandardMaterial({
color: 0xe8e4d8,
emissive: new THREE.Color(0xd4c090),
emissiveIntensity: 0.7,
metalness: 0.1,
roughness: 0.5,
transparent: true,
opacity: 0.75,
});
const slotPositions: [number, number][] = [
[-8, -8],
[8, -8],
[-8, 8],
[8, 8],
];
slotPositions.forEach(([sx, sz]) => {
const slotGeo = new THREE.BoxGeometry(6, 0.05, 0.4);
const slot = new THREE.Mesh(slotGeo, slotMat);
slot.position.set(sx, CEILING_Y - 0.03, sz);
scene.add(slot);
});
}
// ─── Wall Segments ───────────────────────────────
function addWallSegments(scene: THREE.Scene): void {
const WALL_HEIGHT = 10;
const WALL_LENGTH = 50;
const OFFSET = 25;
// Matte architectural walls — zero metalness, high roughness (concrete/gypsum)
const wallMat = new THREE.MeshStandardMaterial({
color: 0x0a0e14,
emissive: new THREE.Color(0x080c10),
emissiveIntensity: 0.05,
metalness: 0.0,
roughness: 0.75,
transparent: true,
opacity: 0.6,
side: THREE.DoubleSide,
});
// Wall configs: [x, z, rotationY]
const walls: [number, number, number][] = [
[0, -OFFSET, 0], // back (+Z face)
[0, OFFSET, Math.PI], // front (-Z face)
[-OFFSET, 0, Math.PI / 2], // left
[ OFFSET, 0, -Math.PI / 2], // right
];
walls.forEach(([wx, wz, ry]) => {
const wallGeo = new THREE.PlaneGeometry(WALL_LENGTH, WALL_HEIGHT);
const wall = new THREE.Mesh(wallGeo, wallMat);
wall.position.set(wx, WALL_HEIGHT / 2, wz);
wall.rotation.y = ry;
scene.add(wall);
// Warm gold edge trim — architectural base accent, not neon purple
const trimMat = new THREE.MeshStandardMaterial({
color: 0xa07828,
emissive: new THREE.Color(0xb08030),
emissiveIntensity: 0.4,
metalness: 0.6,
roughness: 0.4,
transparent: true,
opacity: 0.45,
});
const trimGeo = new THREE.BoxGeometry(WALL_LENGTH, 0.06, 0.08);
const trim = new THREE.Mesh(trimGeo, trimMat);
trim.position.set(wx, 0.06, wz);
trim.rotation.y = ry;
scene.add(trim);
});
}