gltf_kun 0.0.10

Graph-based glTF processing library.
Documentation
use petgraph::{graph::NodeIndex, visit::EdgeRef, Direction};

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

use super::{accessor::Accessor, material::Material, mesh::MeshEdge, GltfEdge, GltfWeight, Mesh};

pub use gltf::json::mesh::{Mode, Semantic};

mod morph_target;

pub use morph_target::*;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PrimitiveEdge {
    Attribute(Semantic),
    Indices,
    Material,
    MorphTarget(usize),
}

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

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

#[derive(Clone, Debug)]
pub struct PrimitiveWeight {
    pub extras: gltf::json::Extras,
    pub mode: Mode,
}

impl Default for PrimitiveWeight {
    fn default() -> Self {
        Self {
            extras: None,
            mode: Mode::Triangles,
        }
    }
}

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

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

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

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

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

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

impl GraphNodeWeight<PrimitiveWeight> for Primitive {}
impl GraphNodeEdges for Primitive {}
impl Extensions for Primitive {}

impl Primitive {
    pub fn attributes(&self, graph: &Graph) -> Vec<(Semantic, Accessor)> {
        graph
            .edges_directed(self.0, Direction::Outgoing)
            .filter_map(|edge| {
                if let Edge::Gltf(GltfEdge::Primitive(PrimitiveEdge::Attribute(semantic))) =
                    edge.weight()
                {
                    Some((semantic.clone(), Accessor(edge.target())))
                } else {
                    None
                }
            })
            .collect()
    }
    pub fn attribute(&self, graph: &Graph, semantic: Semantic) -> Option<Accessor> {
        self.find_edge_target(graph, &PrimitiveEdge::Attribute(semantic))
    }
    pub fn set_attribute(&self, graph: &mut Graph, semantic: Semantic, accessor: Option<Accessor>) {
        self.set_edge_target(graph, PrimitiveEdge::Attribute(semantic), accessor);
    }

    pub fn indices(&self, graph: &Graph) -> Option<Accessor> {
        self.find_edge_target(graph, &PrimitiveEdge::Indices)
    }
    pub fn set_indices(&self, graph: &mut Graph, indices: Option<Accessor>) {
        self.set_edge_target(graph, PrimitiveEdge::Indices, indices);
    }

    pub fn material(&self, graph: &Graph) -> Option<Material> {
        self.find_edge_target(graph, &PrimitiveEdge::Material)
    }
    pub fn set_material(&self, graph: &mut Graph, material: Option<Material>) {
        self.set_edge_target(graph, PrimitiveEdge::Material, material);
    }

    pub fn morph_targets(&self, graph: &Graph) -> Vec<MorphTarget> {
        let mut morph_targets = graph
            .edges_directed(self.0, Direction::Outgoing)
            .filter_map(|edge_ref| {
                let edge: &PrimitiveEdge = match edge_ref.weight().try_into() {
                    Ok(edge) => edge,
                    Err(_) => return None,
                };

                match edge {
                    PrimitiveEdge::MorphTarget(i) => {
                        Some((i, MorphTarget::from(edge_ref.target())))
                    }
                    _ => None,
                }
            })
            .collect::<Vec<_>>();

        morph_targets.sort_by_key(|(i, _)| *i);

        morph_targets.into_iter().map(|(_, node)| node).collect()
    }
    pub fn add_morph_target(&self, graph: &mut Graph, node: &MorphTarget, index: usize) {
        self.add_edge_target(graph, PrimitiveEdge::MorphTarget(index), *node);
    }
    pub fn remove_morph_target(&self, graph: &mut Graph, node: &MorphTarget) {
        let target_idx: NodeIndex = (*node).into();

        let found_edge = graph
            .edges_directed(self.0, Direction::Outgoing)
            .filter(|edge_ref| {
                let edge: &PrimitiveEdge = match edge_ref.weight().try_into() {
                    Ok(edge) => edge,
                    Err(_) => return false,
                };

                matches!(edge, PrimitiveEdge::MorphTarget(_))
            })
            .find(|edge_ref| edge_ref.target() == target_idx);

        if let Some(found_edge) = found_edge {
            graph.remove_edge(found_edge.id());
        }
    }
    pub fn create_morph_target(&self, graph: &mut Graph, index: usize) -> MorphTarget {
        let idx = graph.add_node(Weight::Gltf(GltfWeight::MorphTarget));
        self.add_edge_target(graph, PrimitiveEdge::MorphTarget(index), idx);
        MorphTarget(idx)
    }

    pub fn mesh(&self, graph: &Graph) -> Option<Mesh> {
        self.find_edge_source(graph, &MeshEdge::Primitive)
    }
}

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

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

        let primitive = Primitive::new(&mut graph);
        let indices = Accessor::new(&mut graph);

        primitive.set_indices(&mut graph, Some(indices));
        assert_eq!(primitive.indices(&graph), Some(indices));

        primitive.set_indices(&mut graph, None);
        assert_eq!(primitive.indices(&graph), None);
    }

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

        let primitive = Primitive::new(&mut graph);
        let position = Accessor::new(&mut graph);
        let normal = Accessor::new(&mut graph);

        primitive.set_attribute(&mut graph, Semantic::Positions, Some(position));
        assert_eq!(
            primitive.attribute(&graph, Semantic::Positions),
            Some(position)
        );

        primitive.set_attribute(&mut graph, Semantic::Normals, Some(normal));
        assert_eq!(primitive.attribute(&graph, Semantic::Normals), Some(normal));
        assert_eq!(primitive.attributes(&graph).len(), 2);

        primitive.set_attribute(&mut graph, Semantic::Normals, None);
        assert_eq!(primitive.attribute(&graph, Semantic::Normals), None);
        assert_eq!(primitive.attributes(&graph).len(), 1);

        primitive.set_attribute(&mut graph, Semantic::Positions, None);
        assert!(primitive.attribute(&graph, Semantic::Positions).is_none());
    }

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

        let primitive = Primitive::new(&mut graph);
        let material = Material::new(&mut graph);

        primitive.set_material(&mut graph, Some(material));
        assert_eq!(primitive.material(&graph), Some(material));

        primitive.set_material(&mut graph, None);
        assert!(primitive.material(&graph).is_none());
    }

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

        let primitive = Primitive::new(&mut graph);
        let target1 = MorphTarget::new(&mut graph);
        let target2 = MorphTarget::new(&mut graph);

        primitive.add_morph_target(&mut graph, &target1, 0);
        primitive.add_morph_target(&mut graph, &target2, 1);

        assert_eq!(primitive.morph_targets(&graph), vec![target1, target2]);

        primitive.remove_morph_target(&mut graph, &target1);
        assert_eq!(primitive.morph_targets(&graph), vec![target2]);

        let target3 = primitive.create_morph_target(&mut graph, 2);
        assert_eq!(primitive.morph_targets(&graph), vec![target2, target3]);
    }

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

        let mesh = Mesh::new(&mut graph);
        let primitive = mesh.create_primitive(&mut graph);
        assert_eq!(primitive.mesh(&graph), Some(mesh));

        mesh.remove_primitive(&mut graph, &primitive);
        assert!(primitive.mesh(&graph).is_none());
    }
}