Skip to main content

dreamwell_engine/
lighting.rs

1//! Light descriptor types shared between engine and GPU crates.
2
3use serde::{Deserialize, Serialize};
4
5/// Directional light (sun, moon). Infinite range, parallel rays.
6#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
7pub struct DirectionalLightDesc {
8    /// Light direction (normalized, points toward light source).
9    pub direction: [f32; 3],
10    /// Light color (RGB linear).
11    pub color: [f32; 3],
12    /// Illuminance in lux.
13    pub intensity_lux: f32,
14}
15
16impl Default for DirectionalLightDesc {
17    fn default() -> Self {
18        Self {
19            direction: [0.0, -1.0, 0.0],
20            color: [1.0, 1.0, 1.0],
21            intensity_lux: 100_000.0,
22        }
23    }
24}
25
26/// Point light (omni-directional, finite range).
27#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
28pub struct PointLightDesc {
29    /// World-space position.
30    pub position: [f32; 3],
31    /// Light color (RGB linear).
32    pub color: [f32; 3],
33    /// Luminous intensity in lumens.
34    pub intensity_lumens: f32,
35    /// Attenuation range in world units. Light contribution is zero beyond this.
36    pub range: f32,
37}
38
39impl Default for PointLightDesc {
40    fn default() -> Self {
41        Self {
42            position: [0.0, 3.0, 0.0],
43            color: [1.0, 1.0, 1.0],
44            intensity_lumens: 800.0,
45            range: 10.0,
46        }
47    }
48}
49
50/// Spot light (cone-shaped, finite range).
51#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
52pub struct SpotLightDesc {
53    /// World-space position.
54    pub position: [f32; 3],
55    /// Cone direction (normalized).
56    pub direction: [f32; 3],
57    /// Light color (RGB linear).
58    pub color: [f32; 3],
59    /// Luminous intensity in lumens.
60    pub intensity_lumens: f32,
61    /// Attenuation range in world units.
62    pub range: f32,
63    /// Cosine of inner cone half-angle (full intensity inside).
64    pub inner_cos: f32,
65    /// Cosine of outer cone half-angle (zero intensity outside).
66    pub outer_cos: f32,
67}
68
69impl Default for SpotLightDesc {
70    fn default() -> Self {
71        Self {
72            position: [0.0, 3.0, 0.0],
73            direction: [0.0, -1.0, 0.0],
74            color: [1.0, 1.0, 1.0],
75            intensity_lumens: 800.0,
76            range: 10.0,
77            inner_cos: 0.9659, // cos(15 degrees)
78            outer_cos: 0.8660, // cos(30 degrees)
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn directional_default() {
89        let d = DirectionalLightDesc::default();
90        assert_eq!(d.direction, [0.0, -1.0, 0.0]);
91        assert!(d.intensity_lux > 0.0);
92    }
93
94    #[test]
95    fn point_default() {
96        let p = PointLightDesc::default();
97        assert!(p.range > 0.0);
98        assert!(p.intensity_lumens > 0.0);
99    }
100
101    #[test]
102    fn spot_cone_angles() {
103        let s = SpotLightDesc::default();
104        assert!(s.inner_cos > s.outer_cos, "inner cone must be tighter than outer");
105    }
106
107    #[test]
108    fn serde_roundtrip() {
109        let d = DirectionalLightDesc::default();
110        let json = serde_json::to_string(&d).unwrap();
111        let back: DirectionalLightDesc = serde_json::from_str(&json).unwrap();
112        assert_eq!(d, back);
113    }
114}