Skip to main content

mittens_engine/engine/graphics/
render_assets.rs

1use std::collections::HashMap;
2
3use crate::engine::graphics::MeshUploader;
4use crate::engine::graphics::mesh::CpuMesh;
5use crate::engine::graphics::mesh::MeshFactory;
6use crate::engine::graphics::primitives::{CpuMeshHandle, MeshHandle};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum BuiltinMeshType {
10    Triangle2D,
11    Quad2D,
12    Cube,
13    Tetrahedron,
14    Sphere,
15    Cone,
16    Circle2D,
17}
18
19/// Renderer-side asset registry used by ECS systems.
20///
21/// Design:
22/// - ECS and gameplay code refer to geometry by `CpuMeshHandle` (CPU asset identity).
23/// - The renderer owns GPU resources and returns `MeshHandle`.
24/// - `RenderAssets` bridges the two and caches uploads.
25#[derive(Debug, Default)]
26pub struct RenderAssets {
27    cpu_meshes: Vec<CpuMesh>,
28    gpu_meshes: HashMap<CpuMeshHandle, MeshHandle>,
29
30    /// Imported CPU meshes keyed by a stable string (e.g. "{gltf_name}:{mesh_name_or_index}:{prim_index}").
31    imported_meshes: HashMap<String, CpuMeshHandle>,
32
33    /// Built-in CPU mesh handles (stable ids) keyed by mesh kind.
34    ///
35    /// These are pre-registered in `RenderAssets::new()` so scenes that refer to built-in
36    /// meshes by numeric id can load without any explicit setup.
37    builtin_meshes: HashMap<BuiltinMeshType, CpuMeshHandle>,
38
39    /// Procedural unit wireframe boxes keyed by the exact authored thickness bits.
40    wireframe_box_meshes: HashMap<u32, CpuMeshHandle>,
41}
42
43impl RenderAssets {
44    pub fn new() -> Self {
45        let mut s = Self::default();
46        s.register_builtin_meshes();
47        s
48    }
49
50    /// Return the CPU mesh handle for a built-in mesh.
51    ///
52    /// Builtins are pre-registered, but this is also safe to call if a `RenderAssets` was
53    /// constructed via `Default`.
54    pub fn get_mesh(&mut self, mesh: BuiltinMeshType) -> CpuMeshHandle {
55        self.ensure_builtin_mesh(mesh)
56    }
57
58    fn register_builtin_meshes(&mut self) {
59        // Keep this order stable so serialized scenes that refer to built-in meshes by id
60        // stay valid across runs.
61        let _ = self.ensure_builtin_mesh(BuiltinMeshType::Triangle2D);
62        let _ = self.ensure_builtin_mesh(BuiltinMeshType::Quad2D);
63        let _ = self.ensure_builtin_mesh(BuiltinMeshType::Cube);
64        let _ = self.ensure_builtin_mesh(BuiltinMeshType::Tetrahedron);
65        // Appended to preserve existing numeric ids.
66        let _ = self.ensure_builtin_mesh(BuiltinMeshType::Sphere);
67        let _ = self.ensure_builtin_mesh(BuiltinMeshType::Cone);
68        let _ = self.ensure_builtin_mesh(BuiltinMeshType::Circle2D);
69    }
70
71    fn ensure_builtin_mesh(&mut self, mesh: BuiltinMeshType) -> CpuMeshHandle {
72        if let Some(h) = self.builtin_meshes.get(&mesh).copied() {
73            return h;
74        }
75
76        let cpu_mesh = match mesh {
77            BuiltinMeshType::Triangle2D => MeshFactory::triangle_2d(),
78            BuiltinMeshType::Quad2D => MeshFactory::quad_2d(),
79            BuiltinMeshType::Cube => MeshFactory::cube(),
80            BuiltinMeshType::Tetrahedron => MeshFactory::tetrahedron(),
81            BuiltinMeshType::Sphere => MeshFactory::sphere(),
82            BuiltinMeshType::Cone => MeshFactory::cone(32),
83            BuiltinMeshType::Circle2D => MeshFactory::circle_2d(0.45, 0.5, 64),
84        };
85
86        let h = self.register_mesh(cpu_mesh);
87        self.builtin_meshes.insert(mesh, h);
88        h
89    }
90
91    /// Register CPU mesh data and get a stable CPU-side handle.
92    ///
93    /// If callers want reuse, they should keep and share this handle.
94    pub fn register_mesh(&mut self, mesh: CpuMesh) -> CpuMeshHandle {
95        let h = CpuMeshHandle(self.cpu_meshes.len() as u32);
96        self.cpu_meshes.push(mesh);
97        h
98    }
99
100    /// Return a shared unit wireframe-box mesh for the requested relative edge thickness.
101    pub fn wireframe_box_mesh(&mut self, thickness: f32) -> CpuMeshHandle {
102        let thickness = thickness.clamp(1.0e-4, 1.0);
103        let key = thickness.to_bits();
104        if let Some(handle) = self.wireframe_box_meshes.get(&key).copied() {
105            return handle;
106        }
107        let handle = self.register_mesh(MeshFactory::wireframe_box(thickness));
108        self.wireframe_box_meshes.insert(key, handle);
109        handle
110    }
111
112    /// Register an imported mesh and index it by `key` for later lookup.
113    pub fn register_imported_mesh(
114        &mut self,
115        key: impl Into<String>,
116        mesh: CpuMesh,
117    ) -> CpuMeshHandle {
118        let key = key.into();
119        let h = self.register_mesh(mesh);
120        self.imported_meshes.insert(key, h);
121        h
122    }
123
124    /// Look up an imported mesh handle by key.
125    pub fn imported_mesh(&self, key: &str) -> Option<CpuMeshHandle> {
126        self.imported_meshes.get(key).copied()
127    }
128
129    pub fn cpu_mesh(&self, h: CpuMeshHandle) -> Option<&CpuMesh> {
130        self.cpu_meshes.get(h.0 as usize)
131    }
132
133    pub fn cpu_mesh_count(&self) -> usize {
134        self.cpu_meshes.len()
135    }
136
137    pub fn imported_mesh_count(&self) -> usize {
138        self.imported_meshes.len()
139    }
140
141    /// Get (or upload) a mesh into the renderer and return a renderer-owned `MeshHandle`.
142    pub fn gpu_mesh_handle(
143        &mut self,
144        uploader: &mut dyn MeshUploader,
145        cpu_mesh: CpuMeshHandle,
146    ) -> Result<MeshHandle, Box<dyn std::error::Error>> {
147        if let Some(h) = self.gpu_meshes.get(&cpu_mesh).copied() {
148            return Ok(h);
149        }
150
151        let mesh = self
152            .cpu_mesh(cpu_mesh)
153            .ok_or("RenderAssets: invalid CpuMeshHandle")?;
154        let h = uploader.upload_mesh(mesh)?;
155        self.gpu_meshes.insert(cpu_mesh, h);
156        Ok(h)
157    }
158}