2026-03-31 05:09:15 +02:00
|
|
|
import { DEFAULT_SUN_DIRECTION, type Vec3 } from "../core/vector";
|
|
|
|
|
|
2026-03-31 19:57:22 +02:00
|
|
|
export type WorldBackgroundMode = "solid" | "verticalGradient" | "image";
|
2026-04-13 14:43:48 +02:00
|
|
|
export type WorldTimePhase = "dawn" | "dusk" | "night";
|
2026-03-31 05:09:15 +02:00
|
|
|
|
2026-04-02 20:41:17 +02:00
|
|
|
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;
|
2026-04-06 08:17:53 +02:00
|
|
|
export const BOX_VOLUME_RENDER_PATHS = ["performance", "quality"] as const;
|
2026-04-07 06:29:31 +02:00
|
|
|
export const ADVANCED_RENDERING_WATER_REFLECTION_MODES = ["none", "world", "all"] as const;
|
2026-04-02 20:41:17 +02:00
|
|
|
|
|
|
|
|
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];
|
2026-04-06 08:17:53 +02:00
|
|
|
export type BoxVolumeRenderPath = (typeof BOX_VOLUME_RENDER_PATHS)[number];
|
2026-04-07 06:29:31 +02:00
|
|
|
export type AdvancedRenderingWaterReflectionMode = (typeof ADVANCED_RENDERING_WATER_REFLECTION_MODES)[number];
|
2026-04-02 20:41:17 +02:00
|
|
|
|
2026-03-31 05:09:15 +02:00
|
|
|
export interface WorldSolidBackgroundSettings {
|
|
|
|
|
mode: "solid";
|
|
|
|
|
colorHex: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface WorldVerticalGradientBackgroundSettings {
|
|
|
|
|
mode: "verticalGradient";
|
|
|
|
|
topColorHex: string;
|
|
|
|
|
bottomColorHex: string;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 19:57:22 +02:00
|
|
|
export interface WorldImageBackgroundSettings {
|
|
|
|
|
mode: "image";
|
|
|
|
|
assetId: string;
|
2026-03-31 23:13:12 +02:00
|
|
|
environmentIntensity: number;
|
2026-03-31 19:57:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type WorldBackgroundSettings =
|
|
|
|
|
| WorldSolidBackgroundSettings
|
|
|
|
|
| WorldVerticalGradientBackgroundSettings
|
|
|
|
|
| WorldImageBackgroundSettings;
|
2026-03-31 05:09:15 +02:00
|
|
|
|
|
|
|
|
export interface WorldAmbientLightSettings {
|
|
|
|
|
colorHex: string;
|
|
|
|
|
intensity: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface WorldSunLightSettings {
|
|
|
|
|
colorHex: string;
|
|
|
|
|
intensity: number;
|
|
|
|
|
direction: Vec3;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-13 14:43:48 +02:00
|
|
|
export interface WorldTimePhaseProfile {
|
2026-04-22 12:27:55 +02:00
|
|
|
background: WorldBackgroundSettings;
|
2026-04-13 14:43:48 +02:00
|
|
|
skyTopColorHex: string;
|
|
|
|
|
skyBottomColorHex: string;
|
|
|
|
|
ambientColorHex: string;
|
|
|
|
|
ambientIntensityFactor: number;
|
|
|
|
|
lightColorHex: string;
|
|
|
|
|
lightIntensityFactor: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface WorldNightEnvironmentSettings {
|
|
|
|
|
background: WorldBackgroundSettings;
|
|
|
|
|
ambientColorHex: string;
|
|
|
|
|
ambientIntensityFactor: number;
|
|
|
|
|
lightColorHex: string;
|
|
|
|
|
lightIntensityFactor: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface WorldTimeOfDaySettings {
|
|
|
|
|
dawn: WorldTimePhaseProfile;
|
|
|
|
|
dusk: WorldTimePhaseProfile;
|
|
|
|
|
night: WorldNightEnvironmentSettings;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:41:17 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-12 01:03:23 +02:00
|
|
|
export interface AdvancedRenderingWhiteboxBevelSettings {
|
|
|
|
|
enabled: boolean;
|
|
|
|
|
edgeWidth: number;
|
|
|
|
|
normalStrength: number;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:41:17 +02:00
|
|
|
export interface AdvancedRenderingSettings {
|
|
|
|
|
enabled: boolean;
|
|
|
|
|
shadows: AdvancedRenderingShadowsSettings;
|
|
|
|
|
ambientOcclusion: AdvancedRenderingAmbientOcclusionSettings;
|
|
|
|
|
bloom: AdvancedRenderingBloomSettings;
|
|
|
|
|
toneMapping: AdvancedRenderingToneMappingSettings;
|
|
|
|
|
depthOfField: AdvancedRenderingDepthOfFieldSettings;
|
2026-04-12 01:03:23 +02:00
|
|
|
whiteboxBevel: AdvancedRenderingWhiteboxBevelSettings;
|
2026-04-06 08:17:53 +02:00
|
|
|
fogPath: BoxVolumeRenderPath;
|
|
|
|
|
waterPath: BoxVolumeRenderPath;
|
2026-04-07 06:29:31 +02:00
|
|
|
waterReflectionMode: AdvancedRenderingWaterReflectionMode;
|
2026-04-02 20:41:17 +02:00
|
|
|
}
|
|
|
|
|
|
2026-03-31 05:09:15 +02:00
|
|
|
export interface WorldSettings {
|
2026-04-12 14:07:32 +02:00
|
|
|
projectTimeLightingEnabled: boolean;
|
2026-03-31 05:09:15 +02:00
|
|
|
background: WorldBackgroundSettings;
|
|
|
|
|
ambientLight: WorldAmbientLightSettings;
|
|
|
|
|
sunLight: WorldSunLightSettings;
|
2026-04-13 14:43:48 +02:00
|
|
|
timeOfDay: WorldTimeOfDaySettings;
|
2026-04-02 20:41:17 +02:00
|
|
|
advancedRendering: AdvancedRenderingSettings;
|
2026-03-31 05:09:15 +02:00
|
|
|
}
|
|
|
|
|
|
2026-03-31 20:02:56 +02:00
|
|
|
const DEFAULT_SOLID_BACKGROUND_COLOR = "#2f3947";
|
|
|
|
|
const DEFAULT_GRADIENT_TOP_COLOR = DEFAULT_SOLID_BACKGROUND_COLOR;
|
2026-03-31 05:09:15 +02:00
|
|
|
const DEFAULT_GRADIENT_BOTTOM_COLOR = "#141a22";
|
2026-04-13 14:44:48 +02:00
|
|
|
export const DEFAULT_NIGHT_IMAGE_ENVIRONMENT_INTENSITY = 0.35 as const;
|
2026-04-22 12:27:55 +02:00
|
|
|
export const DEFAULT_TIME_PHASE_IMAGE_ENVIRONMENT_INTENSITY = 0.5 as const;
|
2026-04-02 20:41:17 +02:00
|
|
|
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;
|
2026-04-12 01:03:23 +02:00
|
|
|
const DEFAULT_ADVANCED_RENDERING_WHITEBOX_BEVEL_EDGE_WIDTH = 0.14;
|
|
|
|
|
const DEFAULT_ADVANCED_RENDERING_WHITEBOX_BEVEL_NORMAL_STRENGTH = 0.75;
|
2026-04-06 08:17:53 +02:00
|
|
|
const DEFAULT_BOX_VOLUME_RENDER_PATH: BoxVolumeRenderPath = "performance";
|
2026-04-07 06:29:31 +02:00
|
|
|
const DEFAULT_ADVANCED_RENDERING_WATER_REFLECTION_MODE: AdvancedRenderingWaterReflectionMode = "none";
|
2026-04-02 20:41:17 +02:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 08:17:53 +02:00
|
|
|
export function isBoxVolumeRenderPath(value: unknown): value is BoxVolumeRenderPath {
|
|
|
|
|
return BOX_VOLUME_RENDER_PATHS.includes(value as BoxVolumeRenderPath);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 06:29:31 +02:00
|
|
|
export function isAdvancedRenderingWaterReflectionMode(value: unknown): value is AdvancedRenderingWaterReflectionMode {
|
|
|
|
|
return ADVANCED_RENDERING_WATER_REFLECTION_MODES.includes(value as AdvancedRenderingWaterReflectionMode);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:41:17 +02:00
|
|
|
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
|
2026-04-06 08:17:53 +02:00
|
|
|
},
|
2026-04-12 01:03:23 +02:00
|
|
|
whiteboxBevel: {
|
|
|
|
|
enabled: false,
|
|
|
|
|
edgeWidth: DEFAULT_ADVANCED_RENDERING_WHITEBOX_BEVEL_EDGE_WIDTH,
|
|
|
|
|
normalStrength: DEFAULT_ADVANCED_RENDERING_WHITEBOX_BEVEL_NORMAL_STRENGTH
|
|
|
|
|
},
|
2026-04-06 08:17:53 +02:00
|
|
|
fogPath: DEFAULT_BOX_VOLUME_RENDER_PATH,
|
2026-04-07 06:29:31 +02:00
|
|
|
waterPath: DEFAULT_BOX_VOLUME_RENDER_PATH,
|
|
|
|
|
waterReflectionMode: DEFAULT_ADVANCED_RENDERING_WATER_REFLECTION_MODE
|
2026-04-02 20:41:17 +02:00
|
|
|
};
|
|
|
|
|
}
|
2026-03-31 05:09:15 +02:00
|
|
|
|
2026-04-13 14:43:48 +02:00
|
|
|
export function createDefaultWorldTimePhaseProfile(
|
|
|
|
|
phase: WorldTimePhase
|
|
|
|
|
): WorldTimePhaseProfile {
|
|
|
|
|
switch (phase) {
|
|
|
|
|
case "dawn":
|
|
|
|
|
return {
|
2026-04-22 12:27:55 +02:00
|
|
|
background: {
|
|
|
|
|
mode: "verticalGradient",
|
|
|
|
|
topColorHex: "#5877b2",
|
|
|
|
|
bottomColorHex: "#f6a66f"
|
|
|
|
|
},
|
2026-04-13 14:43:48 +02:00
|
|
|
skyTopColorHex: "#5877b2",
|
|
|
|
|
skyBottomColorHex: "#f6a66f",
|
|
|
|
|
ambientColorHex: "#ffd7b0",
|
|
|
|
|
ambientIntensityFactor: 0.72,
|
|
|
|
|
lightColorHex: "#ffc98d",
|
|
|
|
|
lightIntensityFactor: 0.78
|
|
|
|
|
};
|
|
|
|
|
case "dusk":
|
|
|
|
|
return {
|
2026-04-22 12:27:55 +02:00
|
|
|
background: {
|
|
|
|
|
mode: "verticalGradient",
|
|
|
|
|
topColorHex: "#304076",
|
|
|
|
|
bottomColorHex: "#f08b5b"
|
|
|
|
|
},
|
2026-04-13 14:43:48 +02:00
|
|
|
skyTopColorHex: "#304076",
|
|
|
|
|
skyBottomColorHex: "#f08b5b",
|
|
|
|
|
ambientColorHex: "#f0b69a",
|
|
|
|
|
ambientIntensityFactor: 0.6,
|
|
|
|
|
lightColorHex: "#ffae7d",
|
|
|
|
|
lightIntensityFactor: 0.66
|
|
|
|
|
};
|
|
|
|
|
case "night":
|
|
|
|
|
return {
|
2026-04-22 12:27:55 +02:00
|
|
|
background: {
|
|
|
|
|
mode: "verticalGradient",
|
|
|
|
|
topColorHex: "#081120",
|
|
|
|
|
bottomColorHex: "#1a2438"
|
|
|
|
|
},
|
2026-04-13 14:43:48 +02:00
|
|
|
skyTopColorHex: "#081120",
|
|
|
|
|
skyBottomColorHex: "#1a2438",
|
|
|
|
|
ambientColorHex: "#1d2d45",
|
|
|
|
|
ambientIntensityFactor: 0.24,
|
|
|
|
|
lightColorHex: "#99b5ff",
|
|
|
|
|
lightIntensityFactor: 0.16
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function createDefaultWorldTimeOfDaySettings(): WorldTimeOfDaySettings {
|
|
|
|
|
const nightProfile = createDefaultWorldTimePhaseProfile("night");
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
dawn: createDefaultWorldTimePhaseProfile("dawn"),
|
|
|
|
|
dusk: createDefaultWorldTimePhaseProfile("dusk"),
|
|
|
|
|
night: {
|
|
|
|
|
background: {
|
|
|
|
|
mode: "verticalGradient",
|
|
|
|
|
topColorHex: nightProfile.skyTopColorHex,
|
|
|
|
|
bottomColorHex: nightProfile.skyBottomColorHex
|
|
|
|
|
},
|
|
|
|
|
ambientColorHex: nightProfile.ambientColorHex,
|
|
|
|
|
ambientIntensityFactor: nightProfile.ambientIntensityFactor,
|
|
|
|
|
lightColorHex: nightProfile.lightColorHex,
|
|
|
|
|
lightIntensityFactor: nightProfile.lightIntensityFactor
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 05:09:15 +02:00
|
|
|
export function createDefaultWorldSettings(): WorldSettings {
|
|
|
|
|
return {
|
2026-04-12 14:07:32 +02:00
|
|
|
projectTimeLightingEnabled: true,
|
2026-03-31 05:09:15 +02:00
|
|
|
background: {
|
|
|
|
|
mode: "solid",
|
2026-03-31 20:02:56 +02:00
|
|
|
colorHex: DEFAULT_SOLID_BACKGROUND_COLOR
|
2026-03-31 05:09:15 +02:00
|
|
|
},
|
|
|
|
|
ambientLight: {
|
|
|
|
|
colorHex: "#f7f1e8",
|
|
|
|
|
intensity: 1
|
|
|
|
|
},
|
|
|
|
|
sunLight: {
|
|
|
|
|
colorHex: "#fff1d5",
|
|
|
|
|
intensity: 1.75,
|
|
|
|
|
direction: {
|
|
|
|
|
...DEFAULT_SUN_DIRECTION
|
|
|
|
|
}
|
2026-04-02 20:41:17 +02:00
|
|
|
},
|
2026-04-13 14:43:48 +02:00
|
|
|
timeOfDay: createDefaultWorldTimeOfDaySettings(),
|
2026-04-02 20:41:17 +02:00
|
|
|
advancedRendering: createDefaultAdvancedRenderingSettings()
|
2026-03-31 05:09:15 +02:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isHexColorString(value: string): boolean {
|
|
|
|
|
return /^#[0-9a-f]{6}$/i.test(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isWorldBackgroundMode(value: unknown): value is WorldBackgroundMode {
|
2026-03-31 19:57:22 +02:00
|
|
|
return value === "solid" || value === "verticalGradient" || value === "image";
|
2026-03-31 05:09:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function cloneWorldBackgroundSettings(background: WorldBackgroundSettings): WorldBackgroundSettings {
|
|
|
|
|
if (background.mode === "solid") {
|
|
|
|
|
return {
|
|
|
|
|
mode: "solid",
|
|
|
|
|
colorHex: background.colorHex
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 19:57:28 +02:00
|
|
|
if (background.mode === "verticalGradient") {
|
|
|
|
|
return {
|
|
|
|
|
mode: "verticalGradient",
|
|
|
|
|
topColorHex: background.topColorHex,
|
|
|
|
|
bottomColorHex: background.bottomColorHex
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-03-31 19:57:22 +02:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
mode: "image",
|
2026-03-31 23:13:17 +02:00
|
|
|
assetId: background.assetId,
|
|
|
|
|
environmentIntensity: background.environmentIntensity
|
2026-03-31 19:57:22 +02:00
|
|
|
};
|
2026-03-31 05:09:15 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-13 14:43:48 +02:00
|
|
|
export function cloneWorldTimePhaseProfile(
|
|
|
|
|
profile: WorldTimePhaseProfile
|
|
|
|
|
): WorldTimePhaseProfile {
|
|
|
|
|
return {
|
2026-04-22 12:27:55 +02:00
|
|
|
background: cloneWorldBackgroundSettings(profile.background),
|
2026-04-13 14:43:48 +02:00
|
|
|
skyTopColorHex: profile.skyTopColorHex,
|
|
|
|
|
skyBottomColorHex: profile.skyBottomColorHex,
|
|
|
|
|
ambientColorHex: profile.ambientColorHex,
|
|
|
|
|
ambientIntensityFactor: profile.ambientIntensityFactor,
|
|
|
|
|
lightColorHex: profile.lightColorHex,
|
|
|
|
|
lightIntensityFactor: profile.lightIntensityFactor
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function cloneWorldNightEnvironmentSettings(
|
|
|
|
|
settings: WorldNightEnvironmentSettings
|
|
|
|
|
): WorldNightEnvironmentSettings {
|
|
|
|
|
return {
|
|
|
|
|
background: cloneWorldBackgroundSettings(settings.background),
|
|
|
|
|
ambientColorHex: settings.ambientColorHex,
|
|
|
|
|
ambientIntensityFactor: settings.ambientIntensityFactor,
|
|
|
|
|
lightColorHex: settings.lightColorHex,
|
|
|
|
|
lightIntensityFactor: settings.lightIntensityFactor
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function cloneWorldTimeOfDaySettings(
|
|
|
|
|
settings: WorldTimeOfDaySettings
|
|
|
|
|
): WorldTimeOfDaySettings {
|
|
|
|
|
return {
|
|
|
|
|
dawn: cloneWorldTimePhaseProfile(settings.dawn),
|
|
|
|
|
dusk: cloneWorldTimePhaseProfile(settings.dusk),
|
|
|
|
|
night: cloneWorldNightEnvironmentSettings(settings.night)
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 05:09:15 +02:00
|
|
|
export function cloneWorldSettings(world: WorldSettings): WorldSettings {
|
|
|
|
|
return {
|
2026-04-12 14:07:32 +02:00
|
|
|
projectTimeLightingEnabled: world.projectTimeLightingEnabled,
|
2026-03-31 05:09:15 +02:00
|
|
|
background: cloneWorldBackgroundSettings(world.background),
|
|
|
|
|
ambientLight: {
|
|
|
|
|
...world.ambientLight
|
|
|
|
|
},
|
|
|
|
|
sunLight: {
|
|
|
|
|
...world.sunLight,
|
|
|
|
|
direction: {
|
|
|
|
|
...world.sunLight.direction
|
|
|
|
|
}
|
2026-04-02 20:41:17 +02:00
|
|
|
},
|
2026-04-13 14:43:48 +02:00
|
|
|
timeOfDay: cloneWorldTimeOfDaySettings(world.timeOfDay),
|
2026-04-02 20:41:17 +02:00
|
|
|
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
|
2026-04-06 08:17:53 +02:00
|
|
|
},
|
2026-04-12 01:03:23 +02:00
|
|
|
whiteboxBevel: {
|
|
|
|
|
...settings.whiteboxBevel
|
|
|
|
|
},
|
2026-04-06 08:17:53 +02:00
|
|
|
fogPath: settings.fogPath,
|
2026-04-07 06:29:31 +02:00
|
|
|
waterPath: settings.waterPath,
|
|
|
|
|
waterReflectionMode: settings.waterReflectionMode
|
2026-03-31 05:09:15 +02:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function areWorldBackgroundSettingsEqual(left: WorldBackgroundSettings, right: WorldBackgroundSettings): boolean {
|
|
|
|
|
if (left.mode !== right.mode) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 05:16:49 +02:00
|
|
|
if (left.mode === "solid" && right.mode === "solid") {
|
2026-03-31 05:09:15 +02:00
|
|
|
return left.colorHex === right.colorHex;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 19:57:22 +02:00
|
|
|
if (left.mode === "verticalGradient" && right.mode === "verticalGradient") {
|
|
|
|
|
return left.topColorHex === right.topColorHex && left.bottomColorHex === right.bottomColorHex;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 23:13:22 +02:00
|
|
|
return left.mode === "image" && right.mode === "image" && left.assetId === right.assetId && left.environmentIntensity === right.environmentIntensity;
|
2026-03-31 05:09:15 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-13 14:43:48 +02:00
|
|
|
export function areWorldTimePhaseProfilesEqual(
|
|
|
|
|
left: WorldTimePhaseProfile,
|
|
|
|
|
right: WorldTimePhaseProfile
|
|
|
|
|
): boolean {
|
|
|
|
|
return (
|
2026-04-22 12:27:55 +02:00
|
|
|
areWorldBackgroundSettingsEqual(left.background, right.background) &&
|
2026-04-13 14:43:48 +02:00
|
|
|
left.skyTopColorHex === right.skyTopColorHex &&
|
|
|
|
|
left.skyBottomColorHex === right.skyBottomColorHex &&
|
|
|
|
|
left.ambientColorHex === right.ambientColorHex &&
|
|
|
|
|
left.ambientIntensityFactor === right.ambientIntensityFactor &&
|
|
|
|
|
left.lightColorHex === right.lightColorHex &&
|
|
|
|
|
left.lightIntensityFactor === right.lightIntensityFactor
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function areWorldNightEnvironmentSettingsEqual(
|
|
|
|
|
left: WorldNightEnvironmentSettings,
|
|
|
|
|
right: WorldNightEnvironmentSettings
|
|
|
|
|
): boolean {
|
|
|
|
|
return (
|
|
|
|
|
areWorldBackgroundSettingsEqual(left.background, right.background) &&
|
|
|
|
|
left.ambientColorHex === right.ambientColorHex &&
|
|
|
|
|
left.ambientIntensityFactor === right.ambientIntensityFactor &&
|
|
|
|
|
left.lightColorHex === right.lightColorHex &&
|
|
|
|
|
left.lightIntensityFactor === right.lightIntensityFactor
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function areWorldTimeOfDaySettingsEqual(
|
|
|
|
|
left: WorldTimeOfDaySettings,
|
|
|
|
|
right: WorldTimeOfDaySettings
|
|
|
|
|
): boolean {
|
|
|
|
|
return (
|
|
|
|
|
areWorldTimePhaseProfilesEqual(left.dawn, right.dawn) &&
|
|
|
|
|
areWorldTimePhaseProfilesEqual(left.dusk, right.dusk) &&
|
|
|
|
|
areWorldNightEnvironmentSettingsEqual(left.night, right.night)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 05:09:15 +02:00
|
|
|
export function areWorldSettingsEqual(left: WorldSettings, right: WorldSettings): boolean {
|
|
|
|
|
return (
|
2026-04-12 14:07:32 +02:00
|
|
|
left.projectTimeLightingEnabled === right.projectTimeLightingEnabled &&
|
2026-03-31 05:09:15 +02:00
|
|
|
areWorldBackgroundSettingsEqual(left.background, right.background) &&
|
|
|
|
|
left.ambientLight.colorHex === right.ambientLight.colorHex &&
|
|
|
|
|
left.ambientLight.intensity === right.ambientLight.intensity &&
|
|
|
|
|
left.sunLight.colorHex === right.sunLight.colorHex &&
|
|
|
|
|
left.sunLight.intensity === right.sunLight.intensity &&
|
|
|
|
|
left.sunLight.direction.x === right.sunLight.direction.x &&
|
|
|
|
|
left.sunLight.direction.y === right.sunLight.direction.y &&
|
2026-04-02 20:41:17 +02:00
|
|
|
left.sunLight.direction.z === right.sunLight.direction.z &&
|
2026-04-13 14:43:48 +02:00
|
|
|
areWorldTimeOfDaySettingsEqual(left.timeOfDay, right.timeOfDay) &&
|
2026-04-02 20:41:17 +02:00
|
|
|
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 &&
|
2026-04-06 08:17:53 +02:00
|
|
|
left.depthOfField.bokehScale === right.depthOfField.bokehScale &&
|
2026-04-12 01:03:23 +02:00
|
|
|
left.whiteboxBevel.enabled === right.whiteboxBevel.enabled &&
|
|
|
|
|
left.whiteboxBevel.edgeWidth === right.whiteboxBevel.edgeWidth &&
|
|
|
|
|
left.whiteboxBevel.normalStrength === right.whiteboxBevel.normalStrength &&
|
2026-04-06 08:17:53 +02:00
|
|
|
left.fogPath === right.fogPath &&
|
2026-04-07 06:29:31 +02:00
|
|
|
left.waterPath === right.waterPath &&
|
|
|
|
|
left.waterReflectionMode === right.waterReflectionMode
|
2026-03-31 05:09:15 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 19:57:22 +02:00
|
|
|
export function changeWorldBackgroundMode(
|
|
|
|
|
background: WorldBackgroundSettings,
|
|
|
|
|
mode: WorldBackgroundMode,
|
|
|
|
|
imageAssetId?: string
|
|
|
|
|
): WorldBackgroundSettings {
|
2026-03-31 20:02:56 +02:00
|
|
|
if (mode === "image") {
|
|
|
|
|
if (imageAssetId === undefined || imageAssetId.trim().length === 0) {
|
|
|
|
|
if (background.mode === "image") {
|
|
|
|
|
return cloneWorldBackgroundSettings(background);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new Error("An image asset must be selected to use an image background.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
mode: "image",
|
2026-03-31 23:13:27 +02:00
|
|
|
assetId: imageAssetId,
|
2026-04-22 12:27:55 +02:00
|
|
|
environmentIntensity:
|
|
|
|
|
background.mode === "image"
|
|
|
|
|
? background.environmentIntensity
|
|
|
|
|
: DEFAULT_TIME_PHASE_IMAGE_ENVIRONMENT_INTENSITY
|
2026-03-31 20:02:56 +02:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 05:09:15 +02:00
|
|
|
if (background.mode === mode) {
|
|
|
|
|
return cloneWorldBackgroundSettings(background);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mode === "solid") {
|
|
|
|
|
return {
|
|
|
|
|
mode: "solid",
|
2026-03-31 20:02:56 +02:00
|
|
|
colorHex:
|
|
|
|
|
background.mode === "solid"
|
|
|
|
|
? background.colorHex
|
|
|
|
|
: background.mode === "verticalGradient"
|
|
|
|
|
? background.topColorHex
|
|
|
|
|
: DEFAULT_SOLID_BACKGROUND_COLOR
|
2026-03-31 05:09:15 +02:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 20:02:56 +02:00
|
|
|
if (background.mode === "solid") {
|
|
|
|
|
return {
|
|
|
|
|
mode: "verticalGradient",
|
|
|
|
|
topColorHex: background.colorHex,
|
|
|
|
|
bottomColorHex: DEFAULT_GRADIENT_BOTTOM_COLOR
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-03-31 19:57:22 +02:00
|
|
|
|
2026-03-31 20:02:56 +02:00
|
|
|
if (background.mode === "verticalGradient") {
|
|
|
|
|
return {
|
|
|
|
|
mode: "verticalGradient",
|
|
|
|
|
topColorHex: background.topColorHex,
|
|
|
|
|
bottomColorHex: background.bottomColorHex
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
mode: "verticalGradient",
|
|
|
|
|
topColorHex: DEFAULT_GRADIENT_TOP_COLOR,
|
|
|
|
|
bottomColorHex: DEFAULT_GRADIENT_BOTTOM_COLOR
|
|
|
|
|
};
|
2026-03-31 05:09:15 +02:00
|
|
|
}
|