import { useMemo } from 'react'; import * as THREE from 'three'; import type { GalleryWindowSpec, GalleryWindowStyle } from '../data/movement-interior-styles'; const WALL_HEIGHT = 4.2; interface Props { windows: GalleryWindowSpec[]; halfW: number; halfD: number; trimColor: string; } function windowWorldPosition( spec: GalleryWindowSpec, halfW: number, halfD: number ): { position: [number, number, number]; rotation: [number, number, number] } { const inset = 0.1; switch (spec.wall) { case 'back': return { position: [spec.x, spec.y, -halfD + inset], rotation: [0, 0, 0], }; case 'left': return { position: [-halfW + inset, spec.y, spec.x], rotation: [0, Math.PI / 2, 0], }; case 'right': return { position: [halfW - inset, spec.y, spec.x], rotation: [0, -Math.PI / 2, 0], }; case 'ceiling': return { position: [spec.x, WALL_HEIGHT - 0.06, spec.x === 0 ? 0 : spec.x], rotation: [Math.PI / 2, 0, 0], }; default: return { position: [0, spec.y, -halfD + inset], rotation: [0, 0, 0] }; } } function WindowFrame({ style, width, height, trimColor, }: { style: GalleryWindowStyle; width: number; height: number; trimColor: string; }) { const frameW = 0.08; const depth = 0.12; const arch = style === 'roman-arch' || style === 'gothic-lancet' || style === 'art-nouveau'; return ( {/* Outer frame */} {arch && style === 'gothic-lancet' && ( )} {style === 'roman-arch' && ( )} {style === 'factory' && ( <> {[-width / 3, 0, width / 3].map((ox) => ( ))} {[height / 4, -height / 4].map((oy) => ( ))} )} {style === 'glass-block' && Array.from({ length: 4 }, (_, row) => Array.from({ length: 3 }, (_, col) => ( )) )} {style === 'sash' && ( )} {style === 'baroque-pair' && ( )} ); } function SingleWindow({ spec, trimColor, }: { spec: GalleryWindowSpec; trimColor: string; }) { const glassColor = useMemo(() => new THREE.Color(spec.lightColor), [spec.lightColor]); return ( {/* Sky / daylight pane */} {/* Soft sky gradient overlay */} {/* One fill light per window — keep total scene lights well under WebGL limits. */} ); } export default function GalleryWindows({ windows, halfW, halfD, trimColor }: Props) { return ( {windows.map((spec, i) => { const { position, rotation } = windowWorldPosition(spec, halfW, halfD); return ( ); })} ); } /** Ceiling-mounted gallery track lights for even illumination. */ export function GalleryTrackLights({ width, depth, intensity, color, }: { width: number; depth: number; intensity: number; color: string; }) { const positions = useMemo(() => { const pts: [number, number, number][] = []; // Cap fixtures so MeshStandard hall materials stay within WebGL light limits. const cols = Math.max(2, Math.min(3, Math.floor(width / 5))); const rows = Math.max(1, Math.min(2, Math.floor(depth / 8))); for (let c = 0; c < cols; c++) { for (let r = 0; r < rows; r++) { const x = -width / 2 + (width / (cols + 1)) * (c + 1); const z = -depth / 2 + (depth / (rows + 1)) * (r + 1); pts.push([x, WALL_HEIGHT - 0.15, z]); } } return pts; }, [width, depth]); return ( {positions.map(([x, y, z], i) => ( ))} ); }