gltf_kun 0.0.10

Graph-based glTF processing library.
Documentation
use petgraph::graph::NodeIndex;

use crate::graph::{Edge, Extensions, Graph, GraphNodeEdges, GraphNodeWeight, Weight};

use super::{GltfEdge, GltfWeight, Texture};

pub use gltf::json::material::{AlphaCutoff, AlphaMode};

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MaterialEdge {
    BaseColorTexture,
    EmissiveTexture,
    MetallicRoughnessTexture,
    NormalTexture,
    OcclusionTexture,
}

impl<'a> TryFrom<&'a Edge> for &'a MaterialEdge {
    type Error = ();
    fn try_from(value: &'a Edge) -> Result<Self, Self::Error> {
        match value {
            Edge::Gltf(GltfEdge::Material(edge)) => Ok(edge),
            _ => Err(()),
        }
    }
}

impl From<MaterialEdge> for Edge {
    fn from(edge: MaterialEdge) -> Self {
        Self::Gltf(GltfEdge::Material(edge))
    }
}

#[derive(Clone, Debug)]
pub struct MaterialWeight {
    pub name: Option<String>,
    pub extras: gltf::json::Extras,

    pub alpha_cutoff: AlphaCutoff,
    pub alpha_mode: AlphaMode,
    pub base_color_factor: [f32; 4],
    pub double_sided: bool,
    pub emissive_factor: [f32; 3],
    pub metallic_factor: f32,
    pub normal_scale: f32,
    pub occlusion_strength: f32,
    pub roughness_factor: f32,

    pub base_color_tex_coord: usize,
    pub emissive_tex_coord: usize,
    pub metallic_roughness_tex_coord: usize,
    pub normal_tex_coord: usize,
    pub occlusion_tex_coord: usize,
}

impl Default for MaterialWeight {
    fn default() -> Self {
        Self {
            name: None,
            extras: Default::default(),

            alpha_cutoff: AlphaCutoff::default(),
            alpha_mode: AlphaMode::default(),
            base_color_factor: [1.0, 1.0, 1.0, 1.0],
            double_sided: false,
            emissive_factor: [0.0, 0.0, 0.0],
            metallic_factor: 1.0,
            normal_scale: 1.0,
            occlusion_strength: 1.0,
            roughness_factor: 1.0,

            base_color_tex_coord: 0,
            emissive_tex_coord: 0,
            metallic_roughness_tex_coord: 0,
            normal_tex_coord: 0,
            occlusion_tex_coord: 0,
        }
    }
}

impl From<MaterialWeight> for Weight {
    fn from(weight: MaterialWeight) -> Self {
        Self::Gltf(GltfWeight::Material(weight))
    }
}

impl<'a> TryFrom<&'a Weight> for &'a MaterialWeight {
    type Error = ();
    fn try_from(value: &'a Weight) -> Result<Self, Self::Error> {
        match value {
            Weight::Gltf(GltfWeight::Material(weight)) => Ok(weight),
            _ => Err(()),
        }
    }
}

impl<'a> TryFrom<&'a mut Weight> for &'a mut MaterialWeight {
    type Error = ();
    fn try_from(value: &'a mut Weight) -> Result<Self, Self::Error> {
        match value {
            Weight::Gltf(GltfWeight::Material(weight)) => Ok(weight),
            _ => Err(()),
        }
    }
}

#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Material(pub NodeIndex);

impl From<NodeIndex> for Material {
    fn from(index: NodeIndex) -> Self {
        Self(index)
    }
}

impl From<Material> for NodeIndex {
    fn from(material: Material) -> Self {
        material.0
    }
}

impl GraphNodeWeight<MaterialWeight> for Material {}
impl GraphNodeEdges for Material {}
impl Extensions for Material {}

impl Material {
    pub fn base_color_texture(&self, graph: &Graph) -> Option<Texture> {
        self.find_edge_target(graph, &MaterialEdge::BaseColorTexture)
    }
    pub fn set_base_color_texture(&self, graph: &mut Graph, texture: Option<Texture>) {
        self.set_edge_target(graph, MaterialEdge::BaseColorTexture, texture);
    }

    pub fn emissive_texture(&self, graph: &Graph) -> Option<Texture> {
        self.find_edge_target(graph, &MaterialEdge::EmissiveTexture)
    }
    pub fn set_emissive_texture(&self, graph: &mut Graph, texture: Option<Texture>) {
        self.set_edge_target(graph, MaterialEdge::EmissiveTexture, texture);
    }

    pub fn metallic_roughness_texture(&self, graph: &Graph) -> Option<Texture> {
        self.find_edge_target(graph, &MaterialEdge::MetallicRoughnessTexture)
    }
    pub fn set_metallic_roughness_texture(&self, graph: &mut Graph, texture: Option<Texture>) {
        self.set_edge_target(graph, MaterialEdge::MetallicRoughnessTexture, texture);
    }

    pub fn normal_texture(&self, graph: &Graph) -> Option<Texture> {
        self.find_edge_target(graph, &MaterialEdge::NormalTexture)
    }
    pub fn set_normal_texture(&self, graph: &mut Graph, texture: Option<Texture>) {
        self.set_edge_target(graph, MaterialEdge::NormalTexture, texture);
    }

    pub fn occlusion_texture(&self, graph: &Graph) -> Option<Texture> {
        self.find_edge_target(graph, &MaterialEdge::OcclusionTexture)
    }
    pub fn set_occlusion_texture(&self, graph: &mut Graph, texture: Option<Texture>) {
        self.set_edge_target(graph, MaterialEdge::OcclusionTexture, texture);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn base_color_texture() {
        let graph = &mut Graph::default();

        let material = Material::new(graph);
        let texture = Texture::new(graph);

        material.set_base_color_texture(graph, Some(texture));
        assert_eq!(material.base_color_texture(graph), Some(texture));

        material.set_base_color_texture(graph, None);
        assert!(material.base_color_texture(graph).is_none());
    }

    #[test]
    fn emmissive_texture() {
        let graph = &mut Graph::default();

        let material = Material::new(graph);
        let texture = Texture::new(graph);

        material.set_emissive_texture(graph, Some(texture));
        assert_eq!(material.emissive_texture(graph), Some(texture));

        material.set_emissive_texture(graph, None);
        assert!(material.emissive_texture(graph).is_none());
    }

    #[test]
    fn metallic_roughness_texture() {
        let graph = &mut Graph::default();

        let material = Material::new(graph);
        let texture = Texture::new(graph);

        material.set_metallic_roughness_texture(graph, Some(texture));
        assert_eq!(material.metallic_roughness_texture(graph), Some(texture));

        material.set_metallic_roughness_texture(graph, None);
        assert!(material.metallic_roughness_texture(graph).is_none());
    }

    #[test]
    fn normal_texture() {
        let graph = &mut Graph::default();

        let material = Material::new(graph);
        let texture = Texture::new(graph);

        material.set_normal_texture(graph, Some(texture));
        assert_eq!(material.normal_texture(graph), Some(texture));

        material.set_normal_texture(graph, None);
        assert!(material.normal_texture(graph).is_none());
    }

    #[test]
    fn occlusion_texture() {
        let graph = &mut Graph::default();

        let material = Material::new(graph);
        let texture = Texture::new(graph);

        material.set_occlusion_texture(graph, Some(texture));
        assert_eq!(material.occlusion_texture(graph), Some(texture));

        material.set_occlusion_texture(graph, None);
        assert!(material.occlusion_texture(graph).is_none());
    }
}