use serde::de;
use serde_json::from_value;
use std::collections::HashMap;
use std::fmt;
use validation::{Checked, Error, Validate};
use {accessor, extensions, material, Extras, Index};
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: &'static [u32] = &[
POINTS,
LINES,
LINE_LOOP,
LINE_STRIP,
TRIANGLES,
TRIANGLE_STRIP,
TRIANGLE_FAN,
];
pub const VALID_MORPH_TARGETS: &'static [&'static str] = &[
"POSITION",
"NORMAL",
"TANGENT",
];
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
pub enum Mode {
Points = 1,
Lines,
LineLoop,
LineStrip,
Triangles,
TriangleStrip,
TriangleFan,
}
#[derive(Clone, Debug, Deserialize, Validate)]
pub struct Mesh {
#[serde(default)]
pub extensions: extensions::mesh::Mesh,
#[serde(default)]
pub extras: Extras,
#[cfg(feature = "names")]
pub name: Option<String>,
pub primitives: Vec<Primitive>,
pub weights: Option<Vec<f32>>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Primitive {
pub attributes: HashMap<Checked<Semantic>, Index<accessor::Accessor>>,
#[serde(default)]
pub extensions: extensions::mesh::Primitive,
#[serde(default)]
pub extras: Extras,
pub indices: Option<Index<accessor::Accessor>>,
pub material: Option<Index<material::Material>>,
#[serde(default)]
pub mode: Checked<Mode>,
pub targets: Option<Vec<MorphTarget>>,
}
impl Validate for Primitive {
fn validate_minimally<P, R>(&self, root: &::Root, path: P, report: &mut R)
where
P: Fn() -> ::Path,
R: FnMut(&Fn() -> ::Path, ::validation::Error),
{
self.attributes
.validate_minimally(root, || path().field("attributes"), report);
self.extensions
.validate_minimally(root, || path().field("extensions"), report);
self.extras
.validate_minimally(root, || path().field("extras"), report);
self.indices
.validate_minimally(root, || path().field("indices"), report);
self.material
.validate_minimally(root, || path().field("material"), report);
self.mode
.validate_minimally(root, || path().field("mode"), report);
self.targets
.validate_minimally(root, || path().field("targets"), report);
if let Some(pos_accessor_index) = self.attributes.get(&Checked::Valid(Semantic::Positions)) {
let pos_accessor = &root.accessors[pos_accessor_index.value()];
let min_path = &|| path().field("attributes").key("POSITION").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 = &|| path().field("attributes").key("POSITION").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);
}
}
}
}
#[derive(Clone, Debug, Deserialize, Validate)]
pub struct MorphTarget {
#[serde(rename = "POSITION")]
pub positions: Option<Index<accessor::Accessor>>,
#[serde(rename = "NORMAL")]
pub normals: Option<Index<accessor::Accessor>>,
#[serde(rename = "TANGENT")]
pub tangents: Option<Index<accessor::Accessor>>,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
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<'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 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 Semantic {
fn checked(s: &str) -> Checked<Self> {
use self::Semantic::*;
use 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 ToString for Semantic {
fn to_string(&self) -> String {
use self::Semantic::*;
match self {
&Positions => format!("POSITION"),
&Normals => format!("NORMAL"),
&Tangents => format!("TANGENT"),
&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 => format!("<invalid semantic name>"),
}
}
}
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)
}
}