diff --git a/src/app.rs b/src/app.rs index faa9c5f..b05e711 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,6 +1,6 @@ +use crate::assets::asset_manager::AssetManager; use crate::common::camera::{Camera, CameraResources}; use crate::common::instance::InstanceRegistry; -use crate::common::mesh::MeshRegistry; use crate::common::render_batch::RenderBatch; use crate::common::scene::Scene; use crate::gpu::instance::GpuInstance; @@ -62,7 +62,7 @@ struct VulkanContext { render_pass: Arc, framebuffers: Vec>, pipeline: Arc, - mesh_registry: Arc, + asset_manager: Arc, instance_registry: Arc, camera: Camera, camera_resources: Arc, @@ -189,9 +189,11 @@ impl VulkanContext { ); let scene = Scene::from_file_json("./test_scene.scene"); - let mut mesh_registry = MeshRegistry::new(mem_allocator.clone()); - mesh_registry.load_scene(&scene, mem_allocator.clone()); - let mesh_registry = Arc::new(mesh_registry); + let mut asset_manager = AssetManager::new(mem_allocator.clone()); + asset_manager + .load_scene(&scene) + .expect("Couldn't load scene's meshes"); + let asset_manager = Arc::new(asset_manager); let instance_registry = Arc::new(InstanceRegistry::from_scene(&scene)); Ok(Self { @@ -208,7 +210,7 @@ impl VulkanContext { frag_shader, framebuffers, pipeline, - mesh_registry, + asset_manager, instance_registry, camera, camera_resources, @@ -247,10 +249,10 @@ impl VulkanContext { self.framebuffers[image_idx as usize].clone(), &self.camera, &self.camera_resources, - self.mesh_registry.clone(), + self.asset_manager.clone(), RenderBatch::build_batches( self.mem_allocator.clone(), - self.mesh_registry.clone(), + self.asset_manager.clone(), self.instance_registry.clone(), ), )?; @@ -431,7 +433,7 @@ impl VulkanContext { framebuffer: Arc, camera: &Camera, camera_resources: &CameraResources, - mesh_registry: Arc, + asset_manager: Arc, render_batches: Vec, ) -> Result, Box> { let mut builder = AutoCommandBufferBuilder::primary( @@ -460,29 +462,14 @@ impl VulkanContext { camera_resources.descriptor_set().clone(), )? .bind_pipeline_graphics(pipeline.clone())? - .bind_vertex_buffers( - 0, - mesh_registry - .vertex_buffer - .as_ref() - .expect("Vertex buffer not allocated") - .as_ref() - .clone(), - )? - .bind_index_buffer( - mesh_registry - .index_buffer - .as_ref() - .expect("Index buffer not allocated") - .as_ref() - .clone(), - )?; + .bind_vertex_buffers(0, asset_manager.vertex_buffer())? + .bind_index_buffer(asset_manager.index_buffer())?; unsafe { render_batches.iter().for_each(|batch| { // Grab mesh information - let mesh = mesh_registry - .get(&batch.mesh_id) + let mesh = asset_manager + .get_mesh(&batch.mesh_id) .expect("Mesh does not exist in registry"); builder diff --git a/src/assets/asset_manager.rs b/src/assets/asset_manager.rs index 761e3de..b465267 100644 --- a/src/assets/asset_manager.rs +++ b/src/assets/asset_manager.rs @@ -1,15 +1,25 @@ +use std::collections::HashMap; use std::path::Path; use std::sync::Arc; use anyhow::Result; +use vulkano::buffer::Subbuffer; use vulkano::memory::allocator::MemoryAllocator; use crate::assets::loaders::material::mat_loader::MaterialLoader; use crate::assets::loaders::mesh_loader::MeshLoader; use crate::common::material::MaterialRegistry; use crate::common::mesh::{MeshHandle, MeshRegistry}; +use crate::common::scene::Scene; +use crate::gpu::vertex3::Vertex3; /// A manager that controls all assets used for display. It handles loading all meshes, materials, /// and textures for now. +/// +/// Callers outside this module should go through `AssetManager` rather than reaching into a +/// [`MeshRegistry`]/[`MaterialRegistry`] directly - it's the thing that will keep meshes and their +/// materials consistent with each other once material loading is implemented. pub struct AssetManager { + /// The memory allocator used to (re)allocate the mesh registry's buffers. + allocator: Arc, /// The asset manager's [`MeshRegistry`]. mesh_registry: MeshRegistry, /// The asset manager's [`MaterialRegistry`]. @@ -26,6 +36,7 @@ impl AssetManager { let mesh_registry = MeshRegistry::new(allocator.clone()); let material_registry = MaterialRegistry::new(); Self { + allocator, mesh_registry, material_registry, mesh_loader: MeshLoader::new(), @@ -39,23 +50,51 @@ impl AssetManager { /// /// This method will attempt to auto-generate IDs for materials and textures if they do not have /// any preconfigured, using the ID of the mesh and the type of resource being stored. - pub fn load_model>(&mut self, _mesh_id: String, path: P) -> Result<()> { - // Load and register all materials first - submeshes will reference these materials. - let _materials = self.material_loader.load_materials(&path)?; + pub fn load_model>(&mut self, mesh_id: String, path: P) -> Result<()> { + // TODO: load this model's materials via `self.material_loader` and register them into + // `self.material_registry` under "{mesh_id}_mat_{index}" ids. `MeshRegistry` already + // derives each submesh's `material_id` using that exact format, so nothing else needs to + // change here once a real material loader exists. + let mesh = self.mesh_loader.load_mesh(&path)?; + self.mesh_registry.register_mesh(mesh_id, mesh); - // Load the mesh. Each of `mesh.submeshes` carries a `material_index` local to this file, - // which should be resolved against `materials` (e.g. via a generated - // "{mesh_id}_mat_{index}" id) once there's a way to register it. - let _mesh = self.mesh_loader.load_mesh(&path)?; - - // `MeshRegistry` only knows how to allocate its shared vertex/index buffers in one batch - // via `load_scene`; there's no way yet to register a single mesh's `MeshInfo` into - // already-allocated buffers (or grow them), so this can't build a `MeshHandle` yet. - todo!("MeshRegistry needs support for registering a single mesh after its buffers are already allocated") - } - - /// Loads all information from a scene, returning an empty `Result` indicating success or failure. - pub fn load_scene>(&self, path: P) -> Result<()> { Ok(()) } + + /// Loads every mesh referenced by a [`Scene`] and allocates the buffers needed to render them. + pub fn load_scene(&mut self, scene: &Scene) -> Result<()> { + self.mesh_registry.load_scene(scene, self.allocator.clone()); + + Ok(()) + } + + /// Returns the shared vertex buffer backing every mesh currently registered. + pub fn vertex_buffer(&self) -> Subbuffer<[Vertex3]> { + self.mesh_registry + .vertex_buffer + .as_ref() + .expect("Vertex buffer not allocated") + .as_ref() + .clone() + } + + /// Returns the shared index buffer backing every mesh currently registered. + pub fn index_buffer(&self) -> Subbuffer<[u32]> { + self.mesh_registry + .index_buffer + .as_ref() + .expect("Index buffer not allocated") + .as_ref() + .clone() + } + + /// Looks up a registered mesh by its ID. + pub fn get_mesh(&self, mesh_id: &String) -> Option<&MeshHandle> { + self.mesh_registry.get(mesh_id) + } + + /// Returns every currently registered mesh, keyed by ID. + pub fn meshes(&self) -> &HashMap { + &self.mesh_registry.meshes + } } diff --git a/src/common/mesh.rs b/src/common/mesh.rs index f0da5ac..9adfa35 100644 --- a/src/common/mesh.rs +++ b/src/common/mesh.rs @@ -34,10 +34,11 @@ impl MeshHandle {} /// offset into the registry's shared index buffer rather than an offset local to one loaded file. #[derive(Debug, Clone)] pub struct SubmeshHandle { - /// The material index this submesh was assigned in its source file, if any. This is not yet - /// resolved to a [`crate::common::material::Material`] - that mapping is the caller's - /// responsibility until material loading is wired into mesh registration. - pub material_index: Option, + /// The id this submesh's material should be looked up under in a + /// [`crate::common::material::MaterialRegistry`], if the source file assigned one. Follows + /// the `"{mesh_id}_mat_{index}"` convention - nothing requires a material actually be + /// registered under this id yet, until material loading is wired up. + pub material_id: Option, pub index_loc: usize, pub index_count: usize, } @@ -58,6 +59,11 @@ pub struct MeshRegistry { pub vertex_buffer: Option, /// The index buffer allocated for all registered meshes. pub index_buffer: Option, + /// The raw vertex data backing `vertex_buffer`, kept around so a single mesh can be appended + /// via [`Self::register_mesh`] without losing every previously registered mesh's data. + raw_vertices: Vec, + /// The raw index data backing `index_buffer`. See `raw_vertices`. + raw_indices: Vec, } impl MeshRegistry { @@ -67,13 +73,79 @@ impl MeshRegistry { meshes: HashMap::new(), vertex_buffer: None, index_buffer: None, + raw_vertices: Vec::new(), + raw_indices: Vec::new(), allocator } } - /// Registers a [`MeshHandle`] to the registry, if it does not already exist. - pub fn register(&mut self, mesh_id: String, mesh: MeshHandle) { - self.meshes.insert(mesh_id, mesh); + /// Appends a single already-loaded mesh to `verts`/`indices`, returning a [`MeshHandle`] + /// describing where its data landed. A submesh's `material_id` is derived purely from + /// `mesh_id` and the submesh's position in the source file, so this works whether or not a + /// material has actually been registered under that id yet. + fn append_mesh( + mesh_id: &str, + info: MeshInfo, + verts: &mut Vec, + indices: &mut Vec, + ) -> MeshHandle { + let vertex_loc = verts.len(); + let vertex_count = info.vertices.len(); + info.vertices + .iter() + .zip(info.normals.iter()) + .zip(info.uvs.iter()) + .for_each(|((pos, norm), uv)| { + verts.push(Vertex3::new(*pos, *norm, *uv)); + }); + + let index_loc = indices.len(); + let index_count = info.indices.len(); + indices.extend(info.indices); + + let submeshes = info + .submeshes + .into_iter() + .map(|submesh| SubmeshHandle { + material_id: submesh + .material_index + .map(|i| format!("{mesh_id}_mat_{i}")), + index_loc: index_loc + submesh.index_start, + index_count: submesh.index_count, + }) + .collect::>(); + + println!( + "Loaded mesh \"{}\" with {} vertices, {} indices, and {} submesh(es)", + mesh_id, vertex_count, index_count, submeshes.len() + ); + + MeshHandle { + id: mesh_id.to_string(), + vertex_loc, + vertex_count, + index_loc, + index_count, + submeshes, + } + } + + /// Registers a single mesh, independently of a full [`Scene`] - e.g. from + /// [`crate::assets::asset_manager::AssetManager`] loading one model at a time. Appends the + /// mesh's data to the registry's shared buffers and reallocates them. + /// + /// This is far less efficient than batching a whole scene through [`Self::load_scene`], since + /// every call reallocates and re-uploads the full vertex/index buffers - fine for loading a + /// handful of models up front, not for anything performance-sensitive. + pub fn register_mesh(&mut self, mesh_id: String, info: MeshInfo) { + let handle = Self::append_mesh( + &mesh_id, + info, + &mut self.raw_vertices, + &mut self.raw_indices, + ); + self.meshes.insert(mesh_id, handle); + self.rebuild_buffers(); } /// Creates a `MeshRegistry` from the given [`Scene`]. @@ -84,6 +156,7 @@ impl MeshRegistry { pub fn load_scene(&mut self, scene: &Scene, allocator: Arc) { // Load all the meshes into a single map. println!("Loading meshes from scene"); + self.allocator = allocator; let mesh_loader = MeshLoader::new(); let meshes = scene @@ -99,64 +172,29 @@ impl MeshRegistry { }) .collect::>(); - let mut buf_verts = Vec::new(); - let mut buf_indices = Vec::new(); + self.raw_vertices.clear(); + self.raw_indices.clear(); let mut mesh_list = HashMap::new(); - meshes.into_iter().for_each(|(id, obj)| { - // Information will be added directly to the verts and indices vectors, and a new Mesh - // struct will be created and inserted into the mesh_list. - let vert_start = buf_verts.len(); - let all_verts = obj.vertices; - let all_norms = obj.normals; - let obj_num_verts = all_verts.len(); - all_verts - .iter() - .zip(all_norms.iter()) - .zip(obj.uvs.iter()) - .for_each(|((pos, norm), uv)| { - buf_verts.push(Vertex3::new(*pos, *norm, *uv)); - }); - - let index_start = buf_indices.len(); - let obj_num_idx = obj.indices.len(); - buf_indices.extend(obj.indices); - - let submeshes = obj - .submeshes - .into_iter() - .map(|submesh| SubmeshHandle { - material_index: submesh.material_index, - index_loc: index_start + submesh.index_start, - index_count: submesh.index_count, - }) - .collect::>(); - - println!( - "Loaded mesh \"{}\" with {} vertices, {} indices, and {} submesh(es)", - id, obj_num_verts, obj_num_idx, submeshes.len() - ); - - // TODO Create bounding boxes for each mesh and associate them with the struct. - mesh_list.insert( - id.clone(), - MeshHandle { - id, - vertex_loc: vert_start, - vertex_count: obj_num_verts, - index_loc: index_start, - index_count: obj_num_idx, - submeshes, - }, - ); + meshes.into_iter().for_each(|(id, info)| { + let handle = Self::append_mesh(&id, info, &mut self.raw_vertices, &mut self.raw_indices); + mesh_list.insert(id, handle); }); - // Allocate buffers for the information. - let vertex_buffer = Self::alloc_vert_buffer(allocator.clone(), buf_verts); - let index_buffer = Self::alloc_index_buffer(allocator.clone(), buf_indices); - self.meshes = mesh_list; - self.vertex_buffer = Some(vertex_buffer); - self.index_buffer = Some(index_buffer); + self.rebuild_buffers(); + } + + /// (Re)allocates `vertex_buffer`/`index_buffer` from the currently accumulated + /// `raw_vertices`/`raw_indices`. + fn rebuild_buffers(&mut self) { + self.vertex_buffer = Some(Self::alloc_vert_buffer( + self.allocator.clone(), + self.raw_vertices.clone(), + )); + self.index_buffer = Some(Self::alloc_index_buffer( + self.allocator.clone(), + self.raw_indices.clone(), + )); } /// Allocates and writes to a vertex buffer with the given `Vec`. diff --git a/src/common/render_batch.rs b/src/common/render_batch.rs index d732f1f..3758166 100644 --- a/src/common/render_batch.rs +++ b/src/common/render_batch.rs @@ -1,5 +1,5 @@ +use crate::assets::asset_manager::AssetManager; use crate::common::instance::InstanceRegistry; -use crate::common::mesh::MeshRegistry; use crate::gpu::instance::GpuInstance; use std::sync::Arc; use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; @@ -17,13 +17,13 @@ impl RenderBatch { /// once per frame. pub fn build_batches( allocator: Arc, - mesh_registry: Arc, + asset_manager: Arc, instance_registry: Arc, ) -> Vec { - mesh_registry - .meshes + asset_manager + .meshes() .iter() - .filter_map(|(id, mesh)| { + .filter_map(|(id, _mesh)| { let instances = instance_registry.get_instances_for(id); if instances.is_empty() {