use glam::Vec3;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LightType {
Ambient,
Directional,
Point,
Spot,
}
#[derive(Debug, Clone, Copy)]
pub struct Attenuation {
pub constant: f32,
pub linear: f32,
pub quadratic: f32,
}
impl Attenuation {
pub fn new(constant: f32, linear: f32, quadratic: f32) -> Self {
Self {
constant,
linear,
quadratic,
}
}
pub fn range_50() -> Self {
Self::new(1.0, 0.09, 0.032)
}
pub fn range_20() -> Self {
Self::new(1.0, 0.22, 0.20)
}
pub fn range_100() -> Self {
Self::new(1.0, 0.045, 0.0075)
}
pub fn none() -> Self {
Self::new(1.0, 0.0, 0.0)
}
pub fn to_array(&self) -> [f32; 3] {
[self.constant, self.linear, self.quadratic]
}
}
impl Default for Attenuation {
fn default() -> Self {
Self::range_50()
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct LightUniforms {
pub direction_or_position: [f32; 4],
pub color_intensity: [f32; 4],
pub attenuation: [f32; 4],
}
pub trait Light {
fn light_type(&self) -> LightType;
fn uniforms(&self) -> LightUniforms;
}
#[derive(Debug, Clone)]
pub struct AmbientLight {
pub intensity: f32,
pub color: [f32; 3],
}
impl AmbientLight {
pub fn new(intensity: f32, color: [f32; 3]) -> Self {
Self { intensity, color }
}
pub fn white(intensity: f32) -> Self {
Self::new(intensity, [1.0, 1.0, 1.0])
}
}
impl Light for AmbientLight {
fn light_type(&self) -> LightType {
LightType::Ambient
}
fn uniforms(&self) -> LightUniforms {
LightUniforms {
direction_or_position: [0.0, 0.0, 0.0, 0.0],
color_intensity: [self.color[0], self.color[1], self.color[2], self.intensity],
attenuation: [0.0, 0.0, 0.0, 0.0],
}
}
}
impl Default for AmbientLight {
fn default() -> Self {
Self::white(0.3)
}
}
#[derive(Debug, Clone)]
pub struct DirectionalLight {
pub intensity: f32,
pub color: [f32; 3],
pub direction: Vec3,
}
impl DirectionalLight {
pub fn new(intensity: f32, color: [f32; 3], direction: Vec3) -> Self {
Self {
intensity,
color,
direction: direction.normalize(),
}
}
pub fn white(intensity: f32, direction: Vec3) -> Self {
Self::new(intensity, [1.0, 1.0, 1.0], direction)
}
}
impl Light for DirectionalLight {
fn light_type(&self) -> LightType {
LightType::Directional
}
fn uniforms(&self) -> LightUniforms {
LightUniforms {
direction_or_position: [self.direction.x, self.direction.y, self.direction.z, 0.0],
color_intensity: [self.color[0], self.color[1], self.color[2], self.intensity],
attenuation: [0.0, 0.0, 0.0, 0.0],
}
}
}
impl Default for DirectionalLight {
fn default() -> Self {
Self::white(1.0, Vec3::new(-0.3, -1.0, -0.5))
}
}
#[derive(Debug, Clone)]
pub struct PointLight {
pub intensity: f32,
pub color: [f32; 3],
pub position: Vec3,
pub attenuation: [f32; 3],
}
impl PointLight {
pub fn new(intensity: f32, color: [f32; 3], position: Vec3, attenuation: [f32; 3]) -> Self {
Self {
intensity,
color,
position,
attenuation,
}
}
pub fn white(intensity: f32, position: Vec3) -> Self {
Self::new(intensity, [1.0, 1.0, 1.0], position, [1.0, 0.09, 0.032])
}
}
impl Light for PointLight {
fn light_type(&self) -> LightType {
LightType::Point
}
fn uniforms(&self) -> LightUniforms {
LightUniforms {
direction_or_position: [self.position.x, self.position.y, self.position.z, 1.0],
color_intensity: [self.color[0], self.color[1], self.color[2], self.intensity],
attenuation: [
self.attenuation[0],
self.attenuation[1],
self.attenuation[2],
0.0,
],
}
}
}
impl Default for PointLight {
fn default() -> Self {
Self::white(1.0, Vec3::new(0.0, 2.0, 0.0))
}
}
#[derive(Debug, Clone)]
pub struct SpotLight {
pub intensity: f32,
pub color: [f32; 3],
pub position: Vec3,
pub direction: Vec3,
pub inner_angle: f32,
pub outer_angle: f32,
pub attenuation: Attenuation,
}
impl SpotLight {
pub fn new(
intensity: f32,
color: [f32; 3],
position: Vec3,
direction: Vec3,
inner_angle: f32,
outer_angle: f32,
attenuation: Attenuation,
) -> Self {
Self {
intensity,
color,
position,
direction: direction.normalize(),
inner_angle,
outer_angle,
attenuation,
}
}
pub fn white(intensity: f32, position: Vec3, direction: Vec3) -> Self {
Self::new(
intensity,
[1.0, 1.0, 1.0],
position,
direction,
std::f32::consts::PI / 6.0, std::f32::consts::PI / 4.0, Attenuation::default(),
)
}
pub fn with_cone(
intensity: f32,
color: [f32; 3],
position: Vec3,
direction: Vec3,
inner_degrees: f32,
outer_degrees: f32,
) -> Self {
Self::new(
intensity,
color,
position,
direction,
inner_degrees.to_radians(),
outer_degrees.to_radians(),
Attenuation::default(),
)
}
}
impl Light for SpotLight {
fn light_type(&self) -> LightType {
LightType::Spot
}
fn uniforms(&self) -> LightUniforms {
LightUniforms {
direction_or_position: [self.position.x, self.position.y, self.position.z, 1.0],
color_intensity: [self.color[0], self.color[1], self.color[2], self.intensity],
attenuation: [
self.attenuation.constant,
self.attenuation.linear,
self.attenuation.quadratic,
0.0,
],
}
}
}
impl Default for SpotLight {
fn default() -> Self {
Self::white(1.0, Vec3::new(0.0, 5.0, 0.0), Vec3::new(0.0, -1.0, 0.0))
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct SpotLightUniform {
pub position: [f32; 4],
pub direction: [f32; 4],
pub color_intensity: [f32; 4],
pub cone_attenuation: [f32; 4],
}
impl SpotLight {
pub fn extended_uniforms(&self) -> SpotLightUniform {
SpotLightUniform {
position: [self.position.x, self.position.y, self.position.z, 1.0],
direction: [self.direction.x, self.direction.y, self.direction.z, 0.0],
color_intensity: [self.color[0], self.color[1], self.color[2], self.intensity],
cone_attenuation: [
self.inner_angle.cos(),
self.outer_angle.cos(),
self.attenuation.linear,
self.attenuation.quadratic,
],
}
}
}