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}