1use serde::{Deserialize, Serialize};
8
9use crate::client::Client;
10use crate::error::Result;
11use crate::jobs::{JobCreateRequest, JobStatusResponse};
12
13#[derive(Debug, Clone, Serialize, Default)]
18pub struct RemeshRequest {
19 #[serde(skip_serializing_if = "Option::is_none")]
21 pub input_task_id: Option<String>,
22
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub model_url: Option<String>,
26
27 #[serde(skip_serializing_if = "Option::is_none")]
30 pub target_formats: Option<Vec<String>>,
31
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub topology: Option<String>,
35
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub target_polycount: Option<i32>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub resize_height: Option<f64>,
43
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub origin_at: Option<String>,
47
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub convert_format_only: Option<bool>,
51}
52
53#[derive(Debug, Clone, Deserialize, Default)]
55pub struct ModelUrls {
56 #[serde(default)]
57 pub glb: String,
58 #[serde(default)]
59 pub fbx: String,
60 #[serde(default)]
61 pub obj: String,
62 #[serde(default)]
63 pub usdz: String,
64 #[serde(default)]
65 pub stl: String,
66 #[serde(default)]
67 pub blend: String,
68}
69
70#[derive(Debug, Clone, Serialize, Default)]
72pub struct RetextureRequest {
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub input_task_id: Option<String>,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub model_url: Option<String>,
80
81 pub prompt: String,
83
84 #[serde(skip_serializing_if = "Option::is_none")]
86 pub enable_pbr: Option<bool>,
87
88 #[serde(skip_serializing_if = "Option::is_none")]
90 pub ai_model: Option<String>,
91}
92
93#[derive(Debug, Clone, Serialize, Default)]
95pub struct RigRequest {
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub input_task_id: Option<String>,
99
100 #[serde(skip_serializing_if = "Option::is_none")]
102 pub model_url: Option<String>,
103
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub height_meters: Option<f64>,
107}
108
109#[derive(Debug, Clone, Serialize, Default)]
111pub struct AnimateRequest {
112 pub rig_task_id: String,
114
115 pub action_id: i32,
117
118 #[serde(skip_serializing_if = "Option::is_none")]
120 pub post_process: Option<AnimationPostProcess>,
121}
122
123#[derive(Debug, Clone, Serialize, Default)]
125pub struct AnimationPostProcess {
126 pub operation_type: String,
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub fps: Option<i32>,
131}
132
133pub type PostProcess = AnimationPostProcess;
135
136pub type Generate3DRequest = crate::image::ImageRequest;
139
140#[derive(Debug, Clone, Deserialize, Default)]
142pub struct BasicAnimations {
143 #[serde(default)]
145 pub walking_glb: String,
146
147 #[serde(default)]
149 pub walking_fbx: String,
150
151 #[serde(default)]
153 pub running_glb: String,
154
155 #[serde(default)]
157 pub running_fbx: String,
158
159 #[serde(default)]
161 pub idle_glb: String,
162
163 #[serde(default)]
165 pub idle_fbx: String,
166}
167
168impl Client {
171 pub async fn remesh(&self, req: &RemeshRequest) -> Result<JobStatusResponse> {
176 self.submit_and_poll("3d/remesh", req).await
177 }
178
179 pub async fn retexture(&self, req: &RetextureRequest) -> Result<JobStatusResponse> {
183 self.submit_and_poll("3d/retexture", req).await
184 }
185
186 pub async fn rig(&self, req: &RigRequest) -> Result<JobStatusResponse> {
190 self.submit_and_poll("3d/rig", req).await
191 }
192
193 pub async fn animate(&self, req: &AnimateRequest) -> Result<JobStatusResponse> {
197 self.submit_and_poll("3d/animate", req).await
198 }
199
200 async fn submit_and_poll(
202 &self,
203 job_type: &str,
204 params: &impl serde::Serialize,
205 ) -> Result<JobStatusResponse> {
206 let params = serde_json::to_value(params)?;
207
208 let create_resp = self
209 .create_job(&JobCreateRequest {
210 job_type: job_type.into(),
211 params,
212 })
213 .await?;
214
215 self.poll_job(
216 &create_resp.job_id,
217 std::time::Duration::from_secs(5),
218 120, )
220 .await
221 }
222}