1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
use std::sync::Arc;
use slotmap::{SecondaryMap, SlotMap};
use crate::{
mesh::{CpuMesh, GpuMesh, MeshError},
renderer::GpuMeshInstance,
RenderContext,
};
slotmap::new_key_type! {
/// Key for identifying a cpu mesh in a model.
pub struct CpuModelMeshKey;
}
/// Like [`GpuMeshInstance`], but for CPU sided usage in a [`CpuModel`] only.
pub struct CpuMeshInstance {
pub mesh: CpuModelMeshKey,
pub world_from_mesh: glam::Affine3A,
// TODO(andreas): Expose other properties we have on [`GpuMeshInstance`].
}
/// A collection of meshes & mesh instances on the CPU.
///
/// Note that there is currently no `GpuModel` equivalent, since
/// [`GpuMeshInstance`]es use shared ownership of [`GpuMesh`]es.
///
/// This is the output of a model loader and is ready to be converted into
/// a series of [`GpuMeshInstance`]s that can be rendered.
///
/// This is meant as a useful intermediate structure for doing post-processing steps on the model prior to gpu upload.
#[derive(Default)]
pub struct CpuModel {
pub meshes: SlotMap<CpuModelMeshKey, CpuMesh>,
pub instances: Vec<CpuMeshInstance>,
}
impl CpuModel {
/// Creates a new [`CpuModel`] from a single [`CpuMesh`], creating a single instance with identity transform.
pub fn from_single_mesh(mesh: CpuMesh) -> Self {
let mut model = Self::default();
model.add_single_instance_mesh(mesh);
model
}
/// Adds a new [`CpuMesh`] to the model, creating a single instance with identity transform.
pub fn add_single_instance_mesh(&mut self, mesh: CpuMesh) {
let mesh_key = self.meshes.insert(mesh);
self.instances.push(CpuMeshInstance {
mesh: mesh_key,
world_from_mesh: glam::Affine3A::IDENTITY,
});
}
pub fn calculate_bounding_box(&self) -> re_math::BoundingBox {
re_math::BoundingBox::from_points(
self.instances
.iter()
.filter_map(|mesh_instance| {
self.meshes.get(mesh_instance.mesh).map(|mesh| {
mesh.vertex_positions
.iter()
.map(|p| mesh_instance.world_from_mesh.transform_point3(*p))
})
})
.flatten(),
)
}
/// Converts the entire model into a serious of mesh instances that can be rendered.
///
/// Silently ignores:
/// * instances with invalid mesh keys
/// * unreferenced meshes
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(),
})
})
.collect())
}
}