From 26c6af37e8d396a610d219c65b41ee51f0cb109c Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Thu, 2 Apr 2026 20:41:17 +0200 Subject: [PATCH] Add advanced rendering settings to world configuration --- src/document/world-settings.ts | 163 ++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 2 deletions(-) diff --git a/src/document/world-settings.ts b/src/document/world-settings.ts index 44118515..555cae7b 100644 --- a/src/document/world-settings.ts +++ b/src/document/world-settings.ts @@ -2,6 +2,14 @@ import { DEFAULT_SUN_DIRECTION, type Vec3 } from "../core/vector"; export type WorldBackgroundMode = "solid" | "verticalGradient" | "image"; +export const ADVANCED_RENDERING_SHADOW_MAP_SIZES = [512, 1024, 2048, 4096] as const; +export const ADVANCED_RENDERING_SHADOW_TYPES = ["basic", "pcf", "pcfSoft"] as const; +export const ADVANCED_RENDERING_TONE_MAPPING_MODES = ["none", "linear", "reinhard", "cineon", "acesFilmic"] as const; + +export type AdvancedRenderingShadowMapSize = (typeof ADVANCED_RENDERING_SHADOW_MAP_SIZES)[number]; +export type AdvancedRenderingShadowType = (typeof ADVANCED_RENDERING_SHADOW_TYPES)[number]; +export type AdvancedRenderingToneMappingMode = (typeof ADVANCED_RENDERING_TONE_MAPPING_MODES)[number]; + export interface WorldSolidBackgroundSettings { mode: "solid"; colorHex: string; @@ -35,15 +43,118 @@ export interface WorldSunLightSettings { direction: Vec3; } +export interface AdvancedRenderingShadowsSettings { + enabled: boolean; + mapSize: AdvancedRenderingShadowMapSize; + type: AdvancedRenderingShadowType; + bias: number; +} + +export interface AdvancedRenderingAmbientOcclusionSettings { + enabled: boolean; + intensity: number; + radius: number; + samples: number; +} + +export interface AdvancedRenderingBloomSettings { + enabled: boolean; + intensity: number; + threshold: number; + radius: number; +} + +export interface AdvancedRenderingToneMappingSettings { + mode: AdvancedRenderingToneMappingMode; + exposure: number; +} + +export interface AdvancedRenderingDepthOfFieldSettings { + enabled: boolean; + focusDistance: number; + focalLength: number; + bokehScale: number; +} + +export interface AdvancedRenderingSettings { + enabled: boolean; + shadows: AdvancedRenderingShadowsSettings; + ambientOcclusion: AdvancedRenderingAmbientOcclusionSettings; + bloom: AdvancedRenderingBloomSettings; + toneMapping: AdvancedRenderingToneMappingSettings; + depthOfField: AdvancedRenderingDepthOfFieldSettings; +} + export interface WorldSettings { background: WorldBackgroundSettings; ambientLight: WorldAmbientLightSettings; sunLight: WorldSunLightSettings; + advancedRendering: AdvancedRenderingSettings; } const DEFAULT_SOLID_BACKGROUND_COLOR = "#2f3947"; const DEFAULT_GRADIENT_TOP_COLOR = DEFAULT_SOLID_BACKGROUND_COLOR; const DEFAULT_GRADIENT_BOTTOM_COLOR = "#141a22"; +const DEFAULT_ADVANCED_RENDERING_SHADOW_MAP_SIZE: AdvancedRenderingShadowMapSize = 2048; +const DEFAULT_ADVANCED_RENDERING_SHADOW_TYPE: AdvancedRenderingShadowType = "pcfSoft"; +const DEFAULT_ADVANCED_RENDERING_SHADOW_BIAS = -0.0005; +const DEFAULT_ADVANCED_RENDERING_AMBIENT_OCCLUSION_INTENSITY = 1; +const DEFAULT_ADVANCED_RENDERING_AMBIENT_OCCLUSION_RADIUS = 0.5; +const DEFAULT_ADVANCED_RENDERING_AMBIENT_OCCLUSION_SAMPLES = 8; +const DEFAULT_ADVANCED_RENDERING_BLOOM_INTENSITY = 0.75; +const DEFAULT_ADVANCED_RENDERING_BLOOM_THRESHOLD = 0.85; +const DEFAULT_ADVANCED_RENDERING_BLOOM_RADIUS = 0.35; +const DEFAULT_ADVANCED_RENDERING_TONE_MAPPING_MODE: AdvancedRenderingToneMappingMode = "acesFilmic"; +const DEFAULT_ADVANCED_RENDERING_TONE_MAPPING_EXPOSURE = 1; +const DEFAULT_ADVANCED_RENDERING_DEPTH_OF_FIELD_FOCUS_DISTANCE = 10; +const DEFAULT_ADVANCED_RENDERING_DEPTH_OF_FIELD_FOCAL_LENGTH = 0.03; +const DEFAULT_ADVANCED_RENDERING_DEPTH_OF_FIELD_BOKEH_SCALE = 1.5; + +export function isAdvancedRenderingShadowMapSize(value: unknown): value is AdvancedRenderingShadowMapSize { + return ADVANCED_RENDERING_SHADOW_MAP_SIZES.includes(value as AdvancedRenderingShadowMapSize); +} + +export function isAdvancedRenderingShadowType(value: unknown): value is AdvancedRenderingShadowType { + return ADVANCED_RENDERING_SHADOW_TYPES.includes(value as AdvancedRenderingShadowType); +} + +export function isAdvancedRenderingToneMappingMode(value: unknown): value is AdvancedRenderingToneMappingMode { + return ADVANCED_RENDERING_TONE_MAPPING_MODES.includes(value as AdvancedRenderingToneMappingMode); +} + +export function createDefaultAdvancedRenderingSettings(): AdvancedRenderingSettings { + return { + enabled: false, + shadows: { + enabled: false, + mapSize: DEFAULT_ADVANCED_RENDERING_SHADOW_MAP_SIZE, + type: DEFAULT_ADVANCED_RENDERING_SHADOW_TYPE, + bias: DEFAULT_ADVANCED_RENDERING_SHADOW_BIAS + }, + ambientOcclusion: { + enabled: false, + intensity: DEFAULT_ADVANCED_RENDERING_AMBIENT_OCCLUSION_INTENSITY, + radius: DEFAULT_ADVANCED_RENDERING_AMBIENT_OCCLUSION_RADIUS, + samples: DEFAULT_ADVANCED_RENDERING_AMBIENT_OCCLUSION_SAMPLES + }, + bloom: { + enabled: false, + intensity: DEFAULT_ADVANCED_RENDERING_BLOOM_INTENSITY, + threshold: DEFAULT_ADVANCED_RENDERING_BLOOM_THRESHOLD, + radius: DEFAULT_ADVANCED_RENDERING_BLOOM_RADIUS + }, + toneMapping: { + mode: DEFAULT_ADVANCED_RENDERING_TONE_MAPPING_MODE, + exposure: DEFAULT_ADVANCED_RENDERING_TONE_MAPPING_EXPOSURE + }, + depthOfField: { + enabled: false, + focusDistance: DEFAULT_ADVANCED_RENDERING_DEPTH_OF_FIELD_FOCUS_DISTANCE, + focalLength: DEFAULT_ADVANCED_RENDERING_DEPTH_OF_FIELD_FOCAL_LENGTH, + bokehScale: DEFAULT_ADVANCED_RENDERING_DEPTH_OF_FIELD_BOKEH_SCALE + } + }; +} export function createDefaultWorldSettings(): WorldSettings { return { @@ -61,7 +172,8 @@ export function createDefaultWorldSettings(): WorldSettings { direction: { ...DEFAULT_SUN_DIRECTION } - } + }, + advancedRendering: createDefaultAdvancedRenderingSettings() }; } @@ -107,6 +219,28 @@ export function cloneWorldSettings(world: WorldSettings): WorldSettings { direction: { ...world.sunLight.direction } + }, + advancedRendering: cloneAdvancedRenderingSettings(world.advancedRendering) + }; +} + +export function cloneAdvancedRenderingSettings(settings: AdvancedRenderingSettings): AdvancedRenderingSettings { + return { + enabled: settings.enabled, + shadows: { + ...settings.shadows + }, + ambientOcclusion: { + ...settings.ambientOcclusion + }, + bloom: { + ...settings.bloom + }, + toneMapping: { + ...settings.toneMapping + }, + depthOfField: { + ...settings.depthOfField } }; } @@ -136,7 +270,32 @@ export function areWorldSettingsEqual(left: WorldSettings, right: WorldSettings) left.sunLight.intensity === right.sunLight.intensity && left.sunLight.direction.x === right.sunLight.direction.x && left.sunLight.direction.y === right.sunLight.direction.y && - left.sunLight.direction.z === right.sunLight.direction.z + left.sunLight.direction.z === right.sunLight.direction.z && + areAdvancedRenderingSettingsEqual(left.advancedRendering, right.advancedRendering) + ); +} + +export function areAdvancedRenderingSettingsEqual(left: AdvancedRenderingSettings, right: AdvancedRenderingSettings): boolean { + return ( + left.enabled === right.enabled && + left.shadows.enabled === right.shadows.enabled && + left.shadows.mapSize === right.shadows.mapSize && + left.shadows.type === right.shadows.type && + left.shadows.bias === right.shadows.bias && + left.ambientOcclusion.enabled === right.ambientOcclusion.enabled && + left.ambientOcclusion.intensity === right.ambientOcclusion.intensity && + left.ambientOcclusion.radius === right.ambientOcclusion.radius && + left.ambientOcclusion.samples === right.ambientOcclusion.samples && + left.bloom.enabled === right.bloom.enabled && + left.bloom.intensity === right.bloom.intensity && + left.bloom.threshold === right.bloom.threshold && + left.bloom.radius === right.bloom.radius && + left.toneMapping.mode === right.toneMapping.mode && + left.toneMapping.exposure === right.toneMapping.exposure && + left.depthOfField.enabled === right.depthOfField.enabled && + left.depthOfField.focusDistance === right.depthOfField.focusDistance && + left.depthOfField.focalLength === right.depthOfField.focalLength && + left.depthOfField.bokehScale === right.depthOfField.bokehScale ); }