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