use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum PbrDebugMode {
#[default]
None,
BaseColor,
Normal,
Metallic,
Roughness,
Occlusion,
Emissive,
F,
G,
D,
Diffuse,
Specular,
}
impl PbrDebugMode {
pub const ALL: &'static [PbrDebugMode] = &[
PbrDebugMode::None,
PbrDebugMode::BaseColor,
PbrDebugMode::Normal,
PbrDebugMode::Metallic,
PbrDebugMode::Roughness,
PbrDebugMode::Occlusion,
PbrDebugMode::Emissive,
PbrDebugMode::F,
PbrDebugMode::G,
PbrDebugMode::D,
PbrDebugMode::Diffuse,
PbrDebugMode::Specular,
];
pub fn name(&self) -> &'static str {
match self {
PbrDebugMode::None => "None",
PbrDebugMode::BaseColor => "Base Color",
PbrDebugMode::Normal => "Normal",
PbrDebugMode::Metallic => "Metallic",
PbrDebugMode::Roughness => "Roughness",
PbrDebugMode::Occlusion => "Occlusion",
PbrDebugMode::Emissive => "Emissive",
PbrDebugMode::F => "Fresnel (F)",
PbrDebugMode::G => "Geometry (G)",
PbrDebugMode::D => "Distribution (D)",
PbrDebugMode::Diffuse => "Diffuse",
PbrDebugMode::Specular => "Specular",
}
}
pub fn as_u32(&self) -> u32 {
match self {
PbrDebugMode::None => 0,
PbrDebugMode::BaseColor => 1,
PbrDebugMode::Normal => 2,
PbrDebugMode::Metallic => 3,
PbrDebugMode::Roughness => 4,
PbrDebugMode::Occlusion => 5,
PbrDebugMode::Emissive => 6,
PbrDebugMode::F => 7,
PbrDebugMode::G => 8,
PbrDebugMode::D => 9,
PbrDebugMode::Diffuse => 10,
PbrDebugMode::Specular => 11,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum DepthOfFieldQuality {
Low,
#[default]
Medium,
High,
}
impl DepthOfFieldQuality {
pub const ALL: &'static [DepthOfFieldQuality] = &[
DepthOfFieldQuality::Low,
DepthOfFieldQuality::Medium,
DepthOfFieldQuality::High,
];
pub fn name(&self) -> &'static str {
match self {
DepthOfFieldQuality::Low => "Low",
DepthOfFieldQuality::Medium => "Medium",
DepthOfFieldQuality::High => "High",
}
}
pub fn sample_count(&self) -> u32 {
match self {
DepthOfFieldQuality::Low => 8,
DepthOfFieldQuality::Medium => 16,
DepthOfFieldQuality::High => 32,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct DepthOfField {
pub enabled: bool,
pub focus_distance: f32,
pub focus_range: f32,
pub max_blur_radius: f32,
pub bokeh_threshold: f32,
pub bokeh_intensity: f32,
pub quality: DepthOfFieldQuality,
pub visualize_coc: bool,
pub tilt_shift_enabled: bool,
pub tilt_shift_angle: f32,
pub tilt_shift_center: f32,
pub tilt_shift_blur_amount: f32,
pub visualize_tilt_shift: bool,
}
impl Default for DepthOfField {
fn default() -> Self {
Self {
enabled: false,
focus_distance: 10.0,
focus_range: 5.0,
max_blur_radius: 8.0,
bokeh_threshold: 0.8,
bokeh_intensity: 1.0,
quality: DepthOfFieldQuality::Medium,
visualize_coc: false,
tilt_shift_enabled: false,
tilt_shift_angle: 0.0,
tilt_shift_center: 0.0,
tilt_shift_blur_amount: 1.0,
visualize_tilt_shift: false,
}
}
}
impl DepthOfField {
pub fn portrait() -> Self {
Self {
enabled: true,
focus_distance: 3.0,
focus_range: 1.5,
max_blur_radius: 12.0,
bokeh_threshold: 0.6,
bokeh_intensity: 1.2,
quality: DepthOfFieldQuality::High,
visualize_coc: false,
tilt_shift_enabled: false,
tilt_shift_angle: 0.0,
tilt_shift_center: 0.0,
tilt_shift_blur_amount: 1.0,
visualize_tilt_shift: false,
}
}
pub fn cinematic() -> Self {
Self {
enabled: true,
focus_distance: 8.0,
focus_range: 4.0,
max_blur_radius: 10.0,
bokeh_threshold: 0.7,
bokeh_intensity: 1.0,
quality: DepthOfFieldQuality::Medium,
visualize_coc: false,
tilt_shift_enabled: false,
tilt_shift_angle: 0.0,
tilt_shift_center: 0.0,
tilt_shift_blur_amount: 1.0,
visualize_tilt_shift: false,
}
}
pub fn macro_shot() -> Self {
Self {
enabled: true,
focus_distance: 0.5,
focus_range: 0.2,
max_blur_radius: 16.0,
bokeh_threshold: 0.5,
bokeh_intensity: 1.5,
quality: DepthOfFieldQuality::High,
visualize_coc: false,
tilt_shift_enabled: false,
tilt_shift_angle: 0.0,
tilt_shift_center: 0.0,
tilt_shift_blur_amount: 1.0,
visualize_tilt_shift: false,
}
}
pub fn landscape() -> Self {
Self {
enabled: true,
focus_distance: 50.0,
focus_range: 100.0,
max_blur_radius: 4.0,
bokeh_threshold: 0.9,
bokeh_intensity: 0.5,
quality: DepthOfFieldQuality::Low,
visualize_coc: false,
tilt_shift_enabled: false,
tilt_shift_angle: 0.0,
tilt_shift_center: 0.0,
tilt_shift_blur_amount: 1.0,
visualize_tilt_shift: false,
}
}
pub fn tilt_shift() -> Self {
Self {
enabled: true,
focus_distance: 10.0,
focus_range: 5.0,
max_blur_radius: 12.0,
bokeh_threshold: 0.8,
bokeh_intensity: 0.8,
quality: DepthOfFieldQuality::Medium,
visualize_coc: false,
tilt_shift_enabled: true,
tilt_shift_angle: 0.0,
tilt_shift_center: 0.0,
tilt_shift_blur_amount: 1.0,
visualize_tilt_shift: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum TonemapAlgorithm {
#[default]
Aces,
Reinhard,
ReinhardExtended,
Uncharted2,
AgX,
Neutral,
None,
}
impl TonemapAlgorithm {
pub const ALL: &'static [TonemapAlgorithm] = &[
TonemapAlgorithm::Aces,
TonemapAlgorithm::Reinhard,
TonemapAlgorithm::ReinhardExtended,
TonemapAlgorithm::Uncharted2,
TonemapAlgorithm::AgX,
TonemapAlgorithm::Neutral,
TonemapAlgorithm::None,
];
pub fn as_u32(&self) -> u32 {
match self {
TonemapAlgorithm::Aces => 0,
TonemapAlgorithm::Reinhard => 1,
TonemapAlgorithm::ReinhardExtended => 2,
TonemapAlgorithm::Uncharted2 => 3,
TonemapAlgorithm::AgX => 4,
TonemapAlgorithm::Neutral => 5,
TonemapAlgorithm::None => 6,
}
}
pub fn name(&self) -> &'static str {
match self {
TonemapAlgorithm::Aces => "ACES",
TonemapAlgorithm::Reinhard => "Reinhard",
TonemapAlgorithm::ReinhardExtended => "Reinhard Extended",
TonemapAlgorithm::Uncharted2 => "Uncharted 2",
TonemapAlgorithm::AgX => "AgX",
TonemapAlgorithm::Neutral => "Neutral",
TonemapAlgorithm::None => "None",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum ColorGradingPreset {
#[default]
Default,
Vibrant,
Cinematic,
Muted,
HighContrast,
Warm,
Cool,
Retro,
Desaturated,
Custom,
}
impl ColorGradingPreset {
pub const ALL: &'static [ColorGradingPreset] = &[
ColorGradingPreset::Default,
ColorGradingPreset::Vibrant,
ColorGradingPreset::Cinematic,
ColorGradingPreset::Muted,
ColorGradingPreset::HighContrast,
ColorGradingPreset::Warm,
ColorGradingPreset::Cool,
ColorGradingPreset::Retro,
ColorGradingPreset::Desaturated,
ColorGradingPreset::Custom,
];
pub fn name(&self) -> &'static str {
match self {
ColorGradingPreset::Default => "Default",
ColorGradingPreset::Vibrant => "Vibrant",
ColorGradingPreset::Cinematic => "Cinematic",
ColorGradingPreset::Muted => "Muted",
ColorGradingPreset::HighContrast => "High Contrast",
ColorGradingPreset::Warm => "Warm",
ColorGradingPreset::Cool => "Cool",
ColorGradingPreset::Retro => "Retro",
ColorGradingPreset::Desaturated => "Desaturated",
ColorGradingPreset::Custom => "Custom",
}
}
pub fn to_color_grading(&self) -> ColorGrading {
match self {
ColorGradingPreset::Default => ColorGrading {
gamma: 2.2,
saturation: 1.0,
brightness: 0.0,
contrast: 1.0,
tonemap_algorithm: TonemapAlgorithm::Aces,
preset: *self,
},
ColorGradingPreset::Vibrant => ColorGrading {
gamma: 2.2,
saturation: 1.3,
brightness: 0.02,
contrast: 1.1,
tonemap_algorithm: TonemapAlgorithm::Aces,
preset: *self,
},
ColorGradingPreset::Cinematic => ColorGrading {
gamma: 2.4,
saturation: 0.9,
brightness: -0.02,
contrast: 1.15,
tonemap_algorithm: TonemapAlgorithm::Aces,
preset: *self,
},
ColorGradingPreset::Muted => ColorGrading {
gamma: 2.2,
saturation: 0.7,
brightness: 0.0,
contrast: 0.9,
tonemap_algorithm: TonemapAlgorithm::Reinhard,
preset: *self,
},
ColorGradingPreset::HighContrast => ColorGrading {
gamma: 2.2,
saturation: 1.1,
brightness: 0.0,
contrast: 1.4,
tonemap_algorithm: TonemapAlgorithm::Aces,
preset: *self,
},
ColorGradingPreset::Warm => ColorGrading {
gamma: 2.1,
saturation: 1.1,
brightness: 0.03,
contrast: 1.05,
tonemap_algorithm: TonemapAlgorithm::Aces,
preset: *self,
},
ColorGradingPreset::Cool => ColorGrading {
gamma: 2.3,
saturation: 0.95,
brightness: -0.01,
contrast: 1.05,
tonemap_algorithm: TonemapAlgorithm::Neutral,
preset: *self,
},
ColorGradingPreset::Retro => ColorGrading {
gamma: 2.0,
saturation: 0.8,
brightness: 0.05,
contrast: 1.2,
tonemap_algorithm: TonemapAlgorithm::Reinhard,
preset: *self,
},
ColorGradingPreset::Desaturated => ColorGrading {
gamma: 2.2,
saturation: 0.3,
brightness: 0.0,
contrast: 1.0,
tonemap_algorithm: TonemapAlgorithm::Aces,
preset: *self,
},
ColorGradingPreset::Custom => ColorGrading::default(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct ColorGrading {
pub gamma: f32,
pub saturation: f32,
pub brightness: f32,
pub contrast: f32,
pub tonemap_algorithm: TonemapAlgorithm,
pub preset: ColorGradingPreset,
}
impl Default for ColorGrading {
fn default() -> Self {
Self {
gamma: 2.2,
saturation: 1.0,
brightness: 0.0,
contrast: 1.0,
tonemap_algorithm: TonemapAlgorithm::Aces,
preset: ColorGradingPreset::Default,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct VertexSnap {
pub resolution: [f32; 2],
}
impl Default for VertexSnap {
fn default() -> Self {
Self {
resolution: [160.0, 120.0],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Fog {
pub color: [f32; 3],
pub start: f32,
pub end: f32,
}
impl Default for Fog {
fn default() -> Self {
Self {
color: [0.5, 0.5, 0.55],
start: 2.0,
end: 15.0,
}
}
}
#[derive(Clone)]
pub struct DayNightState {
pub hour: f32,
pub speed: f32,
pub auto_cycle: bool,
pub sun_entity: Option<crate::ecs::world::Entity>,
}
impl Default for DayNightState {
fn default() -> Self {
Self {
hour: 12.0,
speed: 0.0,
auto_cycle: false,
sun_entity: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum Atmosphere {
#[default]
None,
Sky,
CloudySky,
Space,
Nebula,
Sunset,
DayNight,
Hdr,
HdrMip0,
HdrMip1,
HdrMip2,
HdrMip3,
HdrMip4,
Irradiance,
PrefilterMip0,
PrefilterMip1,
PrefilterMip2,
PrefilterMip3,
PrefilterMip4,
}
impl Atmosphere {
pub const ALL: &'static [Atmosphere] = &[
Atmosphere::None,
Atmosphere::Sky,
Atmosphere::CloudySky,
Atmosphere::Space,
Atmosphere::Nebula,
Atmosphere::Sunset,
Atmosphere::DayNight,
Atmosphere::Hdr,
Atmosphere::HdrMip0,
Atmosphere::HdrMip1,
Atmosphere::HdrMip2,
Atmosphere::HdrMip3,
Atmosphere::HdrMip4,
Atmosphere::Irradiance,
Atmosphere::PrefilterMip0,
Atmosphere::PrefilterMip1,
Atmosphere::PrefilterMip2,
Atmosphere::PrefilterMip3,
Atmosphere::PrefilterMip4,
];
pub fn mip_level(&self) -> f32 {
match self {
Atmosphere::HdrMip0 | Atmosphere::PrefilterMip0 => 0.0,
Atmosphere::HdrMip1 | Atmosphere::PrefilterMip1 => 1.0,
Atmosphere::HdrMip2 | Atmosphere::PrefilterMip2 => 2.0,
Atmosphere::HdrMip3 | Atmosphere::PrefilterMip3 => 3.0,
Atmosphere::HdrMip4 | Atmosphere::PrefilterMip4 => 4.0,
_ => 0.0,
}
}
pub fn next(self) -> Self {
let all = Self::ALL;
let current_index = all.iter().position(|&a| a == self).unwrap_or(0);
let next_index = (current_index + 1) % all.len();
all[next_index]
}
pub fn previous(self) -> Self {
let all = Self::ALL;
let current_index = all.iter().position(|&a| a == self).unwrap_or(0);
let prev_index = if current_index == 0 {
all.len() - 1
} else {
current_index - 1
};
all[prev_index]
}
pub fn is_procedural(&self) -> bool {
matches!(
self,
Atmosphere::Sky
| Atmosphere::CloudySky
| Atmosphere::Space
| Atmosphere::Nebula
| Atmosphere::Sunset
| Atmosphere::DayNight
)
}
pub fn as_procedural_cubemap_type(&self) -> Option<u32> {
match self {
Atmosphere::Sky => Some(0),
Atmosphere::CloudySky => Some(1),
Atmosphere::Space => Some(2),
Atmosphere::Nebula => Some(3),
Atmosphere::Sunset => Some(4),
Atmosphere::DayNight => Some(5),
_ => None,
}
}
}
#[derive(Default)]
pub struct IblViews {
pub brdf_lut_view: Option<wgpu::TextureView>,
pub irradiance_view: Option<wgpu::TextureView>,
pub prefiltered_view: Option<wgpu::TextureView>,
}
impl Clone for IblViews {
fn clone(&self) -> Self {
Self {
brdf_lut_view: self.brdf_lut_view.clone(),
irradiance_view: self.irradiance_view.clone(),
prefiltered_view: self.prefiltered_view.clone(),
}
}
}
#[derive(Clone)]
pub struct MeshLodLevel {
pub mesh_name: String,
pub min_screen_pixels: f32,
}
#[derive(Clone)]
pub struct MeshLodChain {
pub base_mesh: String,
pub levels: Vec<MeshLodLevel>,
}
#[derive(Clone)]
pub struct Graphics {
pub compute_grayscale_enabled: bool,
pub gpu_culling_enabled: bool,
pub occlusion_culling_enabled: bool,
pub min_screen_pixel_size: f32,
pub culling_camera_override: Option<freecs::Entity>,
pub show_grid: bool,
pub show_bounding_volumes: bool,
pub show_selected_bounding_volume: bool,
pub bounding_volume_selected_entity: Option<freecs::Entity>,
pub show_normals: bool,
pub normal_line_length: f32,
pub normal_line_color: [f32; 4],
pub atmosphere: Atmosphere,
pub render_layer_world_enabled: bool,
pub render_layer_overlay_enabled: bool,
pub use_fullscreen: bool,
pub show_cursor: bool,
pub clear_color: [f32; 4],
pub ui_scale: Option<f32>,
pub letterbox_amount: f32,
pub letterbox_target: f32,
pub unlit_mode: bool,
pub selection_outline_enabled: bool,
pub selection_outline_color: [f32; 4],
pub vertex_snap: Option<VertexSnap>,
pub affine_texture_mapping: bool,
pub fog: Option<Fog>,
pub color_grading: ColorGrading,
pub bloom_enabled: bool,
pub bloom_intensity: f32,
pub bloom_threshold: f32,
pub depth_of_field: DepthOfField,
pub ssao_enabled: bool,
pub ssao_radius: f32,
pub ssao_bias: f32,
pub ssao_intensity: f32,
pub ssao_sample_count: u32,
pub ssao_visualization: bool,
pub ssao_blur_depth_threshold: f32,
pub ssao_blur_normal_power: f32,
pub ssgi_enabled: bool,
pub ssgi_radius: f32,
pub ssgi_intensity: f32,
pub ssgi_max_steps: u32,
pub ssr_enabled: bool,
pub ssr_max_steps: u32,
pub ssr_thickness: f32,
pub ssr_max_distance: f32,
pub ssr_stride: f32,
pub ssr_fade_start: f32,
pub ssr_fade_end: f32,
pub ssr_intensity: f32,
pub ambient_light: [f32; 4],
pub pbr_debug_mode: PbrDebugMode,
pub texture_debug_stripes: bool,
pub texture_debug_stripes_speed: f32,
pub fxaa_enabled: bool,
pub day_night: DayNightState,
pub ibl_blend_factor: f32,
pub mesh_lod_chains: Vec<MeshLodChain>,
}
impl Default for Graphics {
fn default() -> Self {
Self {
compute_grayscale_enabled: false,
gpu_culling_enabled: true,
occlusion_culling_enabled: true,
min_screen_pixel_size: 0.0,
culling_camera_override: None,
show_grid: false,
show_bounding_volumes: false,
show_selected_bounding_volume: false,
bounding_volume_selected_entity: None,
show_normals: false,
normal_line_length: 0.1,
normal_line_color: [0.0, 1.0, 0.0, 1.0],
atmosphere: Atmosphere::None,
render_layer_world_enabled: true,
render_layer_overlay_enabled: true,
use_fullscreen: false,
show_cursor: true,
clear_color: [0.0, 0.0, 0.0, 1.0],
ui_scale: None,
letterbox_amount: 0.0,
letterbox_target: 0.0,
unlit_mode: false,
selection_outline_enabled: false,
selection_outline_color: [1.0, 0.45, 0.0, 1.0],
vertex_snap: None,
affine_texture_mapping: false,
fog: None,
color_grading: ColorGrading::default(),
bloom_enabled: false,
bloom_intensity: 0.3,
bloom_threshold: 1.0,
depth_of_field: DepthOfField::default(),
ssao_enabled: false,
ssao_radius: 0.5,
ssao_bias: 0.025,
ssao_intensity: 1.0,
ssao_sample_count: 64,
ssao_visualization: false,
ssao_blur_depth_threshold: 0.005,
ssao_blur_normal_power: 8.0,
ssgi_enabled: false,
ssgi_radius: 2.0,
ssgi_intensity: 1.0,
ssgi_max_steps: 16,
ssr_enabled: false,
ssr_max_steps: 64,
ssr_thickness: 0.3,
ssr_max_distance: 50.0,
ssr_stride: 1.0,
ssr_fade_start: 0.8,
ssr_fade_end: 1.0,
ssr_intensity: 1.0,
ambient_light: [0.1, 0.1, 0.1, 1.0],
pbr_debug_mode: PbrDebugMode::None,
texture_debug_stripes: false,
texture_debug_stripes_speed: 100.0,
fxaa_enabled: true,
day_night: DayNightState::default(),
ibl_blend_factor: 0.0,
mesh_lod_chains: Vec::new(),
}
}
}