use dotrix_core::{ Color, Globals, Renderer, World };
use dotrix_core::ecs::{ Const, Mut };
use dotrix_core::renderer::UniformBuffer;
use dotrix_math::Vec3;
const MAX_LIGHTS: usize = 10;
pub enum Light {
Ambient {
color: Color,
intensity: f32,
},
Directional {
color: Color,
direction: Vec3,
intensity: f32,
enabled: bool,
},
Simple {
color: Color,
position: Vec3,
intensity: f32,
enabled: bool,
},
Point {
color: Color,
position: Vec3,
intensity: f32,
enabled: bool,
constant: f32,
linear: f32,
quadratic: f32,
},
Spot {
color: Color,
position: Vec3,
direction: Vec3,
intensity: f32,
enabled: bool,
cut_off: f32,
outer_cut_off: f32,
},
}
impl Light {
pub fn ambient() -> Self {
Light::Ambient {
color: Color::white(),
intensity: 0.8,
}
}
pub fn directional() -> Self {
Light::Directional {
enabled: true,
direction: Vec3::new(0.0, 0.0, 0.0),
color: Color::white(),
intensity: 1.0,
}
}
pub fn simple() -> Self {
Light::Simple {
enabled: true,
position: Vec3::new(0.0, 1000.0, 0.0),
color: Color::white(),
intensity: 1.0,
}
}
pub fn point() -> Self {
Light::Point {
enabled: true,
position: Vec3::new(0.0, 0.0, 0.0),
color: Color::white(),
intensity: 1.0,
constant: 1.0,
linear: 0.35,
quadratic: 0.44,
}
}
pub fn spot() -> Self {
Light::Spot {
enabled: true,
position: Vec3::new(0.0, 0.0, 0.0),
direction: Vec3::new(0.0, 0.0, 0.0),
color: Color::white(),
intensity: 1.0,
cut_off: 0.8,
outer_cut_off: 0.65,
}
}
}
#[derive(Default)]
pub struct Lights {
pub uniform: UniformBuffer,
}
impl Lights {
pub fn add_to_shader(source: &str, bind_group: usize, binding: usize) -> String {
let bind_group = format!("{:?}", bind_group);
let binding = format!("{:?}", binding);
let lights_count = format!("{:?}u", MAX_LIGHTS);
let light_code = include_str!("shaders/light.inc.wgsl");
let light_code = str::replace(light_code, "{{ max_lights_count }}", &lights_count)
.replace("{{ bind_group }}", &bind_group)
.replace("{{ binding }}", &binding);
source.replace("{{ include(light) }}", &light_code)
}
}
pub fn startup(mut globals: Mut<Globals>) {
globals.set(Lights::default());
}
pub fn bind(world: Const<World>, renderer: Const<Renderer>, mut globals: Mut<Globals>) {
if let Some(lights) = globals.get_mut::<Lights>() {
let mut uniform = Uniform::default();
for (light,) in world.query::<(&Light,)>() {
uniform.store(light);
}
renderer.load_uniform_buffer(&mut lights.uniform, bytemuck::cast_slice(&[uniform]));
}
}
#[repr(C)]
#[derive(Default, Clone, Copy, Debug)]
struct Uniform {
ambient: [f32; 4],
count: [u32; 4],
directional: [DirectionalLight; MAX_LIGHTS],
point: [PointLight; MAX_LIGHTS],
simple: [SimpleLight; MAX_LIGHTS],
spot: [SpotLight; MAX_LIGHTS],
}
unsafe impl bytemuck::Zeroable for Uniform {}
unsafe impl bytemuck::Pod for Uniform {}
impl Uniform {
pub fn store(&mut self, light: &Light) {
match light {
Light::Ambient { color, intensity } => {
self.ambient = (*color * (*intensity)).into()
},
Light::Directional { color, direction, intensity, enabled } => {
let i = self.count[0] as usize;
if *enabled && i < MAX_LIGHTS {
self.directional[i] = DirectionalLight {
direction: [direction.x, direction.y, direction.z, 1.0],
color: (*color * (*intensity)).into()
};
self.count[0] = i as u32 + 1;
}
},
Light::Point {
color, position, intensity, enabled, constant, linear, quadratic
} => {
let i = self.count[1] as usize;
if *enabled && i < MAX_LIGHTS {
self.point[i] = PointLight {
position: [position.x, position.y, position.z, 1.0],
color: (*color * (*intensity)).into(),
a_constant: *constant,
a_linear: *linear,
a_quadratic: *quadratic,
..Default::default()
};
self.count[1] = i as u32 + 1;
}
}
Light::Simple { color, position, intensity, enabled } => {
let i = self.count[2] as usize;
if *enabled && i < MAX_LIGHTS {
self.simple[i] = SimpleLight {
position: [position.x, position.y, position.z, 1.0],
color: (*color * (*intensity)).into(),
};
self.count[2] = i as u32 + 1;
}
},
Light::Spot {
color, position, direction, intensity, enabled, cut_off, outer_cut_off
} => {
let i = self.count[3] as usize;
if *enabled && i < MAX_LIGHTS {
self.spot[i] = SpotLight {
position: [position.x, position.y, position.z, 1.0],
direction: [direction.x, direction.y, direction.z, 1.0],
color: (*color * (*intensity)).into(),
cut_off: *cut_off,
outer_cut_off: *outer_cut_off,
..Default::default()
};
self.count[3] = i as u32 + 1;
}
},
};
}
}
#[repr(C)]
#[derive(Default, Copy, Clone, Debug)]
struct DirectionalLight {
direction: [f32; 4],
color: [f32; 4],
}
unsafe impl bytemuck::Zeroable for DirectionalLight {}
unsafe impl bytemuck::Pod for DirectionalLight {}
#[repr(C)]
#[derive(Default, Copy, Clone, Debug)]
struct PointLight {
position: [f32; 4],
color: [f32; 4],
a_constant: f32,
a_linear: f32,
a_quadratic: f32,
padding: f32,
}
unsafe impl bytemuck::Zeroable for PointLight {}
unsafe impl bytemuck::Pod for PointLight {}
#[repr(C)]
#[derive(Default, Copy, Clone, Debug)]
struct SimpleLight {
position: [f32; 4],
color: [f32; 4],
}
unsafe impl bytemuck::Zeroable for SimpleLight {}
unsafe impl bytemuck::Pod for SimpleLight {}
#[repr(C)]
#[derive(Default, Copy, Clone, Debug)]
struct SpotLight {
position: [f32; 4],
direction: [f32; 4],
color: [f32; 4],
cut_off: f32,
outer_cut_off: f32,
padding: [f32; 2],
}
unsafe impl bytemuck::Zeroable for SpotLight {}
unsafe impl bytemuck::Pod for SpotLight {}