1mod loader;
38pub use loader::*;
39
40mod raw_assets;
41pub use raw_assets::*;
42
43#[cfg(not(target_arch = "wasm32"))]
44mod saver;
45#[cfg(not(target_arch = "wasm32"))]
46pub use saver::*;
47
48#[cfg(feature = "obj")]
49mod obj;
50
51#[cfg(feature = "stl")]
52mod stl;
53
54#[cfg(feature = "gltf")]
55mod gltf;
56
57#[cfg(feature = "image")]
58mod img;
59
60#[cfg(feature = "vol")]
61mod vol;
62
63#[cfg(feature = "pcd")]
64mod pcd;
65
66#[cfg(feature = "3mf")]
67#[path = "io/3mf.rs"]
68mod three_mf;
69
70pub fn deserialize<T: Deserialize>(key: &str, bytes: Vec<u8>) -> crate::Result<T> {
76 let mut assets = RawAssets::new();
77 assets.insert(key, bytes);
78 assets.deserialize(key)
79}
80
81#[cfg(not(target_arch = "wasm32"))]
85pub fn load_and_deserialize<T: Deserialize>(path: impl AsRef<std::path::Path>) -> crate::Result<T> {
86 load(&[&path])?.deserialize(path)
87}
88
89pub async fn load_and_deserialize_async<T: Deserialize>(
93 path: impl AsRef<std::path::Path>,
94) -> crate::Result<T> {
95 load_async(&[&path]).await?.deserialize(path)
96}
97
98#[cfg(not(target_arch = "wasm32"))]
102pub fn serialize_and_save<T: Serialize>(
103 path: impl AsRef<std::path::Path>,
104 data: T,
105) -> crate::Result<()> {
106 save(&data.serialize(path)?)
107}
108
109pub trait Deserialize: Sized {
113 fn deserialize(
117 path: impl AsRef<std::path::Path>,
118 raw_assets: &mut RawAssets,
119 ) -> crate::Result<Self>;
120}
121
122pub trait Serialize: Sized {
126 fn serialize(&self, path: impl AsRef<std::path::Path>) -> crate::Result<RawAssets>;
131}
132
133use crate::{Error, Geometry, Result};
134use std::collections::HashSet;
135use std::path::{Path, PathBuf};
136
137impl Deserialize for crate::Texture2D {
138 fn deserialize(path: impl AsRef<std::path::Path>, raw_assets: &mut RawAssets) -> Result<Self> {
139 let path = raw_assets.match_path(path.as_ref())?;
140 let extension = path
141 .extension()
142 .map(|e| e.to_str().unwrap())
143 .unwrap_or("image")
144 .to_string();
145 let data_url_bytes = if is_data_url(&path) {
146 Some(parse_data_url(path.to_str().unwrap())?)
147 } else {
148 None
149 };
150
151 #[allow(unused_variables)]
152 let bytes = if let Some(bytes) = data_url_bytes.as_ref() {
153 bytes
154 } else {
155 raw_assets.get(&path)?
156 };
157
158 if "svg" == extension {
159 #[cfg(not(feature = "svg"))]
161 return Err(Error::FeatureMissing("svg".to_string()));
162
163 #[cfg(feature = "svg")]
164 img::deserialize_svg(path, bytes)
165 } else {
166 #[cfg(not(feature = "image"))]
167 return Err(Error::FeatureMissing(extension));
168
169 #[cfg(feature = "image")]
170 img::deserialize_img(path, bytes)
171 }
172 }
173}
174
175impl Serialize for crate::Texture2D {
176 fn serialize(&self, path: impl AsRef<Path>) -> Result<RawAssets> {
177 let path = path.as_ref();
178
179 #[cfg(not(feature = "image"))]
180 return Err(Error::FeatureMissing(
181 path.extension()
182 .map(|e| e.to_str().unwrap())
183 .unwrap_or("image")
184 .to_string(),
185 ));
186
187 #[cfg(feature = "image")]
188 img::serialize_img(self, path)
189 }
190}
191
192impl Deserialize for crate::Scene {
193 fn deserialize(path: impl AsRef<Path>, raw_assets: &mut RawAssets) -> Result<Self> {
194 let path = raw_assets.match_path(path.as_ref())?;
195 match path.extension().map(|e| e.to_str().unwrap()).unwrap_or("") {
196 "gltf" | "glb" => {
197 #[cfg(not(feature = "gltf"))]
198 return Err(Error::FeatureMissing("gltf".to_string()));
199
200 #[cfg(feature = "gltf")]
201 gltf::deserialize_gltf(raw_assets, &path)
202 }
203 "obj" => {
204 #[cfg(not(feature = "obj"))]
205 return Err(Error::FeatureMissing("obj".to_string()));
206
207 #[cfg(feature = "obj")]
208 obj::deserialize_obj(raw_assets, &path)
209 }
210 "stl" => {
211 #[cfg(not(feature = "stl"))]
212 return Err(Error::FeatureMissing("stl".to_string()));
213
214 #[cfg(feature = "stl")]
215 stl::deserialize_stl(raw_assets, &path)
216 }
217 "pcd" => {
218 #[cfg(not(feature = "pcd"))]
219 return Err(Error::FeatureMissing("pcd".to_string()));
220
221 #[cfg(feature = "pcd")]
222 pcd::deserialize_pcd(raw_assets, &path)
223 }
224 "3mf" => {
225 #[cfg(not(feature = "3mf"))]
226 return Err(Error::FeatureMissing("3mf".to_string()));
227
228 #[cfg(feature = "3mf")]
229 three_mf::deserialize_3mf(raw_assets, &path)
230 }
231 _ => Err(Error::FailedDeserialize(path.to_str().unwrap().to_string())),
232 }
233 }
234}
235
236impl Deserialize for crate::Model {
237 fn deserialize(path: impl AsRef<Path>, raw_assets: &mut RawAssets) -> Result<Self> {
238 let scene = crate::Scene::deserialize(path, raw_assets)?;
239 Ok(scene.into())
240 }
241}
242
243impl Serialize for crate::Scene {
244 fn serialize(&self, path: impl AsRef<Path>) -> Result<RawAssets> {
245 let path = path.as_ref();
246 match path.extension().map(|e| e.to_str().unwrap()).unwrap_or("") {
247 "3mf" => {
248 #[cfg(not(feature = "3mf"))]
249 return Err(Error::FeatureMissing("3mf".to_string()));
250
251 #[cfg(feature = "3mf")]
252 {
253 let bytes = three_mf::serialize_3mf(self)?;
254 let mut raw_assets = RawAssets::new();
255 raw_assets.insert(path, bytes);
256 Ok(raw_assets)
257 }
258 }
259 _ => Err(Error::FailedSerialize(path.to_str().unwrap().to_string())),
260 }
261 }
262}
263
264impl Deserialize for crate::VoxelGrid {
265 fn deserialize(path: impl AsRef<Path>, raw_assets: &mut RawAssets) -> Result<Self> {
266 let path = raw_assets.match_path(path.as_ref())?;
267 match path.extension().map(|e| e.to_str().unwrap()).unwrap_or("") {
268 "vol" => {
269 #[cfg(not(feature = "vol"))]
270 return Err(Error::FeatureMissing("vol".to_string()));
271
272 #[cfg(feature = "vol")]
273 vol::deserialize_vol(raw_assets, &path)
274 }
275 _ => Err(Error::FailedDeserialize(path.to_str().unwrap().to_string())),
276 }
277 }
278}
279
280impl Deserialize for crate::Texture3D {
281 fn deserialize(path: impl AsRef<Path>, raw_assets: &mut RawAssets) -> Result<Self> {
282 let path = raw_assets.match_path(path.as_ref())?;
283 let voxel_grid = crate::VoxelGrid::deserialize(path, raw_assets)?;
284 Ok(voxel_grid.voxels)
285 }
286}
287
288impl Deserialize for crate::TriMesh {
289 fn deserialize(path: impl AsRef<Path>, raw_assets: &mut RawAssets) -> Result<Self> {
290 let path = path.as_ref();
291 let model = crate::Model::deserialize(path, raw_assets)?;
292 model
293 .geometries
294 .into_iter()
295 .find_map(|p| {
296 if let Geometry::Triangles(mesh) = p.geometry {
297 Some(mesh)
298 } else {
299 None
300 }
301 })
302 .ok_or_else(|| {
303 Error::FailedConvertion(
304 "a triangle mesh".to_owned(),
305 path.to_str().unwrap().to_owned(),
306 )
307 })
308 }
309}
310
311impl Deserialize for crate::PointCloud {
312 fn deserialize(path: impl AsRef<Path>, raw_assets: &mut RawAssets) -> Result<Self> {
313 let path = path.as_ref();
314 let model = crate::Model::deserialize(path, raw_assets)?;
315 model
316 .geometries
317 .into_iter()
318 .find_map(|p| {
319 if let Geometry::Points(point_cloud) = p.geometry {
320 Some(point_cloud)
321 } else {
322 None
323 }
324 })
325 .ok_or_else(|| {
326 Error::FailedConvertion(
327 "a point cloud".to_owned(),
328 path.to_str().unwrap().to_owned(),
329 )
330 })
331 }
332}
333
334fn get_dependencies(raw_assets: &RawAssets) -> Vec<PathBuf> {
335 #[allow(unused_mut)]
336 let mut dependencies = HashSet::new();
337 for (path, _) in raw_assets.iter() {
338 match path.extension().map(|e| e.to_str().unwrap()).unwrap_or("") {
339 "gltf" | "glb" => {
340 #[cfg(feature = "gltf")]
341 dependencies.extend(gltf::dependencies(raw_assets, path));
342 }
343 "obj" => {
344 #[cfg(feature = "obj")]
345 dependencies.extend(obj::dependencies_obj(raw_assets, path));
346 }
347 "mtl" => {
348 #[cfg(feature = "obj")]
349 dependencies.extend(obj::dependencies_mtl(raw_assets, path));
350 }
351 _ => {}
352 }
353 }
354 dependencies
355 .into_iter()
356 .filter(|d| !raw_assets.contains_key(d))
357 .collect()
358}
359
360fn is_data_url(path: &Path) -> bool {
361 path.to_str()
362 .map(|s| s.starts_with("data:"))
363 .unwrap_or(false)
364}
365
366#[allow(unused_variables)]
367fn parse_data_url(path: &str) -> Result<Vec<u8>> {
368 #[cfg(feature = "data-url")]
369 {
370 let url = data_url::DataUrl::process(path)
371 .map_err(|e| Error::FailedParsingDataUrl(path.to_string(), format!("{:?}", e)))?;
372 let (body, _) = url
373 .decode_to_vec()
374 .map_err(|e| Error::FailedParsingDataUrl(path.to_string(), format!("{:?}", e)))?;
375 Ok(body)
376 }
377 #[cfg(not(feature = "data-url"))]
378 Err(Error::FeatureMissing("data-url".to_string()))
379}