gemini_engine/view3d/display_mode/
lighting.rs

1use crate::mesh3d::Vec3D;
2
3/// Characters for brightness. The first character is the darkest and the last character is the brightest
4pub const BRIGHTNESS_CHARS: &str = ".,-~:;=!*(%#$@";
5
6/// Type of light, determines how the normal of a face affects its illumination
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub enum LightType {
9    /// Lights the entire scene equally. It's good to have at least one of these so your models don't go completely dark.
10    Ambient,
11
12    /// Lights the scene from a specific direction. A surface facing the specified direction will be lit with the most intensity and a surface facing away from the direction will be lit with the least intensity or no intensity at all
13    Directional {
14        /// The direction the light is pointing
15        direction: Vec3D,
16    },
17
18    /// Light comes from a postion in 3D space. This light type does not currently dissipate over distance, so can be represented with a single position
19    Point {
20        /// The position from which the light emanates
21        position: Vec3D,
22    },
23}
24
25/// A light object used to define a scene's lighting. Used by [`DisplayMode::Illuminated`](super::DisplayMode::Illuminated)
26#[derive(Debug, Clone, Copy, PartialEq)]
27pub struct Light {
28    /// The type of light and the way it affects faces based on their normals
29    pub light_type: LightType,
30    /// The intensity of the light
31    pub intensity: f64,
32}
33
34impl Light {
35    /// Create a new ambient light
36    #[must_use]
37    pub const fn new_ambient(intensity: f64) -> Self {
38        Self {
39            light_type: LightType::Ambient,
40            intensity,
41        }
42    }
43
44    /// Create a new directional light
45    #[must_use]
46    pub const fn new_directional(intensity: f64, direction: Vec3D) -> Self {
47        Self {
48            light_type: LightType::Directional { direction },
49            intensity,
50        }
51    }
52
53    /// Create a new point light
54    #[must_use]
55    pub const fn new_point(intensity: f64, position: Vec3D) -> Self {
56        Self {
57            light_type: LightType::Point { position },
58            intensity,
59        }
60    }
61
62    fn calculate_intensity_for_direction(&self, normal: Vec3D, direction: Vec3D) -> f64 {
63        let n_dot_l = normal.dot(direction);
64        if n_dot_l > 0.0 {
65            self.intensity * n_dot_l / (normal.length() * direction.length())
66        } else {
67            0.0
68        }
69    }
70
71    /// Calculate the intensity of the light as it affects a surface with the given normal
72    #[must_use]
73    pub fn calculate_intensity(&self, point: Vec3D, normal: Vec3D) -> f64 {
74        match self.light_type {
75            LightType::Ambient => self.intensity,
76
77            LightType::Directional { direction } => {
78                self.calculate_intensity_for_direction(normal, direction)
79            }
80
81            LightType::Point { position } => {
82                let direction = point - position;
83
84                self.calculate_intensity_for_direction(normal, direction)
85            }
86        }
87    }
88}