diff --git a/src/loaders/material/gltf_mat_loader.rs b/src/loaders/material/gltf_mat_loader.rs new file mode 100644 index 0000000..61ed187 --- /dev/null +++ b/src/loaders/material/gltf_mat_loader.rs @@ -0,0 +1,16 @@ +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 { + let (document, buffers, images) = gltf::import(path)?; + + // 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. + + todo!() + } +} diff --git a/src/loaders/material/mat_loader.rs b/src/loaders/material/mat_loader.rs new file mode 100644 index 0000000..37228af --- /dev/null +++ b/src/loaders/material/mat_loader.rs @@ -0,0 +1,83 @@ +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 { + #[error("file extension is missing")] + MissingExtension, + #[error("extension {0} is not supported")] + ExtensionNotSupported(String) +} + +/// A material loader struct that handles multiple different file types. +/// List of supported and planned file formats: +/// - `.gltf` +/// - `.mtl` (planned, not implemented) +pub struct MaterialLoader { + loaders: HashMap>, +} + +impl MaterialLoader { + /// Creates a new `MaterialLoader` struct. + pub fn new() -> Self { + let mut loaders = HashMap::new(); + loaders.insert("gltf".into(), Box::new(GltfMaterialLoader) as Box); + + 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 + /// on supported material files and which files to target. + pub fn load_material>(&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) + } +} + +/// Contains all information required for the application to load a new material. +pub struct MaterialInfo { + /// The ID of this material + pub name: String, + /// The material uniforms. These should be defined on every imported material. + pub uniforms: MaterialUniforms, + /// The ID of the base color map, if any. + pub base_color_map: Option, + /// The ID of the roughness map, if any. This is ignored for now. + pub roughness_map: Option, + /// The ID of the normal map, if any. This is ignored for now. + pub normal_map: Option, +} + +/// A struct composed of the material uniforms. These uniforms will typically scale the material +/// texture maps if there are any. Refer to [`MaterialInfo`] for more information. +pub struct MaterialUniforms { + /// The base color of the material. This scales the base color texture, if any. + /// This value must be specified in normalized RGBA format (all values between 0 and 1). + pub base_color: [f32; 4], + /// The metalness of the material. This scales the metalness texture, if any. This value should + /// be specified as a normalized float between 0 and 1, based on an 8-bit number space. + /// + /// This is ignored until the shaders are updated. + pub metalness_factor: f32, + /// The roughness of the material. This scales the roughness texture, if any. This value should + /// be specified as a normalized float between 0 and 1, based on an 8-bit number space. + /// + /// This is ignored until the shaders are updated. + pub roughness_factor: f32, +} + +/// 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; +} diff --git a/src/loaders/material/mod.rs b/src/loaders/material/mod.rs new file mode 100644 index 0000000..d88479d --- /dev/null +++ b/src/loaders/material/mod.rs @@ -0,0 +1,2 @@ +pub mod mat_loader; +pub mod gltf_mat_loader; diff --git a/src/loaders/gltf_loader.rs b/src/loaders/mesh/gltf_loader.rs similarity index 100% rename from src/loaders/gltf_loader.rs rename to src/loaders/mesh/gltf_loader.rs diff --git a/src/loaders/mesh_loader.rs b/src/loaders/mesh/mesh_loader.rs similarity index 80% rename from src/loaders/mesh_loader.rs rename to src/loaders/mesh/mesh_loader.rs index 9d015a9..f8fb12b 100644 --- a/src/loaders/mesh_loader.rs +++ b/src/loaders/mesh/mesh_loader.rs @@ -1,5 +1,5 @@ use super::gltf_loader::*; -use crate::loaders::obj_loader::ObjLoader; +use crate::loaders::mesh::obj_loader::ObjLoader; use anyhow::Result; use std::collections::HashMap; use std::path::Path; @@ -20,10 +20,6 @@ pub trait MeshFileLoader { fn load_mesh(&self, path: &Path) -> Result; } -pub struct MeshLoader { - loaders: HashMap>, -} - #[derive(Error, Debug)] pub enum MeshLoaderError { #[error("file extension is missing")] @@ -32,6 +28,14 @@ pub enum MeshLoaderError { ExtensionNotSupported(String), } +/// A mesh loader struct that handles multiple different file types. The list of supported and +/// planned file formats follows: +/// - `.gltf` +/// - `.obj` +pub struct MeshLoader { + loaders: HashMap>, +} + impl MeshLoader { pub fn new() -> Self { let mut loaders = HashMap::new(); @@ -48,7 +52,9 @@ impl MeshLoader { Self { loaders } } - /// Loads a mesh and returns a `Result` containing the information. + /// Loads a mesh from the given path. Note that materials are not automatically loaded through + /// this function, and must be manually loaded. Refer to the [`MeshLoader`] for more information + /// on what file types are supported. pub fn load_mesh>(&self, path: &P) -> Result { let ext = path .as_ref() @@ -70,11 +76,11 @@ pub mod test_load_meshes { use super::*; fn load_gltf(loader: &MeshLoader) -> Result { - loader.load_mesh(&Path::new("meshes/laat_gltf/scene.gltf")) + loader.load_mesh(&Path::new("../../../meshes/laat_gltf/scene.gltf")) } fn load_obj(loader: &MeshLoader) -> Result { - loader.load_mesh(&Path::new("meshes/laat_gunship/scene.obj")) + loader.load_mesh(&Path::new("../../../meshes/laat_gunship/scene.obj")) } #[test] diff --git a/src/loaders/mesh/mod.rs b/src/loaders/mesh/mod.rs new file mode 100644 index 0000000..a29e1a2 --- /dev/null +++ b/src/loaders/mesh/mod.rs @@ -0,0 +1,3 @@ +pub mod gltf_loader; +pub mod mesh_loader; +mod obj_loader; diff --git a/src/loaders/obj_loader.rs b/src/loaders/mesh/obj_loader.rs similarity index 100% rename from src/loaders/obj_loader.rs rename to src/loaders/mesh/obj_loader.rs diff --git a/src/loaders/mod.rs b/src/loaders/mod.rs index a29e1a2..266a8cb 100644 --- a/src/loaders/mod.rs +++ b/src/loaders/mod.rs @@ -1,3 +1,4 @@ -pub mod gltf_loader; -pub mod mesh_loader; -mod obj_loader; +pub mod mesh; +pub mod material; + +pub use mesh::*; \ No newline at end of file