1use crate::gfx::camera::{camera_to_world, view_ray_scale};
13
14use crate::gfx::render_types::RtParams;
15
16const MAX_INTENSITY: f32 = 1.0;
20
21const MIN_DISTANCE: f32 = 1.0;
23const MAX_DISTANCE: f32 = 1000.0;
27
28#[derive(Debug, Clone, Copy, PartialEq)]
32pub struct RtReflectionSettings {
33 pub intensity: f32,
35 pub max_distance: f32,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq)]
49pub struct RtParamsInputs {
50 pub fov_y_radians: f32,
52 pub aspect: f32,
54 pub inv_view_rot: [[f32; 4]; 4],
56 pub cam_pos: [f32; 3],
58 pub sun_dir: [f32; 3],
60 pub sun_color: [f32; 3],
62 pub prefilter_mip_count: f32,
64}
65
66impl RtReflectionSettings {
67 pub fn resolve(intensity: f32, max_distance: f32) -> Self {
69 Self {
70 intensity: intensity.clamp(0.0, MAX_INTENSITY),
71 max_distance: max_distance.clamp(MIN_DISTANCE, MAX_DISTANCE),
72 }
73 }
74
75 pub fn params(&self, inputs: RtParamsInputs) -> RtParams {
78 let RtParamsInputs {
79 fov_y_radians,
80 aspect,
81 inv_view_rot,
82 cam_pos,
83 sun_dir,
84 sun_color,
85 prefilter_mip_count,
86 } = inputs;
87 let inv_view = camera_to_world(inv_view_rot, cam_pos);
88 let (tan_half_fov_y, aspect) = view_ray_scale(fov_y_radians, aspect);
89 RtParams {
90 intensity: self.intensity,
91 max_distance: self.max_distance,
92 tan_half_fov_y,
93 aspect,
94 prefilter_mip_count,
95 _pad0: 0.0,
96 _pad1: 0.0,
97 _pad2: 0.0,
98 cam_pos: [cam_pos[0], cam_pos[1], cam_pos[2], 0.0],
99 sun_dir: [sun_dir[0], sun_dir[1], sun_dir[2], 0.0],
100 sun_color: [sun_color[0], sun_color[1], sun_color[2], 0.0],
101 inv_view,
102 }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::gfx::camera::MIN_ASPECT;
110 use crate::gfx::transform::IDENTITY;
111
112 #[test]
113 fn resolve_clamps_intensity_and_distance() {
114 let s = RtReflectionSettings::resolve(5.0, 1.0e6);
115 assert_eq!(s.intensity, MAX_INTENSITY);
116 assert_eq!(s.max_distance, MAX_DISTANCE);
117
118 let s = RtReflectionSettings::resolve(-2.0, -10.0);
119 assert_eq!(s.intensity, 0.0);
120 assert_eq!(s.max_distance, MIN_DISTANCE);
121 }
122
123 #[test]
124 fn resolve_passes_through_in_range_values() {
125 let s = RtReflectionSettings::resolve(0.7, 60.0);
126 assert_eq!(s.intensity, 0.7);
127 assert_eq!(s.max_distance, 60.0);
128 }
129
130 #[test]
131 fn params_carry_camera_and_sun_inputs() {
132 let s = RtReflectionSettings::resolve(0.8, 40.0);
133 let p = s.params(RtParamsInputs {
134 fov_y_radians: core::f32::consts::FRAC_PI_2,
135 aspect: 1.6,
136 inv_view_rot: IDENTITY,
137 cam_pos: [3.0, 4.0, 5.0],
138 sun_dir: [0.0, 1.0, 0.0],
139 sun_color: [1.0, 0.9, 0.8],
140 prefilter_mip_count: 6.0,
141 });
142 assert_eq!(p.intensity, 0.8);
143 assert_eq!(p.max_distance, 40.0);
144 assert!((p.tan_half_fov_y - 1.0).abs() < 1.0e-5);
146 assert_eq!(p.aspect, 1.6);
147 assert_eq!(p.prefilter_mip_count, 6.0);
148 assert_eq!(p.cam_pos, [3.0, 4.0, 5.0, 0.0]);
149 assert_eq!(p.sun_dir, [0.0, 1.0, 0.0, 0.0]);
150 assert_eq!(p.sun_color, [1.0, 0.9, 0.8, 0.0]);
151 }
152
153 #[test]
154 fn params_assemble_camera_to_world_translation_column() {
155 let s = RtReflectionSettings::resolve(0.8, 40.0);
158 let p = s.params(RtParamsInputs {
159 fov_y_radians: core::f32::consts::FRAC_PI_2,
160 aspect: 1.6,
161 inv_view_rot: IDENTITY,
162 cam_pos: [3.0, 4.0, 5.0],
163 sun_dir: [0.0, 1.0, 0.0],
164 sun_color: [1.0, 1.0, 1.0],
165 prefilter_mip_count: 6.0,
166 });
167 assert_eq!(p.inv_view[3], [3.0, 4.0, 5.0, 1.0]);
168 assert_eq!(p.inv_view[0], [1.0, 0.0, 0.0, 0.0]);
170 }
171
172 #[test]
173 fn params_floor_a_degenerate_aspect() {
174 let s = RtReflectionSettings::resolve(0.7, 40.0);
175 let p = s.params(RtParamsInputs {
176 fov_y_radians: core::f32::consts::FRAC_PI_2,
177 aspect: 0.0,
178 inv_view_rot: IDENTITY,
179 cam_pos: [0.0, 0.0, 0.0],
180 sun_dir: [0.0, 1.0, 0.0],
181 sun_color: [1.0, 1.0, 1.0],
182 prefilter_mip_count: 0.0,
183 });
184 assert!(p.aspect >= MIN_ASPECT);
185 }
186}