diff --git a/party-cathedral/src/scene/stained-glass-window.js b/party-cathedral/src/scene/stained-glass-window.js index a48630b..84b1857 100644 --- a/party-cathedral/src/scene/stained-glass-window.js +++ b/party-cathedral/src/scene/stained-glass-window.js @@ -132,6 +132,62 @@ export class StainedGlass extends SceneFeature { return geometry; }; + const createProceduralRoseWindowGeometry = (mainColor, radius) => { + const segments = 32; // Number of radial segments + const rings = 5; + const vertices = []; + const colors = []; + const normals = []; + + const randomnessFactor = 0.4; + + const addTriangle = (v1, v2, v3, isBorder = false) => { + let segmentColor; + const rand = Math.random(); + + if (rand < 0.05) { // 5% chance for a "lead line" + segmentColor = new THREE.Color(0x333333); + } else if (isBorder && rand < 0.6) { + segmentColor = mainColor.clone().offsetHSL(0, 0, -0.1); + } else if (isBorder) { + segmentColor = colorPalette[Math.floor(Math.random() * colorPalette.length)].clone().offsetHSL(0, 0, -0.1); + } else { + if (rand < 0.85) { + segmentColor = mainColor.clone(); + segmentColor.offsetHSL((Math.random() - 0.5) * 0.1, (Math.random() - 0.5) * 0.3, (Math.random() - 0.5) * 0.2); + } else { + segmentColor = colorPalette[Math.floor(Math.random() * colorPalette.length)]; + } + } + vertices.push(v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, v3.x, v3.y, v3.z); + colors.push(segmentColor.r, segmentColor.g, segmentColor.b, segmentColor.r, segmentColor.g, segmentColor.b, segmentColor.r, segmentColor.g, segmentColor.b); + const edge1 = new THREE.Vector3().subVectors(v2, v1); + const edge2 = new THREE.Vector3().subVectors(v3, v1); + const faceNormal = new THREE.Vector3().crossVectors(edge1, edge2).normalize(); + const randomVec = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize(); + faceNormal.add(randomVec.multiplyScalar(randomnessFactor)).normalize(); + normals.push(faceNormal.x, faceNormal.y, faceNormal.z, faceNormal.x, faceNormal.y, faceNormal.z, faceNormal.x, faceNormal.y, faceNormal.z); + }; + + for (let r = 0; r < rings; r++) { + for (let s = 0; s < segments; s++) { + const angle1 = (s / segments) * Math.PI * 2; + const angle2 = ((s + 1) / segments) * Math.PI * 2; + const v1 = new THREE.Vector3(Math.cos(angle1) * (r * radius / rings), Math.sin(angle1) * (r * radius / rings), 0); + const v2 = new THREE.Vector3(Math.cos(angle2) * (r * radius / rings), Math.sin(angle2) * (r * radius / rings), 0); + const v3 = new THREE.Vector3(Math.cos(angle1) * ((r + 1) * radius / rings), Math.sin(angle1) * ((r + 1) * radius / rings), 0); + const v4 = new THREE.Vector3(Math.cos(angle2) * ((r + 1) * radius / rings), Math.sin(angle2) * ((r + 1) * radius / rings), 0); + addTriangle(v1, v2, v3, r === rings - 1); + addTriangle(v2, v4, v3, r === rings - 1); + } + } + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); + geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); + geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3)); + return geometry; + }; + // --- Create and Place Windows --- const createAndPlaceWindow = (position, rotationY) => { // Pick a main color for this entire window @@ -164,6 +220,16 @@ export class StainedGlass extends SceneFeature { // Right side of Nave createAndPlaceWindow(new THREE.Vector3(naveWidth / 2 - 0.01, y, z), -Math.PI / 2); } + + // --- Add Huge Rose Window behind the stage --- + const roseWindowRadius = naveWidth / 2 - 1; // Almost as wide as the nave + const roseWindowMainColor = colorPalette[Math.floor(Math.random() * colorPalette.length)]; + const roseWindowGeo = createProceduralRoseWindowGeometry(roseWindowMainColor, roseWindowRadius); + const roseWindowMesh = new THREE.Mesh(roseWindowGeo, material); + roseWindowMesh.position.set(0, naveHeight - 2, -length / 2 + 0.05); + state.scene.add(roseWindowMesh); + this.windows.push(roseWindowMesh); + } update(deltaTime) {