comfy_core/
lighting.rs

1use crate::*;
2
3static LIGHTS: Lazy<AtomicRefCell<LightingState>> =
4    Lazy::new(|| AtomicRefCell::new(LightingState::default()));
5
6#[derive(Default)]
7pub struct LightingState {
8    pub lights: Vec<Light>,
9}
10
11impl LightingState {
12    pub fn begin_frame() {
13        LIGHTS.borrow_mut().lights.clear();
14    }
15
16    pub fn take_lights() -> Vec<Light> {
17        LIGHTS.borrow_mut().lights.clone()
18    }
19}
20
21pub fn draw_light(light: Light) {
22    LIGHTS.borrow_mut().lights.push(light);
23}
24
25pub fn light_count() -> usize {
26    LIGHTS.borrow().lights.len()
27}
28
29pub struct PointLight {
30    pub radius: f32,
31    pub radius_mod: f32,
32    pub strength: f32,
33    pub strength_mod: f32,
34    pub color: Color,
35}
36
37#[derive(Copy, Clone, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)]
38#[repr(C)]
39pub struct Light {
40    pub color: Color,
41    pub world_position: [f32; 2],
42    pub screen_position: [f32; 2],
43    pub radius: f32,
44    pub strength: f32,
45    pub _padding: [f32; 2],
46}
47
48impl Light {
49    pub fn simple(world_position: Vec2, radius: f32, strength: f32) -> Self {
50        Self {
51            color: WHITE,
52            world_position: world_position.as_array(),
53            screen_position: Vec2::ZERO.as_array(),
54            radius,
55            strength,
56            _padding: [0.0; 2],
57        }
58    }
59}
60
61pub const MAX_LIGHTS: usize = 128;
62
63#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
64#[repr(C)]
65pub struct LightUniform {
66    pub lights: [Light; 128],
67    pub num_lights: i32,
68    _padding: [f32; 3],
69}
70
71impl Default for LightUniform {
72    fn default() -> Self {
73        Self {
74            lights: [Light::default(); 128],
75            num_lights: 0,
76            _padding: [0.0; 3],
77        }
78    }
79}
80
81#[repr(C)]
82#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
83pub struct GlobalLightingParams {
84    pub ambient_light_color: [f32; 4],
85    pub ambient_light_intensity: f32,
86    pub quadratic_falloff: u32,
87    pub lighting_enabled: u32,
88    pub shadow_strength: f32,
89    pub fog_color: [f32; 4],
90    pub film_grain_strength: f32,
91    pub noise_strength: f32,
92    pub light_blending_mode: u32,
93    pub global_light_intensity: f32,
94    pub global_light_color: [f32; 4],
95    pub vignette_color: [f32; 4],
96    pub vignette_intensity: f32,
97    pub vignette_radius: f32,
98    pub shake_amount: f32,
99    pub time: f32,
100    pub color_balance: [f32; 4],
101    pub global_brightness: f32,
102    pub debug_visualization: u32,
103    pub gamma_correction: f32,
104    pub use_lut: u32,
105
106    /// Exposure value (EV) offset, measured in stops.
107    pub exposure: f32,
108
109    /// Non-linear luminance adjustment applied before tonemapping. y = pow(x, gamma)
110    pub gamma: f32,
111
112    /// Saturation adjustment applied before tonemapping.
113    /// Values below 1.0 desaturate, with a value of 0.0 resulting in a grayscale image
114    /// with luminance defined by ITU-R BT.709.
115    /// Values above 1.0 increase saturation.
116    pub pre_saturation: f32,
117
118    /// Saturation adjustment applied after tonemapping.
119    /// Values below 1.0 desaturate, with a value of 0.0 resulting in a grayscale image
120    /// with luminance defined by ITU-R BT.709
121    /// Values above 1.0 increase saturation.
122    pub post_saturation: f32,
123
124    pub resolution: [f32; 2],
125    pub chromatic_aberration: f32,
126    pub bloom_threshold: f32,
127    pub bloom_lerp: f32,
128    pub bloom_gamma: f32,
129    pub _padding: [f32; 2],
130}
131
132impl Default for GlobalLightingParams {
133    fn default() -> Self {
134        GlobalLightingParams {
135            ambient_light_color: [1.0, 1.0, 1.0, 1.0],
136            ambient_light_intensity: 1.0,
137            quadratic_falloff: 0,
138            lighting_enabled: 1,
139            shadow_strength: 1.0,
140            fog_color: [0.5, 0.5, 0.5, 1.0],
141            film_grain_strength: 0.5,
142            noise_strength: 0.2,
143            light_blending_mode: 0,
144            global_light_intensity: 1.0,
145            global_light_color: [1.0, 1.0, 1.0, 1.0],
146            vignette_color: [0.0, 0.0, 0.0, 1.0],
147            vignette_intensity: 0.5,
148            vignette_radius: 1.0,
149            color_balance: [1.0, 1.0, 1.0, 1.0],
150            global_brightness: 1.0,
151            debug_visualization: 0,
152            gamma_correction: 1.0,
153            use_lut: 0,
154            shake_amount: 0.0,
155            time: 0.0,
156
157            exposure: 0.0,
158            gamma: 1.0,
159            pre_saturation: 1.0,
160            post_saturation: 1.0,
161
162            resolution: [1920.0, 1080.0],
163            chromatic_aberration: 0.0,
164            bloom_threshold: 0.8,
165            bloom_lerp: 0.3,
166            bloom_gamma: 1.0,
167            _padding: [0.0; 2],
168        }
169    }
170}
171
172pub fn lighting_ui(
173    params: &mut GlobalLightingParams,
174    ui: &mut egui::Ui,
175) -> bool {
176    let mut changed = false;
177
178    changed |= field_editor(
179        ui,
180        "Chromatic Aberration",
181        &mut params.chromatic_aberration,
182    );
183
184    changed |= field_editor(ui, "Bloom Threshold", &mut params.bloom_threshold);
185    changed |= field_editor(ui, "Bloom Lerp", &mut params.bloom_lerp);
186    changed |= field_editor(ui, "Bloom Gamma", &mut params.bloom_gamma);
187
188    changed |= field_editor(ui, "Exposure", &mut params.exposure);
189    changed |= field_editor(ui, "Gamma", &mut params.gamma);
190    changed |= field_editor(ui, "Pre-Saturation", &mut params.pre_saturation);
191    changed |= field_editor(ui, "Post-Saturation", &mut params.post_saturation);
192
193    changed |= field_editor(ui, "Shake Amount", &mut params.shake_amount);
194
195    changed |= field_editor_vec4(
196        ui,
197        "Ambient Light Color",
198        &mut params.ambient_light_color,
199    );
200    changed |= field_editor(
201        ui,
202        "Ambient Light Intensity",
203        &mut params.ambient_light_intensity,
204    );
205    changed |=
206        field_editor(ui, "Quadratic Falloff", &mut params.quadratic_falloff);
207
208    changed |=
209        field_editor(ui, "Light Blend Mode", &mut params.light_blending_mode);
210    changed |= field_editor(
211        ui,
212        "Global Light Intensity",
213        &mut params.global_light_intensity,
214    );
215    changed |= field_editor_vec4(
216        ui,
217        "Global Light Color",
218        &mut params.global_light_color,
219    );
220
221    changed |=
222        field_editor(ui, "Lighting Enabled", &mut params.lighting_enabled);
223    changed |= field_editor(ui, "Shadow Strength", &mut params.shadow_strength);
224    changed |= field_editor_vec4(ui, "Fog Color", &mut params.fog_color);
225    changed |= field_editor(
226        ui,
227        "Film Grain Strength",
228        &mut params.film_grain_strength,
229    );
230    changed |= field_editor(ui, "Noise Strength", &mut params.noise_strength);
231    changed |= field_editor(
232        ui,
233        "Debug Visualization",
234        &mut params.debug_visualization,
235    );
236
237    changed |=
238        field_editor_vec4(ui, "Vignette Color", &mut params.vignette_color);
239    changed |=
240        field_editor(ui, "Vignette Intensity", &mut params.vignette_intensity);
241    changed |= field_editor(ui, "Vignette Radius", &mut params.vignette_radius);
242    changed |=
243        field_editor_vec4(ui, "Color Balance", &mut params.color_balance);
244    changed |=
245        field_editor(ui, "Global Brightness", &mut params.global_brightness);
246    changed |=
247        field_editor(ui, "Gamma Correction", &mut params.gamma_correction);
248    changed |= field_editor(ui, "Use LUT", &mut params.use_lut);
249
250    changed
251}
252
253fn field_editor_vec4(
254    ui: &mut egui::Ui,
255    label: &str,
256    field: &mut [f32; 4],
257) -> bool {
258    let mut changed = false;
259    ui.horizontal(|ui| {
260        ui.label(label);
261        ui.vertical(|ui| {
262            let labels = ["X", "Y", "Z", "W"];
263            ui.horizontal(|ui| {
264                for (i, value) in field.iter_mut().enumerate() {
265                    let value_changed = ui
266                        .add(
267                            egui::DragValue::new(value)
268                                .speed(0.01)
269                                .clamp_range(0.0..=1.0)
270                                .prefix(labels[i]),
271                        )
272                        .changed();
273                    changed |= value_changed;
274                }
275            });
276        });
277    });
278    changed
279}
280
281fn field_editor<T>(ui: &mut egui::Ui, label: &str, field: &mut T) -> bool
282where T: Clone + PartialEq + 'static + egui::emath::Numeric {
283    let mut changed = false;
284
285    ui.horizontal(|ui| {
286        ui.label(label);
287        changed = ui.add(egui::DragValue::new(field).speed(0.01)).changed();
288    });
289
290    changed
291}