Give every movement its own period interior.

Movement halls routed 24 of the 26 movements through 8 generic detail kinds,
four of which (modern, industrial, atelier, museum) rendered nothing at all,
and `salon` was a single ceiling ring shared by eight movements.

`details` is now a 26-value MovementDetailKind, one per movement, each built
from the architecture of the same period as the movement itself. Gothic and
Byzantine are unchanged and stay in MovementHallDetails.tsx as the reference
implementations; the rest live under components/hall-details/, split by era,
over shared wall-flush primitives.

hall-details/geometry.ts records the budget the Gothic and Byzantine
components established, since frames hang 0.23 m proud with a 0.7 m clear
margin at each wall end: relief limits above and below the frame line, corner
pockets for freestanding masses, doorway and title-band clearance, and a cap
of two added lights per hall. Halls now pass their computed windows down so
upper-wall panels, arches and roundels step around the openings.

Also holds the pale interiors back from blowing out: gallery wall tints are
authored around 0.50-0.56 luminance instead of near-white, and the 19 halls
that read too bright take lightScale 0.30, matching the basilica.

Verified by rendering all 26 interiors offline at two hall sizes and
measuring the scene graph: nothing through walls, floor or ceiling, maximum
0.17 m intrusion into the hang zone, at most two added lights per hall.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Danila Khodjaef
2026-08-14 18:44:05 +03:00
co-authored by Claude Opus 5
parent 8e52415ea8
commit f2a256132c
11 changed files with 2828 additions and 233 deletions
+24 -3
View File
@@ -15,7 +15,12 @@ import type {
import { galleryImageUrlCandidates, imageUrl, api } from '../api/client';
import { comparePaintingsChronological, paintingHasCuratorNotes, paintingHasInfluenceLinks, paintingWallCaption } from '../utils/paintingUtils';
import { cloneSurfaceTexture, getSurfaceTexture, type SurfaceTextureKind } from '../utils/galleryProceduralTextures';
import { resolveMovementInteriorStyle, type MovementInteriorStyle, type GalleryWindowSpec } from '../data/movement-interior-styles';
import {
resolveMovementInteriorStyle,
type MovementDetailKind,
type MovementInteriorStyle,
type GalleryWindowSpec,
} from '../data/movement-interior-styles';
import { useTexturedMaterial } from '../hooks/useTexturedMaterial';
import MovementHallDetails from './MovementHallDetails';
import GalleryWindows, { GalleryTrackLights } from './GalleryWindows';
@@ -84,6 +89,15 @@ interface TourGalleryProps extends BaseGalleryProps {
type Props = ArtistGalleryProps | MovementGalleryProps | TourGalleryProps;
/** Period interiors built from steel, concrete or a white cube read better under the city HDR. */
const CITY_ENVIRONMENT_DETAILS = new Set<MovementDetailKind>([
'futurist',
'suprematist',
'constructivist',
'loft',
'white-cube',
]);
const WALL_HEIGHT = 4.2;
const WALL_THICKNESS = 0.18;
const MOUNT_OFFSET = 0.16;
@@ -1739,7 +1753,14 @@ function ArtistHall({
</mesh>
{interiorStyle && (
<MovementHallDetails style={interiorStyle} width={width} depth={depth} halfW={halfW} halfD={halfD} />
<MovementHallDetails
style={interiorStyle}
width={width}
depth={depth}
halfW={halfW}
halfD={halfD}
windows={computedWindows}
/>
)}
<Text
@@ -2635,7 +2656,7 @@ export default function VirtualGallery(props: Props) {
>
<Suspense fallback={null}>
<Environment
preset={interiorStyle?.details === 'modern' || interiorStyle?.details === 'industrial' ? 'city' : 'warehouse'}
preset={interiorStyle && CITY_ENVIRONMENT_DETAILS.has(interiorStyle.details) ? 'city' : 'warehouse'}
environmentIntensity={(interiorStyle ? 0.55 : 0.25) * lightScale}
/>
</Suspense>