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())
    }
}