gltf_v1_json/
animation.rs

1use std::fmt;
2
3use crate::{Node, texture::Sampler};
4
5use super::{accessor::Accessor, common::StringIndex, validation::Checked};
6use gltf_v1_derive::Validate;
7use indexmap::IndexMap;
8use serde::{Serialize, de, ser};
9
10#[derive(Clone, Debug, PartialEq, Eq, Copy)]
11pub enum SamplerInterpolation {
12    Linear,
13}
14
15impl SamplerInterpolation {
16    pub const VALID_INTERPOLATION_TYPES: &[&str] = &["LINEAR"];
17}
18
19impl TryFrom<&str> for SamplerInterpolation {
20    type Error = ();
21
22    fn try_from(value: &str) -> Result<Self, Self::Error> {
23        match value {
24            "LINEAR" => Ok(SamplerInterpolation::Linear),
25            _ => Err(()),
26        }
27    }
28}
29
30impl From<SamplerInterpolation> for &str {
31    fn from(value: SamplerInterpolation) -> Self {
32        match value {
33            SamplerInterpolation::Linear => "LINEAR",
34        }
35    }
36}
37
38impl ser::Serialize for SamplerInterpolation {
39    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
40    where
41        S: ser::Serializer,
42    {
43        serializer.serialize_str(Into::into(*self))
44    }
45}
46impl<'de> de::Deserialize<'de> for Checked<SamplerInterpolation> {
47    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
48    where
49        D: de::Deserializer<'de>,
50    {
51        struct Visitor;
52        impl de::Visitor<'_> for Visitor {
53            type Value = Checked<SamplerInterpolation>;
54
55            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
56                write!(
57                    f,
58                    "any of: {:?}",
59                    SamplerInterpolation::VALID_INTERPOLATION_TYPES
60                )
61            }
62
63            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
64            where
65                E: de::Error,
66            {
67                Ok(TryInto::try_into(value)
68                    .map(Checked::Valid)
69                    .unwrap_or(Checked::Invalid))
70            }
71        }
72        deserializer.deserialize_str(Visitor)
73    }
74}
75
76#[derive(Clone, Debug, PartialEq, Eq, Copy)]
77pub enum AnimationPath {
78    Translation,
79    Rotation,
80    Scale,
81}
82
83impl AnimationPath {
84    pub const VALID_INTERPOLATION_TYPES: &[&str] = &["translation", "rotation", "scale"];
85}
86
87impl TryFrom<&str> for AnimationPath {
88    type Error = ();
89
90    fn try_from(value: &str) -> Result<Self, Self::Error> {
91        match value {
92            "translation" => Ok(AnimationPath::Translation),
93            "rotation" => Ok(AnimationPath::Rotation),
94            "scale" => Ok(AnimationPath::Scale),
95            _ => Err(()),
96        }
97    }
98}
99
100impl From<AnimationPath> for &str {
101    fn from(value: AnimationPath) -> Self {
102        match value {
103            AnimationPath::Translation => "translation",
104            AnimationPath::Rotation => "rotation",
105            AnimationPath::Scale => "scale",
106        }
107    }
108}
109
110impl Serialize for AnimationPath {
111    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
112    where
113        S: ser::Serializer,
114    {
115        serializer.serialize_str((*self).into())
116    }
117}
118
119impl<'de> de::Deserialize<'de> for Checked<AnimationPath> {
120    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
121    where
122        D: de::Deserializer<'de>,
123    {
124        struct Visitor;
125        impl de::Visitor<'_> for Visitor {
126            type Value = Checked<AnimationPath>;
127
128            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
129                write!(f, "any of: {:?}", AnimationPath::VALID_INTERPOLATION_TYPES)
130            }
131
132            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
133            where
134                E: de::Error,
135            {
136                Ok(value
137                    .try_into()
138                    .map(Checked::Valid)
139                    .unwrap_or(Checked::Invalid))
140            }
141        }
142        deserializer.deserialize_str(Visitor)
143    }
144}
145
146#[derive(Clone, Debug, serde_derive::Deserialize, serde_derive::Serialize, Validate)]
147pub struct AnimationChannelTarget {
148    pub id: StringIndex<Node>,
149    pub path: Checked<AnimationPath>,
150}
151
152#[derive(Clone, Debug, serde_derive::Deserialize, serde_derive::Serialize, Validate)]
153pub struct AnimationChannel {
154    pub sampler: StringIndex<Sampler>,
155    pub target: AnimationChannelTarget,
156}
157
158#[derive(Clone, Debug, serde_derive::Deserialize, serde_derive::Serialize, Validate)]
159pub struct AnimationSampler {
160    pub input: String,
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub interpolation: Option<Checked<SamplerInterpolation>>,
163    pub output: String,
164}
165
166#[derive(Clone, Debug, serde_derive::Deserialize, serde_derive::Serialize, Validate)]
167pub struct Animation {
168    #[serde(default)]
169    #[serde(skip_serializing_if = "Vec::is_empty")]
170    pub channels: Vec<AnimationChannel>,
171    #[serde(skip_serializing_if = "IndexMap::is_empty")]
172    pub parameters: IndexMap<String, StringIndex<Accessor>>,
173    #[serde(skip_serializing_if = "IndexMap::is_empty")]
174    pub samplers: IndexMap<String, AnimationSampler>,
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub name: Option<String>,
177}
178
179#[test]
180fn test_animation_deserialize() {
181    let data = r#"{
182            "channels": [
183                {
184                    "sampler": "a_sampler",
185                    "target": {
186                        "id": "node_id",
187                        "path": "rotation",
188                        "extensions" : {
189                            "extension_name" : {
190                                "extension specific" : "value"
191                            }
192                        },
193                        "extras" : {
194                            "Application specific" : "The extra object can contain any properties."
195                        }     
196                    },
197                     "extensions" : {
198                         "extension_name" : {
199                             "extension specific" : "value"
200                         }
201                     },
202                    "extras" : {
203                        "Application specific" : "The extra object can contain any properties."
204                    }     
205                }
206            ],
207            "name": "user-defined animation name",
208            "parameters": {
209                "TIME": "time_accessor",
210                "rotation": "rotation_accessor"
211            },
212            "samplers": {
213                "a_sampler": {
214                    "input": "TIME",
215                    "interpolation": "LINEAR",
216                    "output": "rotation",
217		            "extensions" : {
218		               "extension_name" : {
219		                  "extension specific" : "value"
220		               }
221		            },
222                    "extras" : {
223                        "Application specific" : "The extra object can contain any properties."
224                    }     
225                }
226            },
227            "extensions" : {
228               "extension_name" : {
229                  "extension specific" : "value"
230               }
231            },
232            "extras" : {
233                "Application specific" : "The extra object can contain any properties."
234            }     
235        }"#;
236    let animation: Result<Animation, _> = serde_json::from_str(data);
237    let animation_unwrap = animation.unwrap();
238    println!("{}", serde_json::to_string(&animation_unwrap).unwrap());
239    assert_eq!(
240        Some("user-defined animation name".to_string()),
241        animation_unwrap.name
242    );
243}