Skip to main content

nightshade/ecs/scene/
lighting.rs

1use serde::{Deserialize, Serialize};
2
3use crate::ecs::camera::{Camera, OrthographicCamera, PerspectiveCamera, Projection};
4use crate::ecs::light::{AreaLightShape, Light, LightType};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub enum SceneLight {
8    Directional {
9        color: [f32; 3],
10        intensity: f32,
11        cast_shadows: bool,
12        shadow_bias: f32,
13        #[serde(default)]
14        shadow_resolution: u32,
15        #[serde(default)]
16        shadow_distance: f32,
17        #[serde(default)]
18        cookie_texture: Option<String>,
19    },
20    Point {
21        color: [f32; 3],
22        intensity: f32,
23        range: f32,
24        cast_shadows: bool,
25        shadow_bias: f32,
26        #[serde(default)]
27        shadow_resolution: u32,
28        #[serde(default)]
29        shadow_distance: f32,
30        #[serde(default)]
31        cookie_texture: Option<String>,
32    },
33    Spot {
34        color: [f32; 3],
35        intensity: f32,
36        range: f32,
37        inner_cone_angle: f32,
38        outer_cone_angle: f32,
39        cast_shadows: bool,
40        shadow_bias: f32,
41        #[serde(default)]
42        shadow_resolution: u32,
43        #[serde(default)]
44        shadow_distance: f32,
45        #[serde(default)]
46        cookie_texture: Option<String>,
47    },
48    Area {
49        color: [f32; 3],
50        intensity: f32,
51        range: f32,
52        #[serde(default)]
53        shape: AreaLightShape,
54        #[serde(default)]
55        width: f32,
56        #[serde(default)]
57        height: f32,
58        #[serde(default)]
59        radius: f32,
60        #[serde(default)]
61        two_sided: bool,
62        cast_shadows: bool,
63        shadow_bias: f32,
64        #[serde(default)]
65        shadow_resolution: u32,
66        #[serde(default)]
67        shadow_distance: f32,
68        #[serde(default)]
69        emissive_texture: Option<String>,
70    },
71}
72
73impl SceneLight {
74    pub fn directional(color: [f32; 3], intensity: f32) -> Self {
75        Self::Directional {
76            color,
77            intensity,
78            cast_shadows: true,
79            shadow_bias: 0.005,
80            shadow_resolution: 0,
81            shadow_distance: 0.0,
82            cookie_texture: None,
83        }
84    }
85
86    pub fn point(color: [f32; 3], intensity: f32, range: f32) -> Self {
87        Self::Point {
88            color,
89            intensity,
90            range,
91            cast_shadows: false,
92            shadow_bias: 0.0,
93            shadow_resolution: 0,
94            shadow_distance: 0.0,
95            cookie_texture: None,
96        }
97    }
98
99    pub fn spot(
100        color: [f32; 3],
101        intensity: f32,
102        range: f32,
103        inner_cone_angle: f32,
104        outer_cone_angle: f32,
105    ) -> Self {
106        Self::Spot {
107            color,
108            intensity,
109            range,
110            inner_cone_angle,
111            outer_cone_angle,
112            cast_shadows: false,
113            shadow_bias: 0.0,
114            shadow_resolution: 0,
115            shadow_distance: 0.0,
116            cookie_texture: None,
117        }
118    }
119
120    pub fn to_light(&self) -> Light {
121        match self {
122            SceneLight::Directional {
123                color,
124                intensity,
125                cast_shadows,
126                shadow_bias,
127                shadow_resolution,
128                shadow_distance,
129                cookie_texture,
130            } => Light {
131                light_type: LightType::Directional,
132                color: nalgebra_glm::Vec3::new(color[0], color[1], color[2]),
133                intensity: *intensity,
134                range: 0.0,
135                inner_cone_angle: 0.0,
136                outer_cone_angle: 0.0,
137                cast_shadows: *cast_shadows,
138                shadow_bias: *shadow_bias,
139                shadow_resolution: *shadow_resolution,
140                shadow_distance: *shadow_distance,
141                cookie_texture: cookie_texture.clone(),
142                ..Default::default()
143            },
144            SceneLight::Point {
145                color,
146                intensity,
147                range,
148                cast_shadows,
149                shadow_bias,
150                shadow_resolution,
151                shadow_distance,
152                cookie_texture,
153            } => Light {
154                light_type: LightType::Point,
155                color: nalgebra_glm::Vec3::new(color[0], color[1], color[2]),
156                intensity: *intensity,
157                range: *range,
158                inner_cone_angle: 0.0,
159                outer_cone_angle: 0.0,
160                cast_shadows: *cast_shadows,
161                shadow_bias: *shadow_bias,
162                shadow_resolution: *shadow_resolution,
163                shadow_distance: *shadow_distance,
164                cookie_texture: cookie_texture.clone(),
165                ..Default::default()
166            },
167            SceneLight::Spot {
168                color,
169                intensity,
170                range,
171                inner_cone_angle,
172                outer_cone_angle,
173                cast_shadows,
174                shadow_bias,
175                shadow_resolution,
176                shadow_distance,
177                cookie_texture,
178            } => Light {
179                light_type: LightType::Spot,
180                color: nalgebra_glm::Vec3::new(color[0], color[1], color[2]),
181                intensity: *intensity,
182                range: *range,
183                inner_cone_angle: *inner_cone_angle,
184                outer_cone_angle: *outer_cone_angle,
185                cast_shadows: *cast_shadows,
186                shadow_bias: *shadow_bias,
187                shadow_resolution: *shadow_resolution,
188                shadow_distance: *shadow_distance,
189                cookie_texture: cookie_texture.clone(),
190                ..Default::default()
191            },
192            SceneLight::Area {
193                color,
194                intensity,
195                range,
196                shape,
197                width,
198                height,
199                radius,
200                two_sided,
201                cast_shadows,
202                shadow_bias,
203                shadow_resolution,
204                shadow_distance,
205                emissive_texture,
206            } => Light {
207                light_type: LightType::Area,
208                color: nalgebra_glm::Vec3::new(color[0], color[1], color[2]),
209                intensity: *intensity,
210                range: *range,
211                cast_shadows: *cast_shadows,
212                shadow_bias: *shadow_bias,
213                shadow_resolution: *shadow_resolution,
214                shadow_distance: *shadow_distance,
215                area_shape: *shape,
216                area_width: *width,
217                area_height: *height,
218                area_radius: *radius,
219                area_two_sided: *two_sided,
220                area_emissive_texture: emissive_texture.clone(),
221                ..Default::default()
222            },
223        }
224    }
225}
226
227impl From<&Light> for SceneLight {
228    fn from(light: &Light) -> Self {
229        let color = [light.color.x, light.color.y, light.color.z];
230        match light.light_type {
231            LightType::Directional => SceneLight::Directional {
232                color,
233                intensity: light.intensity,
234                cast_shadows: light.cast_shadows,
235                shadow_bias: light.shadow_bias,
236                shadow_resolution: light.shadow_resolution,
237                shadow_distance: light.shadow_distance,
238                cookie_texture: light.cookie_texture.clone(),
239            },
240            LightType::Point => SceneLight::Point {
241                color,
242                intensity: light.intensity,
243                range: light.range,
244                cast_shadows: light.cast_shadows,
245                shadow_bias: light.shadow_bias,
246                shadow_resolution: light.shadow_resolution,
247                shadow_distance: light.shadow_distance,
248                cookie_texture: light.cookie_texture.clone(),
249            },
250            LightType::Spot => SceneLight::Spot {
251                color,
252                intensity: light.intensity,
253                range: light.range,
254                inner_cone_angle: light.inner_cone_angle,
255                outer_cone_angle: light.outer_cone_angle,
256                cast_shadows: light.cast_shadows,
257                shadow_bias: light.shadow_bias,
258                shadow_resolution: light.shadow_resolution,
259                shadow_distance: light.shadow_distance,
260                cookie_texture: light.cookie_texture.clone(),
261            },
262            LightType::Area => SceneLight::Area {
263                color,
264                intensity: light.intensity,
265                range: light.range,
266                shape: light.area_shape,
267                width: light.area_width,
268                height: light.area_height,
269                radius: light.area_radius,
270                two_sided: light.area_two_sided,
271                cast_shadows: light.cast_shadows,
272                shadow_bias: light.shadow_bias,
273                shadow_resolution: light.shadow_resolution,
274                shadow_distance: light.shadow_distance,
275                emissive_texture: light.area_emissive_texture.clone(),
276            },
277        }
278    }
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
282pub enum SceneCamera {
283    Perspective {
284        aspect_ratio: Option<f32>,
285        y_fov_rad: f32,
286        z_far: Option<f32>,
287        z_near: f32,
288    },
289    Orthographic {
290        x_mag: f32,
291        y_mag: f32,
292        z_far: f32,
293        z_near: f32,
294    },
295}
296
297impl SceneCamera {
298    pub fn perspective(y_fov_rad: f32, z_near: f32) -> Self {
299        Self::Perspective {
300            aspect_ratio: None,
301            y_fov_rad,
302            z_far: None,
303            z_near,
304        }
305    }
306
307    pub fn orthographic(x_mag: f32, y_mag: f32, z_near: f32, z_far: f32) -> Self {
308        Self::Orthographic {
309            x_mag,
310            y_mag,
311            z_far,
312            z_near,
313        }
314    }
315
316    pub fn to_camera(&self) -> Camera {
317        match self {
318            SceneCamera::Perspective {
319                aspect_ratio,
320                y_fov_rad,
321                z_far,
322                z_near,
323            } => Camera {
324                projection: Projection::Perspective(PerspectiveCamera {
325                    aspect_ratio: *aspect_ratio,
326                    y_fov_rad: *y_fov_rad,
327                    z_far: *z_far,
328                    z_near: *z_near,
329                }),
330                smoothing: None,
331            },
332            SceneCamera::Orthographic {
333                x_mag,
334                y_mag,
335                z_far,
336                z_near,
337            } => Camera {
338                projection: Projection::Orthographic(OrthographicCamera {
339                    x_mag: *x_mag,
340                    y_mag: *y_mag,
341                    z_far: *z_far,
342                    z_near: *z_near,
343                }),
344                smoothing: None,
345            },
346        }
347    }
348}
349
350impl From<&Camera> for SceneCamera {
351    fn from(camera: &Camera) -> Self {
352        match &camera.projection {
353            Projection::Perspective(perspective) => SceneCamera::Perspective {
354                aspect_ratio: perspective.aspect_ratio,
355                y_fov_rad: perspective.y_fov_rad,
356                z_far: perspective.z_far,
357                z_near: perspective.z_near,
358            },
359            Projection::Orthographic(orthographic) => SceneCamera::Orthographic {
360                x_mag: orthographic.x_mag,
361                y_mag: orthographic.y_mag,
362                z_far: orthographic.z_far,
363                z_near: orthographic.z_near,
364            },
365        }
366    }
367}