use std::sync::Arc;
use slotmap::{SecondaryMap, SlotMap};
use crate::RenderContext;
use crate::mesh::{CpuMesh, GpuMesh, MeshError};
use crate::renderer::GpuMeshInstance;
slotmap::new_key_type! {
pub struct CpuModelMeshKey;
}
struct CpuMeshInstance {
mesh: CpuModelMeshKey,
world_from_mesh: glam::Affine3A,
}
#[derive(Default)]
pub struct CpuModel {
meshes: SlotMap<CpuModelMeshKey, CpuMesh>,
instances: Vec<CpuMeshInstance>,
bbox: macaw::BoundingBox,
}
impl CpuModel {
pub fn from_single_mesh(mesh: CpuMesh) -> Self {
let mut model = Self::default();
let key = model.add_mesh(mesh);
model.add_instance(key, glam::Affine3A::IDENTITY);
model
}
pub fn add_mesh(&mut self, mesh: CpuMesh) -> CpuModelMeshKey {
self.meshes.insert(mesh)
}
pub fn add_instance(&mut self, mesh_key: CpuModelMeshKey, world_from_mesh: glam::Affine3A) {
if let Some(mesh) = self.meshes.get(mesh_key) {
self.bbox = self
.bbox
.union(mesh.bbox.transform_affine3(&world_from_mesh));
}
self.instances.push(CpuMeshInstance {
mesh: mesh_key,
world_from_mesh,
});
}
pub fn bbox(&self) -> macaw::BoundingBox {
self.bbox
}
pub fn override_albedo_factor(&mut self, albedo_factor: crate::Rgba) {
for (_key, mesh) in &mut self.meshes {
for material in &mut mesh.materials {
material.albedo_factor = albedo_factor;
}
}
}
pub fn into_gpu_meshes(self, ctx: &RenderContext) -> Result<Vec<GpuMeshInstance>, MeshError> {
let mut gpu_meshes = SecondaryMap::with_capacity(self.meshes.len());
for (mesh_key, mesh) in &self.meshes {
gpu_meshes.insert(mesh_key, Arc::new(GpuMesh::new(ctx, mesh)?));
}
Ok(self
.instances
.into_iter()
.filter_map(|instance| {
Some(GpuMeshInstance {
gpu_mesh: gpu_meshes.get(instance.mesh)?.clone(),
world_from_mesh: instance.world_from_mesh,
additive_tint: Default::default(),
outline_mask_ids: Default::default(),
picking_layer_id: Default::default(),
cull_mode: Default::default(),
})
})
.collect())
}
}