diff --git a/lyra-resource/Cargo.toml b/lyra-resource/Cargo.toml index 25cea1b..f5a7799 100644 --- a/lyra-resource/Cargo.toml +++ b/lyra-resource/Cargo.toml @@ -10,7 +10,7 @@ anyhow = "1.0.75" base64 = "0.21.4" edict = "0.5.0" glam = "0.24.1" -gltf = "1.3.0" +gltf = { version = "1.3.0", features = ["KHR_materials_pbrSpecularGlossiness"] } image = "0.24.7" percent-encoding = "2.3.0" thiserror = "1.0.48" diff --git a/lyra-resource/src/lib.rs b/lyra-resource/src/lib.rs index d1a4deb..ccdbdc1 100644 --- a/lyra-resource/src/lib.rs +++ b/lyra-resource/src/lib.rs @@ -12,3 +12,6 @@ pub use loader::*; pub mod model; pub use model::*; + +pub mod material; +pub use material::*; \ No newline at end of file diff --git a/lyra-resource/src/loader/model.rs b/lyra-resource/src/loader/model.rs index a85f12e..a5427e6 100644 --- a/lyra-resource/src/loader/model.rs +++ b/lyra-resource/src/loader/model.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use base64::Engine; -use crate::{ResourceLoader, LoaderError, Mesh, Model, MeshVertexAttribute, VertexAttributeData, Resource}; +use crate::{ResourceLoader, LoaderError, Mesh, Model, MeshVertexAttribute, VertexAttributeData, Resource, Material, PbrRoughness}; impl From for LoaderError { fn from(value: gltf::Error) -> Self { @@ -30,7 +30,7 @@ impl ModelLoader { } } - fn process_node(&self, buffers: &Vec>, node: gltf::Node<'_>) -> Vec { + fn process_node(&self, buffers: &Vec>, materials: &Vec, node: gltf::Node<'_>) -> Vec { let mut meshes = vec![]; if let Some(mesh) = node.mesh() { for prim in mesh.primitives() { @@ -73,12 +73,14 @@ impl ModelLoader { new_mesh.indices = Some(indices); } + prim.material(). + meshes.push(new_mesh); } } for child in node.children() { - let mut child_meshes = self.process_node(buffers, child); + let mut child_meshes = self.process_node(buffers, materials, child); meshes.append(&mut child_meshes); } @@ -109,10 +111,18 @@ impl ResourceLoader for ModelLoader { // TODO: Read in multiple scenes let scene = gltf.scenes().next().unwrap(); - // TODO: materials + // Load the materials + let materials: Vec = gltf.materials() + .map(|mat| Material { + double_sided: mat.double_sided(), + name: mat.name().map(|s| s.to_string()), + shader_uuid: None, + pbr: Some(PbrRoughness::from(mat.pbr_metallic_roughness())), + texture: None + }).collect(); let meshes: Vec = scene.nodes() - .map(|node| self.process_node(&buffers, node)) + .map(|node| self.process_node(&buffers, &materials, node)) .flatten().collect(); Ok(Arc::new(Resource::with_data(path, Model::new(meshes)))) diff --git a/lyra-resource/src/material.rs b/lyra-resource/src/material.rs new file mode 100644 index 0000000..0a566c4 --- /dev/null +++ b/lyra-resource/src/material.rs @@ -0,0 +1,51 @@ +use crate::{Texture, ResHandle}; + +/// PBR metallic roughness +#[derive(Clone, Debug, Default)] +pub struct PbrRoughness { + /// The rgba base color of the PBR material + pub base_color: [f32; 4], + /// The metalness of the material + /// From 0.0 (non-metal) to 1.0 (metal) + pub metallic: f32, + /// The roughness of the material + /// From 0.0 (smooth) to 1.0 (rough) + pub roughness: f32, + // TODO: base_color_texture and metallic_roughness_texture +} + +impl From> for PbrRoughness { + fn from(value: gltf::material::PbrMetallicRoughness) -> Self { + PbrRoughness { + base_color: value.base_color_factor(), + metallic: value.metallic_factor(), + roughness: value.roughness_factor(), + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct PbrGlossiness { + /// The rgba diffuse color of the material + pub diffuse_color: [f32; 4], + // The base color texture + // pub diffuse_texture // TODO + pub specular: [f32; 3], + /// The glossiness factor of the material. + /// From 0.0 (no glossiness) to 1.0 (full glossiness) + pub glossiness: f32, + // pub glossiness_texture // TODO +} + +#[derive(Clone, Default)] +pub struct Material { + pub shader_uuid: Option, + pub name: Option, + pub double_sided: bool, + pub pbr_roughness: PbrRoughness, + pub pbr_glossiness: Option, + pub alpha_cutoff: Option, + pub alpha_mode: gltf::material::AlphaMode, + + pub texture: Option>, +} \ No newline at end of file diff --git a/lyra-resource/src/model.rs b/lyra-resource/src/model.rs index f181efc..104ab84 100644 --- a/lyra-resource/src/model.rs +++ b/lyra-resource/src/model.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; +use crate::{Material, ResHandle}; + #[repr(C)] #[derive(Clone, Debug, PartialEq)] pub enum VertexAttributeData { @@ -35,6 +37,8 @@ pub enum MeshVertexAttribute { TexCoords, Weights, // TODO: Animation MorphTargets, // TODO: Animation + /// Used during loading of the Mesh to process the materials taht it + MaterialRef, Other(String), } @@ -42,6 +46,7 @@ pub enum MeshVertexAttribute { pub struct Mesh { pub attributes: HashMap, pub indices: Option>, + material: Option>, } impl Mesh { @@ -70,6 +75,10 @@ impl Mesh { self.attributes.get(&MeshVertexAttribute::TexCoords) .map(|p| p.as_vec2()) } + + pub fn material(&self) -> ResHandle { + self.material.clone().expect("This mesh is missing a material!") + } } #[derive(Clone, Default)] diff --git a/lyra-resource/src/resource.rs b/lyra-resource/src/resource.rs index dd70bac..331d5f8 100644 --- a/lyra-resource/src/resource.rs +++ b/lyra-resource/src/resource.rs @@ -16,6 +16,9 @@ pub struct Resource { pub state: ResourceState, } +/// A helper type to make it easier to use resources +pub type ResHandle = Arc>; + impl Resource { /// Create the resource with data, its assumed the state is `Ready` pub fn with_data(path: &str, data: T) -> Self {