223 lines
6.4 KiB
TypeScript

import type { SurfaceRole } from '@pascal-app/core'
export type SceneTheme = {
id: string
name: string
// Drives the 2D scene chrome that used to follow the removed light/dark toggle:
// canvas backdrop, grid line colours, measurement-label/cursor contrast, and
// the site ground fill. The 3D background + lights come from the fields below.
appearance: 'light' | 'dark'
background: string
// Colour of the site ground fill + infinite ground-occluder plane. Kept
// separate from `background` so dark themes can have a lit ground that reads
// as ground rather than going near-black.
ground: string
ambient: { color: string; intensity: number }
hemi?: { sky: string; ground: string; intensity: number }
lights: Array<{
position: [number, number, number]
color: string
intensity: number
castShadow?: boolean
}>
toneMappingExposure: number
clayTints?: Partial<Record<SurfaceRole, string>>
}
export const SCENE_THEMES: SceneTheme[] = [
{
id: 'studio',
name: 'Studio',
appearance: 'light',
background: '#ffffff',
ground: '#f4f4f2',
ambient: { color: '#ffffff', intensity: 0.15 },
hemi: { sky: '#ffffff', ground: '#aaa49a', intensity: 0.6 },
lights: [
{ position: [10, 10, 10], color: '#ffffff', intensity: 4, castShadow: true },
{ position: [-10, 10, -10], color: '#ffffff', intensity: 0.75 },
],
toneMappingExposure: 0.9,
clayTints: {
wall: '#e9e5db',
floor: '#d8d2c4',
ceiling: '#f1ede4',
roof: '#c4bba6',
glazing: '#cdd8df',
},
},
{
id: 'paper',
name: 'Paper',
appearance: 'light',
background: '#ede9df',
ground: '#e7e1d3',
ambient: { color: '#fff9eb', intensity: 0.55 },
hemi: { sky: '#fff5d9', ground: '#c2b89c', intensity: 0.35 },
lights: [
{ position: [16, 22, 12], color: '#fff1c8', intensity: 2.6, castShadow: true },
{ position: [-14, 10, -6], color: '#dde5ff', intensity: 0.35 },
],
toneMappingExposure: 1,
clayTints: {
wall: '#efe9da',
floor: '#ddd4bf',
ceiling: '#f5efe0',
roof: '#b9b09a',
glazing: '#cdd5d8',
},
},
{
id: 'sunset',
name: 'Sunset',
appearance: 'light',
background: '#f6e8d4',
ground: '#ecd9bf',
ambient: { color: '#ffd9a8', intensity: 0.45 },
hemi: { sky: '#ffd9a8', ground: '#5b4634', intensity: 0.4 },
lights: [
{ position: [22, 8, 8], color: '#ffb070', intensity: 3.4, castShadow: true },
{ position: [-14, 16, -10], color: '#a4b8ff', intensity: 0.4 },
],
toneMappingExposure: 1,
clayTints: {
wall: '#f3e3cf',
floor: '#e2cdab',
ceiling: '#f6e7d2',
roof: '#a6764f',
glazing: '#e7c9a8',
},
},
{
id: 'overcast',
name: 'Overcast',
appearance: 'light',
background: '#e6e7e6',
ground: '#dadcd9',
ambient: { color: '#eef0ef', intensity: 1.1 },
hemi: { sky: '#f4f5f3', ground: '#bcbfbb', intensity: 0.9 },
lights: [{ position: [12, 28, 10], color: '#f4f5f3', intensity: 0.8, castShadow: true }],
toneMappingExposure: 0.95,
clayTints: {
wall: '#dedfdc',
floor: '#cdcec9',
ceiling: '#e8e9e6',
roof: '#a3a49e',
glazing: '#c6cdd0',
},
},
{
id: 'blueprint',
name: 'Blueprint',
appearance: 'light',
background: '#dde6ef',
ground: '#c9d6e6',
ambient: { color: '#cfdcec', intensity: 0.7 },
hemi: { sky: '#dfeaf6', ground: '#5b6b80', intensity: 0.55 },
lights: [
{ position: [16, 24, 12], color: '#e6efff', intensity: 1.8, castShadow: true },
{ position: [-12, 10, -8], color: '#9fb6d8', intensity: 0.4 },
],
toneMappingExposure: 0.95,
clayTints: {
wall: '#9fb6d2',
floor: '#8ba2c2',
ceiling: '#aec0d8',
roof: '#5f789b',
glazing: '#b6d7ea',
},
},
{
id: 'mediterranean',
name: 'Mediterranean',
appearance: 'light',
background: '#bdd6e8',
ground: '#ddd2bb',
ambient: { color: '#d6e6f3', intensity: 0.5 },
hemi: { sky: '#a8c8e2', ground: '#d8c9a4', intensity: 0.6 },
lights: [
{ position: [18, 20, 12], color: '#fff4d4', intensity: 3.6, castShadow: true },
{ position: [-12, 8, -8], color: '#8fb3d8', intensity: 0.7 },
],
toneMappingExposure: 0.9,
clayTints: {
wall: '#f6f1e6',
floor: '#e0d6c2',
ceiling: '#f3ede0',
roof: '#3e6585',
glazing: '#bcd3e2',
},
},
{
id: 'twilight',
name: 'Twilight',
appearance: 'dark',
background: '#3a3550',
ground: '#4a4566',
ambient: { color: '#a89cc8', intensity: 0.35 },
hemi: { sky: '#d8a8c0', ground: '#1c1830', intensity: 0.5 },
lights: [
{ position: [-14, 22, -10], color: '#a4b6e8', intensity: 1.4, castShadow: true },
{ position: [14, 6, 8], color: '#ffb070', intensity: 0.9 },
],
toneMappingExposure: 1.1,
clayTints: {
wall: '#c5b9cf',
floor: '#ad9fbb',
ceiling: '#d2c6dc',
roof: '#5b4f74',
glazing: '#c3b6d4',
},
},
{
id: 'night',
name: 'Night',
appearance: 'dark',
background: '#1f2433',
ground: '#2b3247',
ambient: { color: '#a0b0ff', intensity: 0.07 },
hemi: { sky: '#3a4666', ground: '#0e111c', intensity: 0.4 },
lights: [
{ position: [10, 10, 10], color: '#e0e5ff', intensity: 0.8, castShadow: true },
{ position: [-10, 10, -10], color: '#8090ff', intensity: 0.2 },
],
toneMappingExposure: 0.9,
clayTints: {
wall: '#aab3c6',
floor: '#98a1b5',
ceiling: '#b7bfd0',
roof: '#5b6680',
glazing: '#aebbd0',
},
},
{
id: 'verdant',
name: 'Verdant',
appearance: 'light',
background: '#d6e4d2',
ground: '#c7d6b4',
ambient: { color: '#e3efdd', intensity: 0.5 },
hemi: { sky: '#cfe6cf', ground: '#8ea06f', intensity: 0.65 },
lights: [
{ position: [16, 22, 12], color: '#fff6d8', intensity: 3, castShadow: true },
{ position: [-12, 10, -8], color: '#bfe0c2', intensity: 0.5 },
],
toneMappingExposure: 0.95,
clayTints: {
wall: '#eef0e6',
floor: '#d8ddc6',
ceiling: '#f1f3ea',
roof: '#6f8a5a',
glazing: '#c4dcd0',
},
},
]
export const SCENE_THEME_IDS = SCENE_THEMES.map((theme) => theme.id)
const SCENE_THEME_BY_ID = new Map(SCENE_THEMES.map((theme) => [theme.id, theme]))
export function getSceneTheme(id: string): SceneTheme {
return SCENE_THEME_BY_ID.get(id) ?? SCENE_THEMES[0]!
}