gltf-reader 0.1.0

A simple glTF 2.0 reader using `serde` and `serde_json`
Documentation
use alloc::collections::BTreeMap;
use alloc::format;
use alloc::vec::Vec;

use alloc::borrow::Cow;
use ownable::IntoOwned;
use serde::Deserialize;

use crate::accessor::Accessor;
use crate::material::Material;
use crate::{Extensions, Extras, Idx};

/// glTF known attribute semantics.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttributeEnum {
    Position,
    Normal,
    Tangent,
    Texcoord(u16),
    Color(u16),
    Joints(u16),
    Weights(u16),
}

/// A vertex attribute semantic.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, IntoOwned)]
#[serde(transparent)]
pub struct Attribute<'a>(#[serde(borrow)] pub Cow<'a, str>);

impl core::fmt::Debug for Attribute<'_> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        if let Some(e) = self.to_enum() {
            e.fmt(f)
        } else {
            self.0.fmt(f)
        }
    }
}

impl Attribute<'_> {
    pub const POSITION: Self = Self(Cow::Borrowed("POSITION"));
    pub const NORMAL: Self = Self(Cow::Borrowed("NORMAL"));
    pub const TANGENT: Self = Self(Cow::Borrowed("TANGENT"));

    const PREFIX_TEXCOORD: &'static str = "TEXCOORD";
    const PREFIX_COLOR: &'static str = "COLOR";
    const PREFIX_JOINTS: &'static str = "JOINTS";
    const PREFIX_WEIGHTS: &'static str = "WEIGHTS";

    pub fn to_enum(&self) -> Option<AttributeEnum> {
        if *self == Self::POSITION {
            return Some(AttributeEnum::Position);
        } else if *self == Self::NORMAL {
            return Some(AttributeEnum::Normal);
        } else if *self == Self::TANGENT {
            return Some(AttributeEnum::Tangent);
        }

        if let Some((prefix, n)) = self.0.rsplit_once('_') {
            let Ok(n) = n.parse::<u16>() else {
                return None;
            };

            match prefix {
                Self::PREFIX_TEXCOORD => return Some(AttributeEnum::Texcoord(n)),
                Self::PREFIX_COLOR => return Some(AttributeEnum::Color(n)),
                Self::PREFIX_JOINTS => return Some(AttributeEnum::Joints(n)),
                Self::PREFIX_WEIGHTS => return Some(AttributeEnum::Weights(n)),
                _ => (),
            }
        }

        None
    }

    pub fn texcoord(n: u16) -> Self {
        Self(Cow::Owned(format!("{}{n}", Self::PREFIX_TEXCOORD)))
    }

    pub fn color(n: u16) -> Self {
        Self(Cow::Owned(format!("{}{n}", Self::PREFIX_COLOR)))
    }

    pub fn joints(n: u16) -> Self {
        Self(Cow::Owned(format!("{}{n}", Self::PREFIX_JOINTS)))
    }

    pub fn weights(n: u16) -> Self {
        Self(Cow::Owned(format!("{}{n}", Self::PREFIX_WEIGHTS)))
    }
}

/// glTF known primitive topologies.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModeEnum {
    Points,
    Lines,
    LineLoop,
    LineStrip,
    Triangles,
    TriangleStrip,
    TriangleFan,
}

/// The topology type of a set primitives to render.
#[derive(Clone, Copy, PartialEq, Eq, Deserialize, IntoOwned)]
#[serde(transparent)]
pub struct Mode(pub u64);

impl Default for Mode {
    fn default() -> Self {
        Self::TRIANGLES
    }
}

impl core::fmt::Debug for Mode {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        if let Some(e) = self.to_enum() {
            e.fmt(f)
        } else {
            self.0.fmt(f)
        }
    }
}

impl Mode {
    pub const POINTS: Self = Self(0);
    pub const LINES: Self = Self(1);
    pub const LINE_LOOP: Self = Self(2);
    pub const LINE_STRIP: Self = Self(3);
    pub const TRIANGLES: Self = Self(4);
    pub const TRIANGLE_STRIP: Self = Self(5);
    pub const TRIANGLE_FAN: Self = Self(6);

    pub fn to_enum(self) -> Option<ModeEnum> {
        Some(match self {
            Self::POINTS => ModeEnum::Points,
            Self::LINES => ModeEnum::Lines,
            Self::LINE_LOOP => ModeEnum::LineLoop,
            Self::LINE_STRIP => ModeEnum::LineStrip,
            Self::TRIANGLES => ModeEnum::Triangles,
            Self::TRIANGLE_STRIP => ModeEnum::TriangleStrip,
            Self::TRIANGLE_FAN => ModeEnum::TriangleFan,
            _ => return None,
        })
    }
}

/// A set of primitives to be rendered.
#[derive(Debug, Clone, Deserialize, IntoOwned)]
pub struct Primitive<'a> {
    /// A map where each key corresponds to a mesh attribute semantic and each value is the index of
    /// the accessor containing the attribute's data.
    #[serde(borrow = "'a")]
    pub attributes: BTreeMap<Attribute<'a>, Idx<Accessor<'static>>>,
    /// The index of the accessor that contains the vertex indices.
    pub indices: Option<Idx<Accessor<'static>>>,
    /// The index of the material to apply to this primitive when rendering.
    pub material: Option<Idx<Material<'static>>>,
    /// The topology type of primitives to render.
    #[serde(default)]
    pub mode: Mode,
    /// An array of morph targets.
    pub targets: Option<Vec<BTreeMap<Cow<'a, str>, Idx<Accessor<'static>>>>>,

    #[serde(borrow)]
    pub extensions: Option<Extensions<'a>>,
    #[serde(borrow)]
    pub extras: Option<Extras<'a>>,
}

/// A set of primitives to be rendered.
#[derive(Debug, Clone, Deserialize, IntoOwned)]
pub struct Mesh<'a> {
    /// The user-defined name of this object.
    #[serde(borrow)]
    pub name: Option<Cow<'a, str>>,

    /// An array of primitives, each defining geometry to be rendered.
    #[serde(borrow)]
    pub primitives: Vec<Primitive<'a>>,
    /// Array of weights to be applied to the morph targets.
    pub weights: Option<Vec<f32>>,

    #[serde(borrow)]
    pub extensions: Option<Extensions<'a>>,
    #[serde(borrow)]
    pub extras: Option<Extras<'a>>,
}