use std::collections::HashMap;
use lights::LightUniform;
use crate::{component_app::EngineComponent, render::render_engine::RenderEngine};
use wgpu::util::DeviceExt;
pub mod lights;
#[derive(Debug)]
pub struct LightEngine {
light_buffer: wgpu::Buffer,
light_count_buffer: wgpu::Buffer,
light_ambient_buffer: wgpu::Buffer,
light_bind_group: wgpu::BindGroup,
lights: HashMap<u32, LightUniform>,
ambient_color: [f32; 3],
last_count: usize,
dirty: bool
}
impl EngineComponent<&mut RenderEngine> for LightEngine {
fn create(engine: &mut RenderEngine) -> Self {
let default_light = lights::LightUniform::new([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], 0.0, 0.0, 1000.0);
let light_buffer = engine.device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Light Buffer"),
contents: bytemuck::cast_slice(&[default_light]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST
}
);
let light_count_buffer = engine.device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Light Count Buffer"),
contents: bytemuck::cast_slice(&[1, 0, 0, 0]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST
}
);
let light_ambient_buffer = engine.device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Light Ambient Buffer"),
contents: bytemuck::cast_slice(&[0.0, 0.0, 0.0, 0.0]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST
}
);
let light_bind_group = engine.device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &engine.device.create_bind_group_layout(&lights::LightUniform::BIND_LAYOUT),
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: light_buffer.as_entire_binding()
},
wgpu::BindGroupEntry {
binding: 1,
resource: light_count_buffer.as_entire_binding()
},
wgpu::BindGroupEntry {
binding: 2,
resource: light_ambient_buffer.as_entire_binding()
}
],
label: Some("light_bind_group")
});
Self {
light_buffer, light_count_buffer,
light_bind_group, light_ambient_buffer,
ambient_color: [0.0, 0.0, 0.0],
lights: HashMap::new(), last_count: 0, dirty: false
}
}
fn update(&mut self, engine: &mut RenderEngine) {
if !self.dirty { return }
engine.queue.write_buffer(
&self.light_count_buffer,
0,
bytemuck::cast_slice(&[self.lights.len() as u32, 0, 0, 0])
);
engine.queue.write_buffer(&self.light_ambient_buffer, 0, bytemuck::cast_slice(&[self.ambient_color[0], self.ambient_color[1], self.ambient_color[2], 0.0]));
if self.last_count != self.lights.len() {
self.last_count = self.lights.len();
let mut lights: Vec<LightUniform> = self.lights.values().cloned().collect();
if lights.len() < 1 {
lights.push(LightUniform::new([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], 0.0, 0.0, 1000.0));
}
self.light_buffer = engine.device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Light Buffer"),
contents: bytemuck::cast_slice(&lights),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST
}
);
self.light_bind_group = engine.device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &engine.device.create_bind_group_layout(&lights::LightUniform::BIND_LAYOUT),
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: self.light_buffer.as_entire_binding()
},
wgpu::BindGroupEntry {
binding: 1,
resource: self.light_count_buffer.as_entire_binding()
},
wgpu::BindGroupEntry {
binding: 2,
resource: self.light_ambient_buffer.as_entire_binding()
}
],
label: Some("light_bind_group")
});
}
else {
let mut lights: Vec<LightUniform> = self.lights.values().cloned().collect();
if lights.len() < 1 {
lights.push(LightUniform::new([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], 0.0, 0.0, 1000.0));
}
engine.queue.write_buffer(
&self.light_buffer,
0,
bytemuck::cast_slice(&lights)
);
}
}
fn render<'rpass>(&'rpass mut self, _: &'rpass RenderEngine, pass: &mut wgpu::RenderPass<'rpass>) {
pass.set_bind_group(2, self.bind_group(), &[]);
}
fn start(&mut self, _: &mut RenderEngine) {}
fn exit(&mut self, _: &mut RenderEngine) {}
}
impl LightEngine {
pub fn bind_group(&self) -> &wgpu::BindGroup { &self.light_bind_group }
pub fn mark_dirty(&mut self) { self.dirty = true; }
pub fn add_light(&mut self, id: u32, light: LightUniform) { self.lights.insert(id, light); self.mark_dirty(); }
pub fn remove_light(&mut self, idx: u32) { self.lights.remove(&idx); self.mark_dirty(); }
pub fn clear_lights(&mut self) { self.lights.clear(); self.mark_dirty(); }
pub fn set_ambient_color(&mut self, ambient: [f32; 3]) { self.ambient_color = ambient; }
pub fn get_ambient_color(&self) -> [f32; 3] { return self.ambient_color; }
}