import * as THREE from 'three'; import { state } from '../state.js'; import wallTextureUrl from '/textures/stone_wall.png'; import sceneFeatureManager from './SceneFeatureManager.js'; import { SceneFeature } from './SceneFeature.js'; const length = 40; export class RoomWalls extends SceneFeature { constructor() { super(); sceneFeatureManager.register(this); } init() { const naveWidth = 12; const aisleWidth = 6; const totalWidth = naveWidth + 2 * aisleWidth; const aisleHeight = 8; const naveHeight = 15; const roofPeakHeight = 6; // Additional height for the nave's vaulted roof peak // --- Pillar and Arch Dimensions --- const pillarSize = 1.0; const pillarHeight = aisleHeight; const numPillars = 5; // Number of pillars along each side const pillarSpacing = length / (numPillars + 1); // --- Materials and Textures --- const wallTexture = state.loader.load(wallTextureUrl); wallTexture.wrapS = THREE.RepeatWrapping; wallTexture.wrapT = THREE.RepeatWrapping; const wallMaterial = new THREE.MeshStandardMaterial({ map: wallTexture, side: THREE.DoubleSide, shininess: 5, roughness: 0.2, metalness: 0.1, specular: 0x111111 }); // --- Geometry Definitions --- const pillarGeo = new THREE.CylinderGeometry(pillarSize / 2, pillarSize / 2, pillarHeight, 24); // --- Object Creation Functions --- const createMesh = (geometry, material, position, rotation = new THREE.Euler()) => { const mesh = new THREE.Mesh(geometry, material); mesh.position.copy(position); mesh.rotation.copy(rotation); mesh.castShadow = true; mesh.receiveShadow = true; state.scene.add(mesh); return mesh; }; // --- Build the Cathedral --- // 1. Back Wall const backWallGeo = new THREE.PlaneGeometry(totalWidth, aisleHeight); const backWallMat = wallMaterial.clone(); backWallMat.map = wallTexture.clone(); backWallMat.map.repeat.set(totalWidth / 4, aisleHeight / 4); createMesh(backWallGeo, backWallMat, new THREE.Vector3(0, aisleHeight / 2, -length / 2)); // 2. Outer Aisle Walls const outerWallGeo = new THREE.PlaneGeometry(length, aisleHeight); const outerWallMat = wallMaterial.clone(); outerWallMat.map = wallTexture.clone(); outerWallMat.map.repeat.set(length / 4, aisleHeight / 4); createMesh(outerWallGeo, outerWallMat, new THREE.Vector3(-totalWidth / 2, aisleHeight / 2, 0), new THREE.Euler(0, Math.PI / 2, 0)); createMesh(outerWallGeo, outerWallMat, new THREE.Vector3(totalWidth / 2, aisleHeight / 2, 0), new THREE.Euler(0, -Math.PI / 2, 0)); // 3. Aisle Roofs (Flat) const aisleRoofGeo = new THREE.PlaneGeometry(aisleWidth, length); const aisleRoofMat = wallMaterial.clone(); aisleRoofMat.map = wallTexture.clone(); aisleRoofMat.map.repeat.set(aisleWidth / 4, length / 4); createMesh(aisleRoofGeo, aisleRoofMat, new THREE.Vector3(-naveWidth / 2 - aisleWidth / 2, aisleHeight, 0), new THREE.Euler(-Math.PI / 2, 0, 0)); createMesh(aisleRoofGeo, aisleRoofMat, new THREE.Vector3(naveWidth / 2 + aisleWidth / 2, aisleHeight, 0), new THREE.Euler(-Math.PI / 2, 0, 0)); // 4. Pillars and Arcades const arcadeWallHeight = aisleHeight - pillarHeight; const arcadeWallGeo = new THREE.PlaneGeometry(pillarSpacing - pillarSize, arcadeWallHeight); const arcadeWallMat = wallMaterial.clone(); arcadeWallMat.map = wallTexture.clone(); arcadeWallMat.map.repeat.set((pillarSpacing - pillarSize) / 4, arcadeWallHeight / 4); for (let i = 0; i <= numPillars; i++) { const z = -length / 2 + pillarSpacing * (i + 0.5); // Add wall sections between pillars if (i <= numPillars) { createMesh(arcadeWallGeo, arcadeWallMat, new THREE.Vector3(-naveWidth / 2 , pillarHeight + arcadeWallHeight / 2, z)); createMesh(arcadeWallGeo, arcadeWallMat, new THREE.Vector3(naveWidth / 2, pillarHeight + arcadeWallHeight / 2, z)); } const pillarZ = -length / 2 + pillarSpacing * (i + 1) - pillarSize / 2; // Left side pillars createMesh(pillarGeo, wallMaterial, new THREE.Vector3(-naveWidth / 2 - pillarSize, pillarHeight / 2, pillarZ)); // Right side pillars createMesh(pillarGeo, wallMaterial, new THREE.Vector3(naveWidth / 2 + pillarSize, pillarHeight / 2, pillarZ)); } // 5. Clerestory (Upper Nave Walls) const clerestoryHeight = naveHeight - aisleHeight; const clerestoryGeo = new THREE.PlaneGeometry(length, clerestoryHeight); const clerestoryMat = wallMaterial.clone(); clerestoryMat.map = wallTexture.clone(); clerestoryMat.map.repeat.set(length / 4, clerestoryHeight / 4); // Left and Right Clerestory walls createMesh(clerestoryGeo, clerestoryMat, new THREE.Vector3(-naveWidth / 2, aisleHeight + clerestoryHeight / 2, 0), new THREE.Euler(0, - Math.PI / 2, 0)); createMesh(clerestoryGeo, clerestoryMat, new THREE.Vector3(naveWidth / 2, aisleHeight + clerestoryHeight / 2, 0), new THREE.Euler(0, Math.PI/2, 0)); // Upper part of the back wall (for the nave) const backClerestoryGeo = new THREE.PlaneGeometry(naveWidth, clerestoryHeight); const backClerestoryMat = wallMaterial.clone(); backClerestoryMat.map = wallTexture.clone(); backClerestoryMat.map.repeat.set(naveWidth / 4, clerestoryHeight / 4); createMesh(backClerestoryGeo, backClerestoryMat, new THREE.Vector3(0, aisleHeight + clerestoryHeight / 2, -length / 2)); // 6. Nave's Vaulted Roof const roofPanelWidth = Math.sqrt(Math.pow(naveWidth / 2, 2) + Math.pow(roofPeakHeight, 2)); const roofAngle = Math.atan2(roofPeakHeight, naveWidth / 2); const roofGeo = new THREE.PlaneGeometry(roofPanelWidth, length); // Swapped width and length const roofMat = wallMaterial.clone(); roofMat.map = wallTexture.clone(); roofMat.map.repeat.set(roofPanelWidth / 4, length / 4); // Left and Right roof panels createMesh(roofGeo, roofMat, new THREE.Vector3(-naveWidth / 4, naveHeight + roofPeakHeight / 2, 0), new THREE.Euler(Math.PI / 2, roofAngle, 0) // Flipped the roof right side up ); createMesh(roofGeo, roofMat, new THREE.Vector3(naveWidth / 4, naveHeight + roofPeakHeight / 2, 0), new THREE.Euler(Math.PI / 2, -roofAngle, 0) // Flipped the roof right side up ); // 7. Back gable wall (triangle part) const gableShape = new THREE.Shape(); gableShape.moveTo(-naveWidth / 2, naveHeight); gableShape.lineTo(naveWidth / 2, naveHeight); gableShape.lineTo(0, naveHeight + roofPeakHeight); const gableGeo = new THREE.ShapeGeometry(gableShape); const gableMat = wallMaterial.clone(); gableMat.map = wallTexture.clone(); gableMat.map.repeat.set(naveWidth / 8, roofPeakHeight / 8); createMesh(gableGeo, gableMat, new THREE.Vector3(0, 0, -length / 2)); // Note: crawlSurfaces and landingSurfaces might need to be updated if spiders/rats are used. } update(deltaTime) { // Add any per-frame update logic here, if needed } } new RoomWalls();