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}