mod pipeline;
mod render;
mod uniforms;
mod vertex;
pub use pipeline::*;
pub use render::*;
pub use uniforms::*;
pub use vertex::*;
use crate::{
CameraView, Color, DrawOrder, GpuRenderer, Index, Vec2, Vec3, Vec4,
instance_buffer::OrderedIndex, parallel::*,
};
use slotmap::SlotMap;
use std::mem;
use wgpu::util::align_to;
pub const MAX_AREA_LIGHTS: usize = 2_000;
pub const MAX_DIR_LIGHTS: usize = 1_333;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct AreaLight {
pub pos: Vec2,
pub color: Color,
pub max_distance: f32,
pub anim_speed: f32,
pub dither: f32,
pub animate: bool,
pub camera_view: CameraView,
pub visible: bool,
}
impl AreaLight {
fn to_raw(self) -> AreaLightRaw {
AreaLightRaw {
pos: self.pos.to_array(),
color: self.color.0,
max_distance: self.max_distance,
dither: self.dither,
anim_speed: self.anim_speed,
animate: u32::from(self.animate),
camera_view: self.camera_view as u32,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct DirectionalLight {
pub pos: Vec2,
pub color: Color,
pub max_distance: f32,
pub max_width: f32,
pub anim_speed: f32,
pub angle: f32,
pub dither: f32,
pub fade_distance: f32,
pub edge_fade_distance: f32,
pub animate: bool,
pub camera_view: CameraView,
pub visible: bool,
}
impl DirectionalLight {
fn to_raw(self) -> DirectionalLightRaw {
DirectionalLightRaw {
pos: self.pos.to_array(),
color: self.color.0,
max_distance: self.max_distance,
animate: u32::from(self.animate),
max_width: self.max_width,
anim_speed: self.anim_speed,
dither: self.dither,
angle: self.angle,
fade_distance: self.fade_distance,
edge_fade_distance: self.edge_fade_distance,
camera_view: self.camera_view as u32,
}
}
}
#[derive(Clone, Debug)]
pub struct Lights {
pub pos: Vec3,
pub size: Vec2,
pub world_color: Vec4,
pub enable_lights: bool,
pub store_id: Index,
pub order: DrawOrder,
pub area_lights: SlotMap<Index, AreaLight>,
pub directional_lights: SlotMap<Index, DirectionalLight>,
pub area_count: u32,
pub dir_count: u32,
pub changed: bool,
pub directionals_changed: bool,
pub areas_changed: bool,
}
impl Lights {
pub fn new(
renderer: &mut GpuRenderer,
order_layer: u32,
pos: Vec3,
size: Vec2,
) -> Self {
Self {
pos,
size,
world_color: Vec4::new(1.0, 1.0, 1.0, 0.0),
enable_lights: false,
store_id: renderer.new_ibo_store(
bytemuck::bytes_of(&LightsVertex::default()).len(),
),
order: DrawOrder::new(true, pos, order_layer),
area_lights: SlotMap::with_capacity_and_key(MAX_AREA_LIGHTS),
directional_lights: SlotMap::with_capacity_and_key(MAX_DIR_LIGHTS),
area_count: 0,
dir_count: 0,
changed: true,
directionals_changed: true,
areas_changed: true,
}
}
pub fn unload(self, renderer: &mut GpuRenderer) {
renderer.remove_ibo_store(self.store_id);
}
pub fn set_world_color(&mut self, color: Vec4) -> &mut Self {
self.world_color = color;
self.order.alpha = color.w < 1.0;
self.changed = true;
self
}
pub fn set_order_alpha(&mut self, alpha: bool) -> &mut Self {
self.order.alpha = alpha;
self
}
pub fn set_pos(&mut self, pos: Vec3) -> &mut Self {
self.pos = pos;
self.order.set_pos(pos);
self.changed = true;
self
}
pub fn set_size(&mut self, size: Vec2) -> &mut Self {
self.size = size;
self.changed = true;
self
}
pub fn create_quad(&mut self, renderer: &mut GpuRenderer) {
let instance = LightsVertex {
world_color: self.world_color.to_array(),
enable_lights: u32::from(self.enable_lights),
dir_count: self
.directional_lights
.iter()
.filter(|(_k, l)| l.visible)
.par_bridge()
.count() as u32,
area_count: self
.area_lights
.iter()
.filter(|(_k, l)| l.visible)
.par_bridge()
.count() as u32,
pos: self.pos.to_array(),
size: self.size.to_array(),
};
if let Some(store) = renderer.get_ibo_store_mut(self.store_id) {
let bytes = bytemuck::bytes_of(&instance);
if bytes.len() != store.store.len() {
store.store.resize_with(bytes.len(), || 0);
}
store.store.copy_from_slice(bytes);
store.changed = true;
}
self.changed = false;
}
pub fn insert_area_light(&mut self, light: AreaLight) -> Option<Index> {
if self.area_lights.len() + 1 >= MAX_AREA_LIGHTS {
return None;
}
self.areas_changed = true;
self.changed = true;
Some(self.area_lights.insert(light))
}
pub fn remove_area_light(&mut self, key: Index) -> Option<AreaLight> {
self.areas_changed = true;
self.changed = true;
self.area_lights.remove(key)
}
pub fn set_area_light_visibility(&mut self, key: Index, visible: bool) {
self.areas_changed = true;
self.changed = true;
if let Some(light) = self.area_lights.get_mut(key) {
light.visible = visible;
}
}
pub fn get_mut_area_light(&mut self, key: Index) -> Option<&mut AreaLight> {
self.areas_changed = true;
self.area_lights.get_mut(key)
}
pub fn insert_directional_light(
&mut self,
light: DirectionalLight,
) -> Option<Index> {
if self.directional_lights.len() + 1 >= MAX_DIR_LIGHTS {
return None;
}
self.directionals_changed = true;
self.changed = true;
Some(self.directional_lights.insert(light))
}
pub fn remove_directional_light(
&mut self,
key: Index,
) -> Option<DirectionalLight> {
self.directionals_changed = true;
self.changed = true;
self.directional_lights.remove(key)
}
pub fn set_directional_light_visibility(
&mut self,
key: Index,
visible: bool,
) {
self.directionals_changed = true;
self.changed = true;
if let Some(light) = self.directional_lights.get_mut(key) {
light.visible = visible;
}
}
pub fn get_mut_directional_light(
&mut self,
key: Index,
) -> Option<&mut DirectionalLight> {
self.directionals_changed = true;
self.directional_lights.get_mut(key)
}
pub fn update(
&mut self,
renderer: &mut GpuRenderer,
areas: &mut wgpu::Buffer,
dirs: &mut wgpu::Buffer,
) -> OrderedIndex {
if self.changed {
self.create_quad(renderer);
}
if self.areas_changed {
let area_alignment: usize =
align_to(mem::size_of::<AreaLightRaw>(), 32) as usize;
let queue = renderer.queue();
self.area_lights
.iter()
.filter(|(_k, l)| l.visible)
.enumerate()
.par_bridge()
.for_each(|(i, (_key, light))| {
queue.write_buffer(
areas,
(i * area_alignment) as wgpu::BufferAddress,
bytemuck::bytes_of(&light.to_raw()),
);
});
self.areas_changed = false;
}
if self.directionals_changed {
let dir_alignment: usize =
align_to(mem::size_of::<DirectionalLightRaw>(), 48) as usize;
let queue = renderer.queue();
self.directional_lights
.iter()
.filter(|(_k, l)| l.visible)
.enumerate()
.par_bridge()
.for_each(|(i, (_key, dir))| {
queue.write_buffer(
dirs,
(i * dir_alignment) as wgpu::BufferAddress,
bytemuck::bytes_of(&dir.to_raw()),
);
});
self.directionals_changed = false;
}
OrderedIndex::new(self.order, self.store_id)
}
}