diff --git a/src/assets/asset_manager.rs b/src/assets/asset_manager.rs new file mode 100644 index 0000000..696560d --- /dev/null +++ b/src/assets/asset_manager.rs @@ -0,0 +1,57 @@ +use std::path::Path; +use std::sync::Arc; +use anyhow::Result; +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}; + +/// A manager that controls all assets used for display. It handles loading all meshes, materials, +/// and textures for now. +pub struct AssetManager { + /// The asset manager's [`MeshRegistry`]. + mesh_registry: MeshRegistry, + /// The asset manager's [`MaterialRegistry`]. + material_registry: MaterialRegistry, + + // Loaders + mesh_loader: MeshLoader, + material_loader: MaterialLoader, +} + +impl AssetManager { + /// Creates a new `AssetManager` instance. + pub fn new(allocator: Arc) -> Self { + let mesh_registry = MeshRegistry::new(allocator.clone()); + let material_registry = MaterialRegistry::new(); + Self { + mesh_registry, + material_registry, + mesh_loader: MeshLoader::new(), + material_loader: MaterialLoader::new(), + } + } + + /// Loads a full model from its file path. This loads the model's mesh, textures, and material + /// definitions, registers them with the `AssetLoader`'s registries, and prepares them for + /// display. Returns a blank `Result` indicating success or failure. + /// + /// 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)?; + + // Load and register the mesh using the materials + let mesh = self.mesh_loader.load_mesh(&path)?; + self.mesh_registry.register(mesh_id, mesh); + + Ok(()) + } + + /// Loads all information from a scene, returning an empty `Result` indicating success or failure. + pub fn load_scene>(&self, path: P) -> Result<()> { + Ok(()) + } +} diff --git a/src/loaders/material/gltf_mat_loader.rs b/src/assets/loaders/material/gltf_mat_loader.rs similarity index 54% rename from src/loaders/material/gltf_mat_loader.rs rename to src/assets/loaders/material/gltf_mat_loader.rs index 61ed187..da914ee 100644 --- a/src/loaders/material/gltf_mat_loader.rs +++ b/src/assets/loaders/material/gltf_mat_loader.rs @@ -1,12 +1,22 @@ +use crate::assets::loaders::material::mat_loader::{MaterialFileLoader, MaterialInfo}; use std::path::Path; -use crate::loaders::material::mat_loader::{MaterialFileLoader, MaterialInfo}; pub struct GltfMaterialLoader; impl MaterialFileLoader for GltfMaterialLoader { - fn load_material(&self, path: &Path) -> anyhow::Result { + fn load_materials(&self, path: &Path) -> anyhow::Result> { let (document, buffers, images) = gltf::import(path)?; + for material in document.materials() { + + } + + for scene in document.scenes() { + for node in scene.nodes() { + + } + } + // We just want to read through the information to see what kinds of materials are available. // I'm not sure how gltf defines materials, especially if they're applied to specific sets // of vertices. diff --git a/src/loaders/material/mat_loader.rs b/src/assets/loaders/material/mat_loader.rs similarity index 84% rename from src/loaders/material/mat_loader.rs rename to src/assets/loaders/material/mat_loader.rs index 37228af..a7b4789 100644 --- a/src/loaders/material/mat_loader.rs +++ b/src/assets/loaders/material/mat_loader.rs @@ -1,8 +1,8 @@ +use crate::assets::loaders::material::gltf_mat_loader::GltfMaterialLoader; +use anyhow::Result; use std::collections::HashMap; use std::path::Path; use thiserror::Error; -use anyhow::Result; -use crate::loaders::material::gltf_mat_loader::GltfMaterialLoader; #[derive(Error, Debug)] pub enum MaterialLoaderError { @@ -29,18 +29,16 @@ impl MaterialLoader { Self { loaders } } - /// Loads a material from the given path. Note that some materials are loaded through their - /// mesh's file (GLTF embeds materials, however OBJ files have a separate .mtl file). These - /// quirks must be handled differently. Refer to the [`MaterialLoader`] for more information + /// Loads all materials from the given path. Refer to the [`MaterialLoader`] for more information /// on supported material files and which files to target. - pub fn load_material>(&mut self, path: P) -> Result { + pub fn load_materials>(&mut self, path: P) -> Result> { let path = path.as_ref(); let file_ext = path.extension().and_then(|ext| ext.to_str()) .ok_or(MaterialLoaderError::MissingExtension)?; self.loaders.get(file_ext) .ok_or(MaterialLoaderError::ExtensionNotSupported(file_ext.to_string()))? - .load_material(path) + .load_materials(path) } } @@ -79,5 +77,5 @@ pub struct MaterialUniforms { /// A trait that must be implemented on all material loaders. Ensures consistent material /// import/load information. pub trait MaterialFileLoader { - fn load_material(&self, path: &Path) -> anyhow::Result; + fn load_materials(&self, path: &Path) -> anyhow::Result>; } diff --git a/src/loaders/material/mod.rs b/src/assets/loaders/material/mod.rs similarity index 100% rename from src/loaders/material/mod.rs rename to src/assets/loaders/material/mod.rs diff --git a/src/loaders/mesh/gltf_loader.rs b/src/assets/loaders/mesh/gltf_loader.rs similarity index 95% rename from src/loaders/mesh/gltf_loader.rs rename to src/assets/loaders/mesh/gltf_loader.rs index 845694c..a8cc03f 100644 --- a/src/loaders/mesh/gltf_loader.rs +++ b/src/assets/loaders/mesh/gltf_loader.rs @@ -1,8 +1,8 @@ -use crate::loaders::mesh_loader::{MeshFileLoader, MeshInfo}; +use crate::assets::loaders::mesh_loader::{MeshFileLoader, MeshInfo}; use anyhow::Result; -use std::path::Path; use gltf::buffer::Data; use nalgebra::{Matrix4, Vector4}; +use std::path::Path; pub struct GltfLoader; @@ -58,6 +58,9 @@ impl GltfLoader { indices.push(index + vert_offset); } } + + primitive.material().pbr_metallic_roughness() + .metallic_factor(); } } diff --git a/src/loaders/mesh/mesh_loader.rs b/src/assets/loaders/mesh/mesh_loader.rs similarity index 95% rename from src/loaders/mesh/mesh_loader.rs rename to src/assets/loaders/mesh/mesh_loader.rs index f8fb12b..33087e4 100644 --- a/src/loaders/mesh/mesh_loader.rs +++ b/src/assets/loaders/mesh/mesh_loader.rs @@ -1,5 +1,5 @@ use super::gltf_loader::*; -use crate::loaders::mesh::obj_loader::ObjLoader; +use crate::assets::loaders::mesh::obj_loader::ObjLoader; use anyhow::Result; use std::collections::HashMap; use std::path::Path; @@ -33,10 +33,12 @@ pub enum MeshLoaderError { /// - `.gltf` /// - `.obj` pub struct MeshLoader { + /// The list of mesh loaders. loaders: HashMap>, } impl MeshLoader { + /// Creates a new `MeshLoader` instance. pub fn new() -> Self { let mut loaders = HashMap::new(); loaders.insert( diff --git a/src/loaders/mesh/mod.rs b/src/assets/loaders/mesh/mod.rs similarity index 100% rename from src/loaders/mesh/mod.rs rename to src/assets/loaders/mesh/mod.rs diff --git a/src/loaders/mesh/obj_loader.rs b/src/assets/loaders/mesh/obj_loader.rs similarity index 88% rename from src/loaders/mesh/obj_loader.rs rename to src/assets/loaders/mesh/obj_loader.rs index d9a1683..e1473e2 100644 --- a/src/loaders/mesh/obj_loader.rs +++ b/src/assets/loaders/mesh/obj_loader.rs @@ -1,5 +1,4 @@ -use crate::gpu::vertex3::Vertex3; -use crate::loaders::mesh_loader::{MeshFileLoader, MeshInfo}; +use crate::assets::loaders::mesh_loader::{MeshFileLoader, MeshInfo}; use std::path::Path; use wavefront::Obj; diff --git a/src/loaders/mod.rs b/src/assets/loaders/mod.rs similarity index 100% rename from src/loaders/mod.rs rename to src/assets/loaders/mod.rs diff --git a/src/assets/mod.rs b/src/assets/mod.rs new file mode 100644 index 0000000..e6b87a5 --- /dev/null +++ b/src/assets/mod.rs @@ -0,0 +1,2 @@ +pub mod asset_manager; +pub(crate) mod loaders; diff --git a/src/common/material.rs b/src/common/material.rs index 8cae542..71b696c 100644 --- a/src/common/material.rs +++ b/src/common/material.rs @@ -1,13 +1,15 @@ use std::collections::HashMap; +use std::sync::Arc; +use vulkano::memory::allocator::MemoryAllocator; pub struct MaterialRegistry { - materials: HashMap, + materials: HashMap } impl MaterialRegistry { pub fn new() -> Self { Self { - materials: HashMap::new(), + materials: HashMap::new() } } diff --git a/src/common/mesh.rs b/src/common/mesh.rs index 2e92ace..99b3c6d 100644 --- a/src/common/mesh.rs +++ b/src/common/mesh.rs @@ -1,24 +1,19 @@ +use crate::assets::loaders::mesh_loader::{MeshInfo, MeshLoader}; use crate::common::scene::Scene; use crate::gpu::vertex3::Vertex3; -use crate::loaders::mesh_loader::{MeshInfo, MeshLoader}; use std::collections::HashMap; use std::sync::Arc; use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter}; -use wavefront::Obj; /// This struct is slightly misleading - it doesn't actually contain the mesh information, but /// the locations of the mesh's information in the overall application's vertex buffer, index buffer, /// and other such structures. /// -/// Once loaded, the mesh should be accessible via ID through a registry-type struct, which simply -/// maintains the list of meshes that have been loaded by the application so far and which assigns -/// the correct indices for buffer-to-render logic. -/// -/// That means that this `Mesh` struct is actually just an entry within that registry. They should -/// rarely be used directly, and should never be edited. +/// That means that this `MeshHandle` struct is actually just an entry within that registry. They +/// should never be edited or created manually. #[derive(Debug)] -pub struct Mesh { +pub struct MeshHandle { pub id: String, pub vertex_loc: usize, pub vertex_count: usize, @@ -26,26 +21,48 @@ pub struct Mesh { pub index_count: usize, } -impl Mesh {} +impl MeshHandle {} + +type VertexBuffer = Arc>; +type IndexBuffer = Arc>; /// # Mesh Registry -/// A wrapper around a series of [`Mesh`] structs, keyed by a unique ID, and the buffers that are +/// A wrapper around a series of [`MeshHandle`] structs, keyed by a unique ID, and the buffers that are /// used by the rendering pipeline. #[derive(Debug)] pub struct MeshRegistry { - pub meshes: HashMap, + /// The map of mesh IDs and their display handles. + pub meshes: HashMap, + /// The memory allocator this registry should use. allocator: Arc, - pub vertex_buffer: Arc>, - pub index_buffer: Arc>, + /// The vertex buffer allocated for all registered meshes. + pub vertex_buffer: Option, + /// The index buffer allocated for all registered meshes. + pub index_buffer: Option, } impl MeshRegistry { + /// Creates a new, blank `MeshRegistry` instance. + pub fn new(allocator: Arc) -> Self { + Self { + meshes: HashMap::new(), + vertex_buffer: None, + index_buffer: None, + 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); + } + /// Creates a `MeshRegistry` from the given [`Scene`]. /// /// This method will load all the meshes indicated by the scene and allocate the required /// buffers. This significantly simplifies the allocation process, as the buffers only need to /// be allocated once. - pub fn from_scene(scene: &Scene, allocator: Arc) -> Self { + pub fn load_scene(&mut self, scene: &Scene, allocator: Arc) { // Load all the meshes into a single map. println!("Loading meshes from scene"); @@ -93,7 +110,7 @@ impl MeshRegistry { // TODO Create bounding boxes for each mesh and associate them with the struct. mesh_list.insert( id.clone(), - Mesh { + MeshHandle { id, vertex_loc: vert_start, vertex_count: obj_num_verts, @@ -107,20 +124,16 @@ impl MeshRegistry { let vertex_buffer = Self::alloc_vert_buffer(allocator.clone(), buf_verts); let index_buffer = Self::alloc_index_buffer(allocator.clone(), buf_indices); - // Finally, return the struct. - Self { - meshes: mesh_list, - vertex_buffer, - index_buffer, - allocator, - } + self.meshes = mesh_list; + self.vertex_buffer = Some(vertex_buffer); + self.index_buffer = Some(index_buffer); } /// Allocates and writes to a vertex buffer with the given `Vec`. fn alloc_vert_buffer( alloc: Arc, verts: Vec, - ) -> Arc> { + ) -> VertexBuffer { println!("Allocating vertex buffer"); Arc::new( Buffer::from_iter( @@ -144,7 +157,7 @@ impl MeshRegistry { fn alloc_index_buffer( alloc: Arc, indices: Vec, - ) -> Arc> { + ) -> IndexBuffer { println!("Allocating index buffer"); Arc::new( Buffer::from_iter( @@ -164,8 +177,13 @@ impl MeshRegistry { ) } - /// Attempts to find a [`Mesh`] with the given ID, returning `None` if it's not found. - pub fn get(&self, uuid: &String) -> Option<&Mesh> { + /// Attempts to find a [`MeshHandle`] with the given ID, returning `None` if it's not found. + pub fn get(&self, uuid: &String) -> Option<&MeshHandle> { self.meshes.get(uuid) } + + /// Returns a boolean indicating whether the requested [`MeshHandle`] exists. + pub fn exists(&self, uuid: &String) -> bool { + self.get(uuid).is_some() + } } diff --git a/src/common/mod.rs b/src/common/mod.rs index 769b9e3..e804f93 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,6 +1,6 @@ pub mod camera; pub mod instance; -mod material; +pub mod material; pub mod mesh; pub mod render_batch; pub mod scene; diff --git a/src/main.rs b/src/main.rs index 9adabb6..c6565a9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ mod app; mod common; pub mod gpu; -pub mod loaders; +pub mod assets; // pub mod math; use crate::app::App; diff --git a/test_scene.scene b/test_scene.scene index 2608faa..f130650 100644 --- a/test_scene.scene +++ b/test_scene.scene @@ -1,13 +1,12 @@ { "name": "Testing Scene", "mesh_paths": { - "gunship": "meshes/laat_gunship/scene.obj", "trooper": "meshes/clone_trooper/scene.gltf", - "destroyer": "../meshes/sw_isd.glb", - "interceptor": "../meshes/sw_tie_interceptor.glb" + "gunship": "meshes/laat_gltf/scene.gltf", + "helmet": "meshes/DamagedHelmet.glb" }, "instances": { - "interceptor": { + "gunship": { "translation": [ 0.0, 0.0, @@ -15,7 +14,7 @@ ], "rotation": [ 0.0, - 0.0, + 3.14, 0.0 ], "scale": [