use alloc::vec::Vec;
use hashbrown::HashMap;
use crate::ecs::ResourceKind;
use crate::ecs::asset_id::AssetId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum MeshBlock {
Mesh,
ProceduralMesh,
VoxelChunk,
File,
Runtime,
}
impl MeshBlock {
pub fn order(self) -> u8 {
self as u8
}
}
#[derive(Debug, Default, Clone)]
pub struct ResourceHandles {
next: HashMap<u8, u32>,
map: HashMap<(u8, AssetId), u32>,
shader_map: HashMap<AssetId, u32>,
shader_next: u32,
}
impl ResourceHandles {
pub fn assign(&mut self, kind: ResourceKind, id: AssetId) -> u32 {
let next = self.next.entry(kind as u8).or_insert(0);
let handle = *next;
*next += 1;
self.map.insert((kind as u8, id), handle);
handle
}
pub fn get(&self, kind: ResourceKind, id: AssetId) -> Option<u32> {
self.map.get(&(kind as u8, id)).copied()
}
pub fn count(&self, kind: ResourceKind) -> u32 {
self.next.get(&(kind as u8)).copied().unwrap_or(0)
}
pub fn from_assets(assets: impl IntoIterator<Item = (AssetId, ResourceKind)>) -> Self {
let mut handles = Self::default();
for (id, kind) in assets {
handles.assign(kind, id);
}
handles
}
pub fn assign_mesh_sources(&mut self, sources: impl IntoIterator<Item = (AssetId, MeshBlock)>) {
let mut sources: Vec<(AssetId, MeshBlock)> = sources.into_iter().collect();
sources.sort_by_key(|(_, block)| block.order());
for (id, _) in sources {
self.assign(ResourceKind::Mesh, id);
}
}
pub fn assign_shader(&mut self, id: AssetId) -> u32 {
let handle = self.shader_next;
self.shader_next += 1;
self.shader_map.insert(id, handle);
handle
}
pub fn shader(&self, id: AssetId) -> Option<u32> {
self.shader_map.get(&id).copied()
}
pub fn shader_count(&self) -> u32 {
self.shader_next
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn handles_are_dense_per_kind_in_declaration_order() {
let handles = ResourceHandles::from_assets([
(AssetId(10), ResourceKind::Texture),
(AssetId(11), ResourceKind::Mesh),
(AssetId(12), ResourceKind::Texture),
(AssetId(20), ResourceKind::AudioClip),
(AssetId(14), ResourceKind::Texture),
]);
assert_eq!(handles.get(ResourceKind::Texture, AssetId(10)), Some(0));
assert_eq!(handles.get(ResourceKind::Texture, AssetId(12)), Some(1));
assert_eq!(handles.get(ResourceKind::Texture, AssetId(14)), Some(2));
assert_eq!(handles.get(ResourceKind::Mesh, AssetId(11)), Some(0));
assert_eq!(handles.get(ResourceKind::AudioClip, AssetId(20)), Some(0));
assert_eq!(handles.count(ResourceKind::Texture), 3);
assert_eq!(handles.count(ResourceKind::Mesh), 1);
assert_eq!(handles.count(ResourceKind::Material), 0);
assert_eq!(handles.get(ResourceKind::Texture, AssetId(99)), None);
}
#[test]
fn the_same_id_in_two_kinds_gets_independent_handles() {
let mut handles = ResourceHandles::default();
assert_eq!(handles.assign(ResourceKind::Texture, AssetId(1)), 0);
assert_eq!(handles.assign(ResourceKind::Mesh, AssetId(1)), 0);
assert_eq!(handles.get(ResourceKind::Texture, AssetId(1)), Some(0));
assert_eq!(handles.get(ResourceKind::Mesh, AssetId(1)), Some(0));
}
#[test]
fn mesh_sources_are_block_ordered_across_kinds() {
let mut handles = ResourceHandles::default();
handles.assign_mesh_sources([
(AssetId(0), MeshBlock::Mesh),
(AssetId(1), MeshBlock::ProceduralMesh),
(AssetId(2), MeshBlock::VoxelChunk),
(AssetId(3), MeshBlock::Mesh),
(AssetId(4), MeshBlock::File),
(AssetId(5), MeshBlock::ProceduralMesh),
]);
let h = |id: u32| handles.get(ResourceKind::Mesh, AssetId(id));
assert_eq!(h(0), Some(0));
assert_eq!(h(3), Some(1));
assert_eq!(h(1), Some(2));
assert_eq!(h(5), Some(3));
assert_eq!(h(2), Some(4));
assert_eq!(h(4), Some(5));
assert_eq!(handles.count(ResourceKind::Mesh), 6);
}
#[test]
fn mesh_blocks_run_mesh_procedural_voxel_file_then_runtime() {
assert_eq!(MeshBlock::Mesh.order(), 0);
assert_eq!(MeshBlock::ProceduralMesh.order(), 1);
assert_eq!(MeshBlock::VoxelChunk.order(), 2);
assert_eq!(MeshBlock::File.order(), 3);
assert_eq!(MeshBlock::Runtime.order(), 4);
}
#[test]
fn shaders_count_in_a_space_of_their_own() {
let mut handles = ResourceHandles::default();
handles.assign(ResourceKind::Material, AssetId(7));
assert_eq!(handles.assign_shader(AssetId(7)), 0);
assert_eq!(handles.assign_shader(AssetId(8)), 1);
assert_eq!(handles.shader(AssetId(7)), Some(0));
assert_eq!(handles.shader(AssetId(8)), Some(1));
assert_eq!(handles.shader(AssetId(9)), None);
assert_eq!(handles.shader_count(), 2);
}
}