gloss_renderer/
config.rs

1use config::{Config as ConfigLib, File};
2use log::LevelFilter;
3use nalgebra as na;
4use std::{collections::HashMap, path::Path};
5
6use crate::components::ConfigChanges;
7use gloss_utils::{config::MyTomlFile, convert_enum_into};
8
9// Things marked as optional will be sometimes automatically filled depending on
10// the scene scale or other factors known at runtime
11#[derive(Clone, serde::Deserialize, Debug)]
12pub struct Config {
13    pub core: CoreConfig,
14    pub render: RenderConfig,
15    pub scene: SceneConfig,
16    is_concrete: Option<bool>, // Some configs are set to "auto", when they are made concrete, this bool gets set to true
17    is_consumed: Option<bool>, // Using the config to create a scene will set this to true so that we don't rerun it
18}
19
20#[derive(Clone, serde::Deserialize, Debug)]
21#[allow(unused)]
22#[allow(clippy::struct_excessive_bools)]
23pub struct CoreConfig {
24    pub enable_gui: bool,
25    pub gui_start_hidden: bool,
26    pub auto_add_floor: bool,
27    pub floor_type: FloorType,
28    pub floor_scale: Option<f32>,
29    pub floor_origin: Option<na::Point3<f32>>,
30    pub floor_texture: FloorTexture,
31    pub floor_uv_scale: Option<f32>,
32    pub floor_grid_line_width: f32,
33    pub canvas_id: Option<String>,
34    pub auto_create_logger: bool,
35    pub log_level: LogLevel,
36    pub log_level_caps: HashMap<String, LogLevel>,
37    pub enable_memory_profiling_callstacks: bool,
38}
39
40#[derive(Clone, serde::Deserialize, Debug)]
41#[allow(unused)]
42pub struct RenderConfig {
43    pub ambient_factor: f32,
44    pub environment_factor: f32,
45    pub bg_color: na::Vector4<f32>,
46    pub enable_distance_fade: Option<bool>,
47    pub distance_fade_center: Option<na::Point3<f32>>,
48    pub distance_fade_start: Option<f32>,
49    pub distance_fade_end: Option<f32>,
50    // Color grading, applied before tonemapping
51    pub apply_lighting: bool,
52    pub saturation: f32,
53    pub gamma: f32,
54    pub exposure: f32,
55    pub shadow_filter_method: ShadowFilteringMethod,
56    pub msaa_nr_samples: u32,
57    pub preallocated_staging_buffer_bytes: u32,
58    pub offscreen_color_float_tex: bool,
59}
60
61#[derive(Clone, serde::Deserialize, Debug)]
62#[allow(unused)]
63pub struct SceneConfig {
64    pub cam: CamConfig,
65    pub lights: Vec<LightConfig>,
66}
67
68#[derive(Clone, serde::Deserialize, Debug)]
69#[allow(unused)]
70pub struct CamConfig {
71    pub position: Option<na::Point3<f32>>,
72    pub lookat: Option<na::Point3<f32>>,
73    pub fovy: f32,
74    pub near: Option<f32>,
75    pub far: Option<f32>,
76    pub limit_max_dist: Option<f32>,
77    pub limit_max_vertical_angle: Option<f32>,
78    pub limit_min_vertical_angle: Option<f32>,
79}
80
81#[derive(Clone, serde::Deserialize, Debug)]
82#[allow(unused)]
83pub struct LightConfig {
84    pub position: Option<na::Point3<f32>>,
85    pub lookat: Option<na::Point3<f32>>,
86    pub fovy: f32, //radians
87    pub near: Option<f32>,
88    pub far: Option<f32>,
89    pub color: na::Vector3<f32>,
90    pub intensity: Option<f32>,
91    pub range: Option<f32>,
92    pub radius: Option<f32>,
93    pub shadow_res: Option<u32>,
94    pub shadow_bias_fixed: Option<f32>,
95    pub shadow_bias: Option<f32>,
96    pub shadow_bias_normal: Option<f32>,
97}
98
99#[derive(Debug, Copy, Clone, serde::Deserialize, PartialEq, Eq)]
100#[serde(rename_all = "lowercase")]
101pub enum ShadowFilteringMethod {
102    /// Hardware 2x2.
103    ///
104    /// Fast but poor quality.
105    Hardware2x2 = 0,
106    /// Method by Ignacio CastaƱo for The Witness using 9 samples and smart
107    /// filtering to achieve the same as a regular 5x5 filter kernel.
108    ///
109    /// Good quality, good performance.
110    Castano13,
111}
112
113#[derive(Debug, Copy, Clone, serde::Deserialize, PartialEq, Eq)]
114#[serde(rename_all = "lowercase")]
115pub enum FloorType {
116    Solid = 0,
117    Grid,
118}
119
120#[derive(Debug, Copy, Clone, serde::Deserialize, PartialEq, Eq)]
121#[serde(rename_all = "lowercase")]
122pub enum FloorTexture {
123    None = 0,
124    Checkerboard,
125}
126
127#[derive(Debug, Copy, Clone, serde::Deserialize, PartialEq, Eq)]
128#[serde(rename_all = "lowercase")]
129pub enum LogLevel {
130    /// A level lower than all log levels.
131    Off,
132    /// Corresponds to the `Error` log level.
133    Error,
134    /// Corresponds to the `Warn` log level.
135    Warn,
136    /// Corresponds to the `Info` log level.
137    Info,
138    /// Corresponds to the `Debug` log level.
139    Debug,
140    /// Corresponds to the `Trace` log level.
141    Trace,
142}
143// https://stackoverflow.com/questions/59984712/rust-macro-to-convert-between-identical-enums
144convert_enum_into!(LogLevel, LevelFilter, Off, Error, Warn, Info, Debug, Trace,);
145
146impl Default for Config {
147    fn default() -> Config {
148        Config::new(None)
149    }
150}
151
152impl Config {
153    /// # Panics
154    /// Will panic if the path is not valid unicode
155    pub fn new(config_path: Option<&str>) -> crate::config::Config {
156        let default_file = File::from_str(include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/config/default.toml")), MyTomlFile);
157
158        // Provide the sources and build the config object:
159        let mut builder = ConfigLib::builder().add_source(default_file.required(true));
160
161        if let Some(config_path) = config_path {
162            // Read the config file path either absolute or relative
163            let config_path_abs = if Path::new(config_path).is_relative() {
164                Path::new(env!("CARGO_MANIFEST_DIR")).join(config_path)
165            } else {
166                Path::new(config_path).to_path_buf()
167            };
168            let config_file = File::new(config_path_abs.to_str().unwrap(), MyTomlFile);
169            builder = builder.add_source(config_file.required(true));
170        }
171
172        let settings = builder.build().unwrap();
173
174        settings.try_deserialize().unwrap()
175    }
176
177    /// # Panics
178    /// Will panic if the path is not valid unicode
179    pub fn new_from_str(config_content: &str) -> crate::config::Config {
180        let default_file = File::from_str(include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/config/default.toml")), MyTomlFile);
181
182        let config_file = File::from_str(config_content, MyTomlFile);
183
184        // Provide the sources and build the config object:
185        let builder = ConfigLib::builder()
186            .add_source(default_file.required(true))
187            .add_source(config_file.required(true));
188
189        let settings = builder.build().unwrap();
190
191        settings.try_deserialize().unwrap()
192    }
193    pub fn is_concrete(&self) -> bool {
194        self.is_concrete.is_some()
195    }
196    pub fn set_concrete(&mut self) {
197        self.is_concrete = Some(true);
198    }
199    pub fn is_consumed(&self) -> bool {
200        self.is_consumed.is_some()
201    }
202    pub fn set_consumed(&mut self) {
203        self.is_consumed = Some(true);
204    }
205
206    pub fn apply_deltas(&mut self, changes: &ConfigChanges) {
207        if let Some(ref mut p) = self.render.distance_fade_center {
208            p.clone_from(&changes.new_distance_fade_center);
209        }
210    }
211}