use super::{
local_compute::LocalComputeTarget,
worker::{LiveComputeTarget, LocalPreviewScale, WorkerSettings},
};
use crate::bridge::export_thread::DEFAULT_TEMPLATE as DEFAULT_EXPORT_FILENAME_TEMPLATE;
use serde::{Deserialize, Serialize};
pub const DEFAULT_TARGET_SAMPLES: u32 = 256;
pub const DEFAULT_RENDER_WIDTH: u32 = 800;
pub const DEFAULT_RENDER_HEIGHT: u32 = 600;
pub const DEFAULT_MAX_BOUNCES: u32 = 12;
pub const DEFAULT_EXPOSURE: f32 = 1.0;
pub const DEFAULT_INCLUSION_SIGMA_S: f32 = 0.0;
pub const DEFAULT_C_AXIS_OVERRIDE_ENABLED: bool = false;
pub const DEFAULT_C_AXIS_TILT_DEG: f32 = 0.0;
pub const DEFAULT_C_AXIS_AZIMUTH_DEG: f32 = 0.0;
pub const DEFAULT_GIRDLE_FROSTED: bool = false;
pub const DEFAULT_EDGE_ROUNDING_RADIUS: f32 = 0.0;
pub const DEFAULT_STONE_WIDTH_MM: f32 = 0.0;
pub const DEFAULT_LOCAL_PREVIEW_SCALE: LocalPreviewScale = LocalPreviewScale::Off;
pub const DEFAULT_REMOTE_RENDER_SAMPLES: u32 = 512;
pub const DEFAULT_LIVE_COMPUTE_TARGET: LiveComputeTarget = LiveComputeTarget::Both;
pub const DEFAULT_LOCAL_COMPUTE_TARGET: LocalComputeTarget = LocalComputeTarget::CpuGpu;
pub const DEFAULT_LIGHT_YAW_DEG: f32 = 48.0;
pub const DEFAULT_LIGHT_PITCH_DEG: f32 = 54.0;
pub const DEFAULT_LIGHTING_RIG: &str = "Gem Studio Ring Lights";
pub const DEFAULT_CAMERA_YAW: f32 = 0.60;
pub const DEFAULT_CAMERA_PITCH: f32 = 0.45;
pub const DEFAULT_CAMERA_DISTANCE: f32 = 2.4;
pub const DEFAULT_MATERIAL: &str = "Diamond";
pub const DEFAULT_PREVIEW_SIZE: u32 = 160;
pub const DEFAULT_PREVIEW_SPP: u32 = 256;
pub const DEFAULT_SOLID_VIEW_MODE: u8 = 0;
pub const DEFAULT_EDITOR_AUTO_SOLVE_BUDGET_MS: u32 = 300;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct AppSettings {
pub target_samples: u32,
pub render_width: u32,
pub render_height: u32,
pub max_bounces: u32,
pub exposure: f32,
pub light_yaw_deg: f32,
pub light_pitch_deg: f32,
pub lighting_rig: String,
pub camera_yaw: f32,
pub camera_pitch: f32,
pub camera_distance: f32,
pub selected_material: String,
#[serde(default)]
pub remote_workers: Vec<WorkerSettings>,
#[serde(default = "default_denoise_enabled")]
pub denoise_enabled: bool,
pub inclusion_sigma_s: f32,
pub c_axis_override_enabled: bool,
pub c_axis_tilt_deg: f32,
pub c_axis_azimuth_deg: f32,
pub girdle_frosted: bool,
pub edge_rounding_radius: f32,
pub stone_width_mm: f32,
pub local_preview_scale: LocalPreviewScale,
pub remote_render_samples: u32,
#[serde(default)]
pub live_compute_target: LiveComputeTarget,
#[serde(default)]
pub local_compute_target: LocalComputeTarget,
#[serde(default)]
pub env_map_path: String,
#[serde(default = "default_preview_size")]
pub preview_size: u32,
#[serde(default = "default_preview_spp")]
pub preview_spp: u32,
#[serde(default)]
pub export_directory: String,
#[serde(default = "default_export_filename_template")]
pub export_filename_template: String,
#[serde(default)]
pub library_panel_collapsed: bool,
#[serde(default)]
pub solid_view_mode: u8,
#[serde(default = "default_editor_auto_solve_budget_ms")]
pub editor_auto_solve_budget_ms: u32,
}
const fn default_preview_size() -> u32 {
DEFAULT_PREVIEW_SIZE
}
const fn default_preview_spp() -> u32 {
DEFAULT_PREVIEW_SPP
}
const fn default_denoise_enabled() -> bool {
true
}
const fn default_editor_auto_solve_budget_ms() -> u32 {
DEFAULT_EDITOR_AUTO_SOLVE_BUDGET_MS
}
fn default_export_filename_template() -> String {
DEFAULT_EXPORT_FILENAME_TEMPLATE.to_string()
}
impl Default for AppSettings {
fn default() -> Self {
Self {
target_samples: DEFAULT_TARGET_SAMPLES,
render_width: DEFAULT_RENDER_WIDTH,
render_height: DEFAULT_RENDER_HEIGHT,
max_bounces: DEFAULT_MAX_BOUNCES,
exposure: DEFAULT_EXPOSURE,
light_yaw_deg: DEFAULT_LIGHT_YAW_DEG,
light_pitch_deg: DEFAULT_LIGHT_PITCH_DEG,
lighting_rig: DEFAULT_LIGHTING_RIG.to_string(),
camera_yaw: DEFAULT_CAMERA_YAW,
camera_pitch: DEFAULT_CAMERA_PITCH,
camera_distance: DEFAULT_CAMERA_DISTANCE,
selected_material: DEFAULT_MATERIAL.to_string(),
remote_workers: Vec::new(),
denoise_enabled: true,
inclusion_sigma_s: DEFAULT_INCLUSION_SIGMA_S,
c_axis_override_enabled: DEFAULT_C_AXIS_OVERRIDE_ENABLED,
c_axis_tilt_deg: DEFAULT_C_AXIS_TILT_DEG,
c_axis_azimuth_deg: DEFAULT_C_AXIS_AZIMUTH_DEG,
girdle_frosted: DEFAULT_GIRDLE_FROSTED,
edge_rounding_radius: DEFAULT_EDGE_ROUNDING_RADIUS,
stone_width_mm: DEFAULT_STONE_WIDTH_MM,
local_preview_scale: DEFAULT_LOCAL_PREVIEW_SCALE,
remote_render_samples: DEFAULT_REMOTE_RENDER_SAMPLES,
live_compute_target: DEFAULT_LIVE_COMPUTE_TARGET,
local_compute_target: DEFAULT_LOCAL_COMPUTE_TARGET,
env_map_path: String::new(),
preview_size: DEFAULT_PREVIEW_SIZE,
preview_spp: DEFAULT_PREVIEW_SPP,
export_directory: String::new(),
export_filename_template: DEFAULT_EXPORT_FILENAME_TEMPLATE.to_string(),
library_panel_collapsed: false,
solid_view_mode: DEFAULT_SOLID_VIEW_MODE,
editor_auto_solve_budget_ms: DEFAULT_EDITOR_AUTO_SOLVE_BUDGET_MS,
}
}
}
impl AppSettings {
pub fn add_worker(&mut self, worker: WorkerSettings) {
self.remote_workers.push(worker);
}
pub fn update_worker(&mut self, index: usize, worker: WorkerSettings) -> Result<(), String> {
let slot = self
.remote_workers
.get_mut(index)
.ok_or_else(|| format!("No worker at index {index}."))?;
*slot = worker;
Ok(())
}
pub fn remove_worker(&mut self, index: usize) -> Result<(), String> {
if index >= self.remote_workers.len() {
return Err(format!("No worker at index {index}."));
}
self.remote_workers.remove(index);
Ok(())
}
}