use crate::gfx::camera::view_ray_scale;
use crate::gfx::render_types::SsgiParams;
const MAX_INTENSITY: f32 = 4.0;
const MIN_DISTANCE: f32 = 0.5;
const MAX_DISTANCE: f32 = 100.0;
#[cfg(test)]
pub(crate) const DEFAULT_RAYS: u32 = crate::components::DEFAULT_SSGI_RAYS;
const MIN_RAYS: u32 = 1;
const MAX_RAYS: u32 = 32;
#[cfg(test)]
pub(crate) const DEFAULT_STEPS: u32 = crate::components::DEFAULT_SSGI_STEPS;
const MIN_STEPS: u32 = 1;
const MAX_STEPS: u32 = 64;
const THICKNESS_SCALE: f32 = 2.0;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SsgiSettings {
pub intensity: f32,
pub max_distance: f32,
pub rays: u32,
pub steps: u32,
pub gi_scale: u32,
}
impl SsgiSettings {
pub fn resolve(
intensity: f32,
max_distance: f32,
rays: u32,
steps: u32,
gi_scale: u32,
) -> Self {
Self {
intensity: intensity.clamp(0.0, MAX_INTENSITY),
max_distance: max_distance.clamp(MIN_DISTANCE, MAX_DISTANCE),
rays: rays.clamp(MIN_RAYS, MAX_RAYS),
steps: steps.clamp(MIN_STEPS, MAX_STEPS),
gi_scale: gi_scale.max(1),
}
}
pub fn gi_dimensions(&self, render_w: u32, render_h: u32) -> (u32, u32) {
(
(render_w / self.gi_scale).max(1),
(render_h / self.gi_scale).max(1),
)
}
pub fn params(&self, fov_y_radians: f32, aspect: f32) -> SsgiParams {
let stride = self.max_distance / self.steps as f32;
let (tan_half_fov_y, aspect) = view_ray_scale(fov_y_radians, aspect);
SsgiParams {
intensity: self.intensity,
max_distance: self.max_distance,
tan_half_fov_y,
aspect,
stride,
thickness: stride * THICKNESS_SCALE,
rays: self.rays as f32,
steps: self.steps as f32,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::gfx::camera::MIN_ASPECT;
#[test]
fn resolve_clamps_intensity_and_distance() {
let s = SsgiSettings::resolve(9.0, 1.0e6, DEFAULT_RAYS, DEFAULT_STEPS, 1);
assert_eq!(s.intensity, MAX_INTENSITY);
assert_eq!(s.max_distance, MAX_DISTANCE);
let s = SsgiSettings::resolve(-2.0, -10.0, DEFAULT_RAYS, DEFAULT_STEPS, 1);
assert_eq!(s.intensity, 0.0);
assert_eq!(s.max_distance, MIN_DISTANCE);
}
#[test]
fn resolve_passes_through_in_range_values() {
let s = SsgiSettings::resolve(0.6, 8.0, DEFAULT_RAYS, DEFAULT_STEPS, 2);
assert_eq!(s.intensity, 0.6);
assert_eq!(s.max_distance, 8.0);
assert_eq!(s.rays, DEFAULT_RAYS);
assert_eq!(s.steps, DEFAULT_STEPS);
assert_eq!(s.gi_scale, 2);
}
#[test]
fn resolve_clamps_rays_steps_and_scale() {
let s = SsgiSettings::resolve(0.6, 8.0, 9999, 9999, 0);
assert_eq!(s.rays, MAX_RAYS);
assert_eq!(s.steps, MAX_STEPS);
assert_eq!(s.gi_scale, 1);
let s = SsgiSettings::resolve(0.6, 8.0, 0, 0, 4);
assert_eq!(s.rays, MIN_RAYS);
assert_eq!(s.steps, MIN_STEPS);
assert_eq!(s.gi_scale, 4);
}
#[test]
fn gi_dimensions_divide_by_scale_and_floor_at_one() {
let full = SsgiSettings::resolve(0.6, 8.0, DEFAULT_RAYS, DEFAULT_STEPS, 1);
assert_eq!(full.gi_dimensions(1920, 1080), (1920, 1080));
let half = SsgiSettings::resolve(0.6, 8.0, DEFAULT_RAYS, DEFAULT_STEPS, 2);
assert_eq!(half.gi_dimensions(1920, 1080), (960, 540));
assert_eq!(half.gi_dimensions(1, 1), (1, 1));
}
#[test]
fn params_derive_stride_and_thickness_from_configured_steps() {
let s = SsgiSettings::resolve(0.6, 12.0, DEFAULT_RAYS, DEFAULT_STEPS, 1);
let p = s.params(core::f32::consts::FRAC_PI_2, 1.6);
assert!((p.stride - 1.0).abs() < 1.0e-5);
assert!((p.thickness - THICKNESS_SCALE).abs() < 1.0e-5);
assert!((p.tan_half_fov_y - 1.0).abs() < 1.0e-5);
assert_eq!(p.aspect, 1.6);
assert_eq!(p.rays, DEFAULT_RAYS as f32);
assert_eq!(p.steps, DEFAULT_STEPS as f32);
let s = SsgiSettings::resolve(0.6, 12.0, DEFAULT_RAYS, 6, 1);
let p = s.params(core::f32::consts::FRAC_PI_2, 1.6);
assert!((p.stride - 2.0).abs() < 1.0e-5);
}
#[test]
fn params_floor_a_degenerate_aspect() {
let s = SsgiSettings::resolve(0.6, 8.0, DEFAULT_RAYS, DEFAULT_STEPS, 1);
let p = s.params(core::f32::consts::FRAC_PI_2, 0.0);
assert!(p.aspect >= MIN_ASPECT);
}
}