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};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttributeEnum {
Position,
Normal,
Tangent,
Texcoord(u16),
Color(u16),
Joints(u16),
Weights(u16),
}
#[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)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModeEnum {
Points,
Lines,
LineLoop,
LineStrip,
Triangles,
TriangleStrip,
TriangleFan,
}
#[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,
})
}
}
#[derive(Debug, Clone, Deserialize, IntoOwned)]
pub struct Primitive<'a> {
#[serde(borrow = "'a")]
pub attributes: BTreeMap<Attribute<'a>, Idx<Accessor<'static>>>,
pub indices: Option<Idx<Accessor<'static>>>,
pub material: Option<Idx<Material<'static>>>,
#[serde(default)]
pub mode: Mode,
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>>,
}
#[derive(Debug, Clone, Deserialize, IntoOwned)]
pub struct Mesh<'a> {
#[serde(borrow)]
pub name: Option<Cow<'a, str>>,
#[serde(borrow)]
pub primitives: Vec<Primitive<'a>>,
pub weights: Option<Vec<f32>>,
#[serde(borrow)]
pub extensions: Option<Extensions<'a>>,
#[serde(borrow)]
pub extras: Option<Extras<'a>>,
}