use crate::validation::{Checked, Error};
use crate::{accessor, extensions, material, Extras, Index};
use gltf_derive::Validate;
use serde::{de, ser};
use serde_derive::{Deserialize, Serialize};
use serde_json::from_value;
use std::collections::BTreeMap;
use std::fmt;
pub const POINTS: u32 = 0;
pub const LINES: u32 = 1;
pub const LINE_LOOP: u32 = 2;
pub const LINE_STRIP: u32 = 3;
pub const TRIANGLES: u32 = 4;
pub const TRIANGLE_STRIP: u32 = 5;
pub const TRIANGLE_FAN: u32 = 6;
pub const VALID_MODES: &[u32] = &[
POINTS,
LINES,
LINE_LOOP,
LINE_STRIP,
TRIANGLES,
TRIANGLE_STRIP,
TRIANGLE_FAN,
];
pub const VALID_MORPH_TARGETS: &[&str] = &["POSITION", "NORMAL", "TANGENT"];
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
pub enum Mode {
Points = 1,
Lines,
LineLoop,
LineStrip,
Triangles,
TriangleStrip,
TriangleFan,
}
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
pub struct Mesh {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extensions: Option<extensions::mesh::Mesh>,
#[serde(default)]
#[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
#[cfg_attr(not(feature = "extras"), serde(skip_serializing))]
pub extras: Extras,
#[cfg(feature = "names")]
#[cfg_attr(feature = "names", serde(skip_serializing_if = "Option::is_none"))]
pub name: Option<String>,
pub primitives: Vec<Primitive>,
#[serde(skip_serializing_if = "Option::is_none")]
pub weights: Option<Vec<f32>>,
}
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
#[gltf(validate_hook = "primitive_validate_hook")]
pub struct Primitive {
pub attributes: BTreeMap<Checked<Semantic>, Index<accessor::Accessor>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extensions: Option<extensions::mesh::Primitive>,
#[serde(default)]
#[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
#[cfg_attr(not(feature = "extras"), serde(skip_serializing))]
pub extras: Extras,
#[serde(skip_serializing_if = "Option::is_none")]
pub indices: Option<Index<accessor::Accessor>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub material: Option<Index<material::Material>>,
#[serde(default, skip_serializing_if = "is_primitive_mode_default")]
pub mode: Checked<Mode>,
#[serde(skip_serializing_if = "Option::is_none")]
pub targets: Option<Vec<MorphTarget>>,
}
fn is_primitive_mode_default(mode: &Checked<Mode>) -> bool {
*mode == Checked::Valid(Mode::Triangles)
}
fn primitive_validate_hook<P, R>(primitive: &Primitive, root: &crate::Root, path: P, report: &mut R)
where
P: Fn() -> crate::Path,
R: FnMut(&dyn Fn() -> crate::Path, crate::validation::Error),
{
let position_path = &|| path().field("attributes").key("POSITION");
if let Some(pos_accessor_index) = primitive
.attributes
.get(&Checked::Valid(Semantic::Positions))
{
let pos_accessor = &root.accessors[pos_accessor_index.value()];
let min_path = &|| position_path().field("min");
if let Some(ref min) = pos_accessor.min {
if from_value::<[f32; 3]>(min.clone()).is_err() {
report(min_path, Error::Invalid);
}
} else {
report(min_path, Error::Missing);
}
let max_path = &|| position_path().field("max");
if let Some(ref max) = pos_accessor.max {
if from_value::<[f32; 3]>(max.clone()).is_err() {
report(max_path, Error::Invalid);
}
} else {
report(max_path, Error::Missing);
}
} else {
report(position_path, Error::Missing);
}
}
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
pub struct MorphTarget {
#[serde(rename = "POSITION")]
#[serde(skip_serializing_if = "Option::is_none")]
pub positions: Option<Index<accessor::Accessor>>,
#[serde(rename = "NORMAL")]
#[serde(skip_serializing_if = "Option::is_none")]
pub normals: Option<Index<accessor::Accessor>>,
#[serde(rename = "TANGENT")]
#[serde(skip_serializing_if = "Option::is_none")]
pub tangents: Option<Index<accessor::Accessor>>,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub enum Semantic {
#[cfg(feature = "extras")]
Extras(String),
Positions,
Normals,
Tangents,
Colors(u32),
TexCoords(u32),
Joints(u32),
Weights(u32),
}
impl Default for Mode {
fn default() -> Mode {
Mode::Triangles
}
}
impl Mode {
pub fn as_gl_enum(self) -> u32 {
match self {
Mode::Points => POINTS,
Mode::Lines => LINES,
Mode::LineLoop => LINE_LOOP,
Mode::LineStrip => LINE_STRIP,
Mode::Triangles => TRIANGLES,
Mode::TriangleStrip => TRIANGLE_STRIP,
Mode::TriangleFan => TRIANGLE_FAN,
}
}
}
impl<'de> de::Deserialize<'de> for Checked<Mode> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = Checked<Mode>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "any of: {:?}", VALID_MODES)
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
use self::Mode::*;
use crate::validation::Checked::*;
Ok(match value as u32 {
POINTS => Valid(Points),
LINES => Valid(Lines),
LINE_LOOP => Valid(LineLoop),
LINE_STRIP => Valid(LineStrip),
TRIANGLES => Valid(Triangles),
TRIANGLE_STRIP => Valid(TriangleStrip),
TRIANGLE_FAN => Valid(TriangleFan),
_ => Invalid,
})
}
}
deserializer.deserialize_u64(Visitor)
}
}
impl ser::Serialize for Mode {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
serializer.serialize_u32(self.as_gl_enum())
}
}
impl Semantic {
fn checked(s: &str) -> Checked<Self> {
use self::Semantic::*;
use crate::validation::Checked::*;
match s {
"NORMAL" => Valid(Normals),
"POSITION" => Valid(Positions),
"TANGENT" => Valid(Tangents),
#[cfg(feature = "extras")]
_ if s.starts_with('_') => Valid(Extras(s[1..].to_string())),
_ if s.starts_with("COLOR_") => match s["COLOR_".len()..].parse() {
Ok(set) => Valid(Colors(set)),
Err(_) => Invalid,
},
_ if s.starts_with("TEXCOORD_") => match s["TEXCOORD_".len()..].parse() {
Ok(set) => Valid(TexCoords(set)),
Err(_) => Invalid,
},
_ if s.starts_with("JOINTS_") => match s["JOINTS_".len()..].parse() {
Ok(set) => Valid(Joints(set)),
Err(_) => Invalid,
},
_ if s.starts_with("WEIGHTS_") => match s["WEIGHTS_".len()..].parse() {
Ok(set) => Valid(Weights(set)),
Err(_) => Invalid,
},
_ => Invalid,
}
}
}
impl ser::Serialize for Semantic {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl ToString for Semantic {
fn to_string(&self) -> String {
use self::Semantic::*;
match *self {
Positions => "POSITION".into(),
Normals => "NORMAL".into(),
Tangents => "TANGENT".into(),
Colors(set) => format!("COLOR_{}", set),
TexCoords(set) => format!("TEXCOORD_{}", set),
Joints(set) => format!("JOINTS_{}", set),
Weights(set) => format!("WEIGHTS_{}", set),
#[cfg(feature = "extras")]
Extras(ref name) => format!("_{}", name),
}
}
}
impl ToString for Checked<Semantic> {
fn to_string(&self) -> String {
match *self {
Checked::Valid(ref semantic) => semantic.to_string(),
Checked::Invalid => "<invalid semantic name>".into(),
}
}
}
impl<'de> de::Deserialize<'de> for Checked<Semantic> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = Checked<Semantic>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "semantic name")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Semantic::checked(value))
}
}
deserializer.deserialize_str(Visitor)
}
}