2023-10-18 02:04:25 +00:00
|
|
|
use std::{fs::File, io::{BufReader, Read}, collections::hash_map::DefaultHasher, hash::{Hash, Hasher}};
|
|
|
|
|
|
|
|
use crate::{Texture, ResHandle, ResourceManager, util};
|
2023-09-29 17:00:33 +00:00
|
|
|
|
|
|
|
/// 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<gltf::material::PbrMetallicRoughness<'_>> 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
|
2023-10-18 02:04:25 +00:00
|
|
|
pub diffuse_color: glam::Vec4,
|
2023-09-29 17:00:33 +00:00
|
|
|
// The base color texture
|
|
|
|
// pub diffuse_texture // TODO
|
2023-10-18 02:04:25 +00:00
|
|
|
pub specular: glam::Vec3,
|
2023-09-29 17:00:33 +00:00
|
|
|
/// The glossiness factor of the material.
|
|
|
|
/// From 0.0 (no glossiness) to 1.0 (full glossiness)
|
|
|
|
pub glossiness: f32,
|
|
|
|
// pub glossiness_texture // TODO
|
|
|
|
}
|
|
|
|
|
2023-09-29 18:20:28 +00:00
|
|
|
impl From<gltf::material::PbrSpecularGlossiness<'_>> for PbrGlossiness {
|
|
|
|
fn from(value: gltf::material::PbrSpecularGlossiness) -> Self {
|
|
|
|
PbrGlossiness {
|
2023-10-18 02:04:25 +00:00
|
|
|
diffuse_color: value.diffuse_factor().into(),
|
|
|
|
specular: value.specular_factor().into(),
|
2023-09-29 18:20:28 +00:00
|
|
|
glossiness: value.glossiness_factor()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-18 02:04:25 +00:00
|
|
|
/// The alpha rendering mode of a material.
|
|
|
|
/// This is essentially a re-export of gltf::material::AlphaMode
|
|
|
|
#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)]
|
|
|
|
pub enum AlphaMode {
|
|
|
|
/// The alpha value is ignored and the rendered output is fully opaque.
|
|
|
|
#[default]
|
|
|
|
Opaque = 1,
|
|
|
|
|
|
|
|
/// The rendered output is either fully opaque or fully transparent depending on
|
|
|
|
/// the alpha value and the specified alpha cutoff value.
|
|
|
|
Mask,
|
|
|
|
|
|
|
|
/// The alpha value is used, to determine the transparency of the rendered output.
|
|
|
|
/// The alpha cutoff value is ignored.
|
|
|
|
Blend,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<gltf::material::AlphaMode> for AlphaMode {
|
|
|
|
fn from(value: gltf::material::AlphaMode) -> Self {
|
|
|
|
match value {
|
|
|
|
gltf::material::AlphaMode::Opaque => AlphaMode::Opaque,
|
|
|
|
gltf::material::AlphaMode::Mask => AlphaMode::Mask,
|
|
|
|
gltf::material::AlphaMode::Blend => AlphaMode::Blend,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-29 17:00:33 +00:00
|
|
|
#[derive(Clone, Default)]
|
|
|
|
pub struct Material {
|
|
|
|
pub shader_uuid: Option<u64>,
|
|
|
|
pub name: Option<String>,
|
|
|
|
pub double_sided: bool,
|
2023-10-18 02:04:25 +00:00
|
|
|
|
|
|
|
//pub pbr_roughness: PbrRoughness,
|
|
|
|
/// The RGBA base color of the model. If a texture is supplied with `base_color_texture`, this value
|
|
|
|
/// will tint the texture. If a texture is not provided, this value would be the color of the Material.
|
|
|
|
pub base_color: glam::Vec4,
|
|
|
|
/// 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,
|
|
|
|
/// The base color texture of the model.
|
|
|
|
pub base_color_texture: Option<ResHandle<Texture>>,
|
|
|
|
|
|
|
|
/// The metallic-roughness texture.
|
|
|
|
///
|
|
|
|
/// The metalness values are sampled from the B channel. The roughness values are sampled from
|
|
|
|
/// the G channel. These values are linear. If other channels are present (R or A), they are
|
|
|
|
/// ignored for metallic-roughness calculations.
|
|
|
|
pub metallic_roughness_texture: Option<ResHandle<Texture>>,
|
|
|
|
|
|
|
|
/// A set of parameter values that are used to define the specular-glossiness material model
|
|
|
|
/// from Physically-Based Rendering (PBR) methodology.
|
|
|
|
/// GLTF extension: [KHR_materials_pbrSpecularGlossiness](https://kcoley.github.io/glTF/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness)
|
2023-09-29 17:00:33 +00:00
|
|
|
pub pbr_glossiness: Option<PbrGlossiness>,
|
2023-10-18 02:04:25 +00:00
|
|
|
|
|
|
|
/// The optional alpha cutoff value of the material.
|
2023-09-29 17:00:33 +00:00
|
|
|
pub alpha_cutoff: Option<f32>,
|
|
|
|
|
2023-10-18 02:04:25 +00:00
|
|
|
/// The alpha rendering mode of the material. The material's alpha rendering
|
|
|
|
/// mode enumeration specifying the interpretation of the alpha value of the main
|
|
|
|
/// factor and texture.
|
|
|
|
///
|
|
|
|
/// * In `Opaque` mode (default) the alpha value is ignored
|
|
|
|
/// and the rendered output is fully opaque.
|
|
|
|
/// * In `Mask` mode, the rendered
|
|
|
|
/// output is either fully opaque or fully transparent depending on the alpha
|
|
|
|
/// value and the specified alpha cutoff value.
|
|
|
|
/// * In `Blend` mode, the alpha value is used to composite the source and
|
|
|
|
/// destination areas and the rendered output is combined with the background
|
|
|
|
/// using the normal painting operation (i.e. the Porter and Duff over
|
|
|
|
/// operator).
|
|
|
|
pub alpha_mode: AlphaMode,
|
|
|
|
|
|
|
|
//pub texture: Option<ResHandle<Texture>>,
|
2023-09-29 18:20:28 +00:00
|
|
|
}
|
|
|
|
|
2023-10-18 02:04:25 +00:00
|
|
|
impl Material {
|
|
|
|
/// Get a uri's identifier
|
|
|
|
///
|
|
|
|
/// I'm not actually sure how identifiable this would be
|
|
|
|
fn uri_ident(gltf_rel_path: &str, uri: &str) -> String {
|
|
|
|
let mut hasher = DefaultHasher::new();
|
|
|
|
uri.hash(&mut hasher);
|
|
|
|
let hash = hasher.finish();
|
|
|
|
|
|
|
|
format!("{gltf_rel_path};{hash}")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn source_ident(gltf_rel_path: &str, src: &gltf::image::Source) -> Option<String> {
|
|
|
|
match src {
|
|
|
|
gltf::image::Source::View { view, mime_type } => {
|
|
|
|
let buf = view.buffer();
|
|
|
|
let src = buf.source();
|
|
|
|
|
|
|
|
match src {
|
|
|
|
gltf::buffer::Source::Bin => None,
|
|
|
|
gltf::buffer::Source::Uri(uri) => {
|
|
|
|
Some(Material::uri_ident(gltf_rel_path, uri))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
gltf::image::Source::Uri { uri, mime_type } => {
|
|
|
|
Some(Material::uri_ident(gltf_rel_path, uri))
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_source(resource_manager: &mut ResourceManager, gltf_rel_path: &str, src: gltf::image::Source) -> Result<Vec<u8>, util::UriReadError> {
|
|
|
|
// TODO: Don't copy sources
|
|
|
|
match src {
|
|
|
|
gltf::image::Source::View { view, mime_type } => {
|
|
|
|
let buf = view.buffer();
|
|
|
|
let src = buf.source();
|
|
|
|
|
|
|
|
let offset = view.offset();
|
|
|
|
let len = view.length();
|
|
|
|
|
|
|
|
match src {
|
|
|
|
gltf::buffer::Source::Bin => todo!("Read material source from gltf Bin"),
|
|
|
|
gltf::buffer::Source::Uri(uri) => {
|
|
|
|
util::gltf_read_buffer_uri(gltf_rel_path, uri)
|
|
|
|
.map(|mut buf| {
|
|
|
|
buf.drain(0..offset);
|
|
|
|
buf.truncate(len);
|
|
|
|
buf
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
gltf::image::Source::Uri { uri, mime_type } => {
|
|
|
|
util::gltf_read_buffer_uri(gltf_rel_path, uri)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load_texture(resource_manager: &mut ResourceManager, gltf_rel_path: &str, texture_info: gltf::texture::Info<'_>) -> ResHandle<Texture> {
|
|
|
|
// TODO: texture_info.tex_coord()
|
|
|
|
let tex = texture_info.texture();
|
|
|
|
let img = tex.source();
|
|
|
|
let src = img.source();
|
|
|
|
|
|
|
|
let buf = Material::read_source(resource_manager, gltf_rel_path, src).unwrap();
|
|
|
|
let buflen = buf.len();
|
|
|
|
let mime_type = infer::get(&buf).expect("Failed to get file type").mime_type();
|
|
|
|
|
|
|
|
resource_manager.load_bytes::<Texture>(&uuid::Uuid::new_v4().to_string(), mime_type,
|
|
|
|
buf, 0, buflen).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Load the Material from a gltf::Material.
|
|
|
|
///
|
|
|
|
/// `gltf_rel_path`: The relative path of the gltf file,
|
|
|
|
/// e.g. gltf model path is "resource/models/player.gltf", the relative path would be "resource/models/"
|
|
|
|
pub fn from_gltf(resource_manager: &mut ResourceManager, gltf_rel_path: &str, gltf_mat: gltf::Material) -> Self {
|
|
|
|
let pbr_rough = gltf_mat.pbr_metallic_roughness();
|
|
|
|
let base_color = pbr_rough.base_color_factor().into();
|
|
|
|
let metallic = pbr_rough.metallic_factor();
|
|
|
|
let roughness = pbr_rough.roughness_factor();
|
|
|
|
|
|
|
|
let base_color_texture = pbr_rough.base_color_texture()
|
|
|
|
.map(|info| Material::load_texture(resource_manager, gltf_rel_path, info));
|
|
|
|
|
|
|
|
let metallic_roughness_texture = pbr_rough.metallic_roughness_texture()
|
|
|
|
.map(|info| Material::load_texture(resource_manager, gltf_rel_path, info));
|
|
|
|
|
|
|
|
/* let base_color_texture = if let Some(base_tex_info) = pbr_rough.base_color_texture() {
|
|
|
|
Some(Material::load_texture(resource_manager, gltf_rel_path, base_tex_info))
|
|
|
|
} else { None }; */
|
|
|
|
|
2023-09-29 18:20:28 +00:00
|
|
|
Material {
|
2023-10-18 02:04:25 +00:00
|
|
|
name: gltf_mat.name()
|
2023-09-29 18:20:28 +00:00
|
|
|
.map(|s| s.to_string()),
|
2023-10-18 02:04:25 +00:00
|
|
|
double_sided: gltf_mat.double_sided(),
|
|
|
|
base_color,
|
|
|
|
metallic,
|
|
|
|
roughness,
|
|
|
|
pbr_glossiness: gltf_mat.pbr_specular_glossiness()
|
2023-09-29 18:20:28 +00:00
|
|
|
.map(|o| o.into()),
|
2023-10-18 02:04:25 +00:00
|
|
|
alpha_cutoff: gltf_mat.alpha_cutoff(),
|
|
|
|
alpha_mode: gltf_mat.alpha_mode().into(),
|
2023-09-29 18:20:28 +00:00
|
|
|
shader_uuid: None,
|
2023-10-18 02:04:25 +00:00
|
|
|
|
|
|
|
// TODO
|
|
|
|
base_color_texture,
|
|
|
|
metallic_roughness_texture,
|
2023-09-29 18:20:28 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-29 17:00:33 +00:00
|
|
|
}
|