tripo_api/tasks/
retarget_animation.rs1use serde::{Deserialize, Deserializer, Serialize, Serializer};
8
9use crate::enums::{Animation, RigOutputFormat};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
14#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
15pub enum AnimationInput {
16 Single(Animation),
18 Many(Vec<Animation>),
20}
21
22#[derive(Debug, Clone)]
24#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
25pub struct RetargetAnimationRequest {
26 pub original_model_task_id: String,
28 pub animation: AnimationInput,
30 pub out_format: Option<RigOutputFormat>,
32 pub bake_animation: Option<bool>,
34 pub export_with_geometry: Option<bool>,
36 pub animate_in_place: Option<bool>,
38}
39
40impl Serialize for RetargetAnimationRequest {
41 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
42 use serde::ser::SerializeMap;
43 let mut m = s.serialize_map(None)?;
44 m.serialize_entry("original_model_task_id", &self.original_model_task_id)?;
45 match &self.animation {
46 AnimationInput::Single(a) => m.serialize_entry("animation", a)?,
47 AnimationInput::Many(list) => m.serialize_entry("animations", list)?,
48 }
49 if let Some(v) = &self.out_format {
50 m.serialize_entry("out_format", v)?;
51 }
52 if let Some(v) = &self.bake_animation {
53 m.serialize_entry("bake_animation", v)?;
54 }
55 if let Some(v) = &self.export_with_geometry {
56 m.serialize_entry("export_with_geometry", v)?;
57 }
58 if let Some(v) = &self.animate_in_place {
59 m.serialize_entry("animate_in_place", v)?;
60 }
61 m.end()
62 }
63}
64
65impl<'de> Deserialize<'de> for RetargetAnimationRequest {
66 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
67 use serde::de::Error as _;
68
69 #[derive(Deserialize)]
70 #[serde(deny_unknown_fields)]
71 struct Wire {
72 original_model_task_id: String,
73 #[serde(default)]
74 animation: Option<Animation>,
75 #[serde(default)]
76 animations: Option<Vec<Animation>>,
77 #[serde(default)]
78 out_format: Option<RigOutputFormat>,
79 #[serde(default)]
80 bake_animation: Option<bool>,
81 #[serde(default)]
82 export_with_geometry: Option<bool>,
83 #[serde(default)]
84 animate_in_place: Option<bool>,
85 }
86
87 let w = Wire::deserialize(d)?;
88 let animation = match (w.animation, w.animations) {
89 (Some(a), None) => AnimationInput::Single(a),
90 (None, Some(list)) => AnimationInput::Many(list),
91 (Some(_), Some(_)) => {
92 return Err(D::Error::custom(
93 "set either `animation` or `animations`, not both",
94 ));
95 }
96 (None, None) => {
97 return Err(D::Error::custom(
98 "missing required field `animation` or `animations`",
99 ));
100 }
101 };
102 Ok(Self {
103 original_model_task_id: w.original_model_task_id,
104 animation,
105 out_format: w.out_format,
106 bake_animation: w.bake_animation,
107 export_with_geometry: w.export_with_geometry,
108 animate_in_place: w.animate_in_place,
109 })
110 }
111}
112
113impl RetargetAnimationRequest {
114 #[must_use]
116 pub fn single(original_model_task_id: impl Into<String>, animation: Animation) -> Self {
117 Self {
118 original_model_task_id: original_model_task_id.into(),
119 animation: AnimationInput::Single(animation),
120 out_format: None,
121 bake_animation: None,
122 export_with_geometry: None,
123 animate_in_place: None,
124 }
125 }
126 #[must_use]
128 pub fn many(original_model_task_id: impl Into<String>, animations: Vec<Animation>) -> Self {
129 Self {
130 original_model_task_id: original_model_task_id.into(),
131 animation: AnimationInput::Many(animations),
132 out_format: None,
133 bake_animation: None,
134 export_with_geometry: None,
135 animate_in_place: None,
136 }
137 }
138}