137 lines
3.7 KiB
Rust
137 lines
3.7 KiB
Rust
use super::{MeshHandle, TextureHandle};
|
|
use std::collections::HashMap;
|
|
use std::ops::Range;
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
pub struct MeshInstance {
|
|
pub mesh: MeshHandle,
|
|
pub albedo: TextureHandle,
|
|
pub transform: glam::Mat4,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct DrawState {
|
|
group_id: usize,
|
|
texture_id: usize,
|
|
sub_id: usize,
|
|
instance_range: Range<u32>,
|
|
}
|
|
|
|
pub enum Command {
|
|
BindMeshGroup {
|
|
group_id: usize,
|
|
},
|
|
BindTexture {
|
|
texture_id: usize,
|
|
},
|
|
Draw {
|
|
sub_id: usize,
|
|
instance_range: Range<u32>,
|
|
},
|
|
}
|
|
|
|
pub struct CommandIterator<'a> {
|
|
command_set: &'a CommandSet,
|
|
command_idx: usize, // TODO use iterator instead lol
|
|
last_group_id: usize,
|
|
last_texture_id: usize,
|
|
}
|
|
|
|
impl<'a> CommandIterator<'a> {
|
|
pub fn new(command_set: &'a CommandSet) -> Self {
|
|
Self {
|
|
command_set,
|
|
command_idx: 0,
|
|
last_group_id: usize::MAX,
|
|
last_texture_id: usize::MAX,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Iterator for CommandIterator<'a> {
|
|
type Item = Command;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let DrawState {
|
|
group_id,
|
|
sub_id,
|
|
texture_id,
|
|
instance_range,
|
|
} = self.command_set.draws.get(self.command_idx)?.clone();
|
|
|
|
if group_id != self.last_group_id {
|
|
self.last_group_id = group_id;
|
|
Some(Command::BindMeshGroup { group_id })
|
|
} else if texture_id != self.last_texture_id {
|
|
self.last_texture_id = texture_id;
|
|
Some(Command::BindTexture { texture_id })
|
|
} else {
|
|
self.command_idx += 1;
|
|
Some(Command::Draw {
|
|
sub_id,
|
|
instance_range,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct CommandSet {
|
|
transforms: Vec<[f32; 16]>,
|
|
draws: Vec<DrawState>,
|
|
}
|
|
|
|
impl CommandSet {
|
|
pub fn build(instances: &Vec<MeshInstance>) -> Self {
|
|
let mut sorted_meshes = HashMap::<(usize, usize), Vec<MeshInstance>>::new();
|
|
for instance in instances.iter() {
|
|
let group_id = instance.mesh.group_id;
|
|
let texture_id = instance.albedo.id;
|
|
let key = (group_id, texture_id);
|
|
if let Some(by_state) = sorted_meshes.get_mut(&key) {
|
|
by_state.push(*instance);
|
|
} else {
|
|
sorted_meshes.insert(key, vec![*instance]);
|
|
}
|
|
}
|
|
|
|
let mut transforms = Vec::new();
|
|
let mut draws = Vec::new();
|
|
|
|
for ((group_id, texture_id), mut meshes) in sorted_meshes.drain() {
|
|
let mut sorted_subs = HashMap::<usize, Vec<MeshInstance>>::new();
|
|
for mesh in meshes.drain(..) {
|
|
let sub_id = mesh.mesh.sub_id;
|
|
if let Some(by_sub) = sorted_subs.get_mut(&sub_id) {
|
|
by_sub.push(mesh);
|
|
} else {
|
|
sorted_subs.insert(sub_id, vec![mesh]);
|
|
}
|
|
}
|
|
|
|
for (sub_id, meshes) in sorted_subs.drain() {
|
|
let start_idx = transforms.len() as u32;
|
|
let as_arrays = meshes.iter().map(|i| i.transform.to_cols_array());
|
|
transforms.extend(as_arrays);
|
|
let end_idx = transforms.len() as u32;
|
|
let instance_range = start_idx..end_idx;
|
|
draws.push(DrawState {
|
|
group_id,
|
|
texture_id,
|
|
sub_id,
|
|
instance_range,
|
|
});
|
|
}
|
|
}
|
|
|
|
Self { transforms, draws }
|
|
}
|
|
|
|
pub fn get_storage(&self) -> &[u8] {
|
|
bytemuck::cast_slice(&self.transforms)
|
|
}
|
|
|
|
pub fn iter(&self) -> CommandIterator {
|
|
CommandIterator::new(self)
|
|
}
|
|
}
|