1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![warn(missing_docs)]
4
5pub mod prelude;
15
16mod camera;
17pub use camera::*;
18
19pub mod texture;
20pub use texture::*;
21
22pub mod material;
23pub use material::*;
24
25pub mod geometry;
26pub use geometry::*;
27
28pub mod volume;
29pub use volume::*;
30
31mod animation;
32pub use animation::*;
33
34#[derive(Debug, Clone)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
45pub struct Scene {
46    pub name: String,
48    pub children: Vec<Node>,
50    pub materials: Vec<PbrMaterial>,
52}
53
54impl Default for Scene {
55    fn default() -> Self {
56        Self {
57            name: "scene".to_owned(),
58            children: Vec::new(),
59            materials: Vec::new(),
60        }
61    }
62}
63
64#[derive(Debug, Clone)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71pub struct Node {
72    pub name: String,
74    pub children: Vec<Node>,
76    pub transformation: Mat4,
78    pub animations: Vec<(Option<String>, KeyFrames)>,
81    pub geometry: Option<Geometry>,
83    pub material_index: Option<usize>,
85}
86
87impl Default for Node {
88    fn default() -> Self {
89        Self {
90            name: "node".to_owned(),
91            children: Vec::new(),
92            transformation: Mat4::identity(),
93            animations: Vec::new(),
94            geometry: None,
95            material_index: None,
96        }
97    }
98}
99
100#[derive(Debug, Clone)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106pub struct Model {
107    pub name: String,
109    pub geometries: Vec<Primitive>,
111    pub materials: Vec<PbrMaterial>,
113}
114
115#[derive(Debug, Clone)]
120#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
121pub struct Primitive {
122    pub name: String,
124    pub transformation: Mat4,
126    pub animations: Vec<KeyFrameAnimation>,
129    pub geometry: Geometry,
131    pub material_index: Option<usize>,
133}
134
135impl std::ops::Deref for Primitive {
136    type Target = Geometry;
137    fn deref(&self) -> &Self::Target {
138        &self.geometry
139    }
140}
141
142impl std::ops::DerefMut for Primitive {
143    fn deref_mut(&mut self) -> &mut Self::Target {
144        &mut self.geometry
145    }
146}
147
148impl std::convert::From<Scene> for Model {
149    fn from(scene: Scene) -> Self {
150        let mut geometries = Vec::new();
151        for child in scene.children {
152            visit(child, Vec::new(), Mat4::identity(), &mut geometries);
153        }
154        Self {
155            name: scene.name,
156            materials: scene.materials,
157            geometries,
158        }
159    }
160}
161
162fn visit(
163    node: Node,
164    mut animations: Vec<KeyFrameAnimation>,
165    transformation: Mat4,
166    geometries: &mut Vec<Primitive>,
167) {
168    let mut transformation = transformation * node.transformation;
169    if !node.animations.is_empty() {
170        for (animation_name, key_frames) in node.animations {
171            if let Some(i) = animations.iter().position(|a| a.name == animation_name) {
172                animations[i]
173                    .key_frames
174                    .push((transformation, std::sync::Arc::new(key_frames)));
175            } else {
176                animations.push(KeyFrameAnimation {
177                    name: animation_name,
178                    key_frames: vec![(transformation, std::sync::Arc::new(key_frames))],
179                });
180            }
181        }
182        transformation = Mat4::identity();
183    };
184    if let Some(geometry) = node.geometry {
185        geometries.push(Primitive {
186            name: node.name.clone(),
187            transformation,
188            animations: animations.clone(),
189            geometry,
190            material_index: node.material_index,
191        });
192    }
193    for child in node.children {
194        visit(child, animations.clone(), transformation, geometries);
195    }
196}
197
198pub mod io;
199
200pub type Result<T> = std::result::Result<T, Error>;
202
203use thiserror::Error;
204#[derive(Error, Debug)]
208#[allow(missing_docs)]
209pub enum Error {
210    #[error("{0} buffer length must be {1}, actual length is {2}")]
211    InvalidBufferLength(String, usize, usize),
212    #[error("the number of indices must be divisable by 3, actual count is {0}")]
213    InvalidNumberOfIndices(usize),
214    #[error("the max index {0} must be less than the number of vertices {1}")]
215    InvalidIndices(usize, usize),
216    #[error("the transformation matrix cannot be inverted and is therefore invalid")]
217    FailedInvertingTransformationMatrix,
218    #[cfg(feature = "image")]
219    #[error("error while parsing an image file")]
220    Image(#[from] image::ImageError),
221
222    #[cfg(feature = "svg")]
223    #[error("error while parsing svg file")]
224    Svg(#[from] resvg::usvg::Error),
225
226    #[cfg(feature = "obj")]
227    #[error("error while parsing an .obj file")]
228    Obj(#[from] wavefront_obj::ParseError),
229
230    #[cfg(feature = "pcd")]
231    #[error("error while parsing an .pcd file")]
232    Pcd(#[from] pcd_rs::Error),
233
234    #[cfg(any(not(target_arch = "wasm32"), feature = "stl"))]
235    #[error("io error")]
236    IO(#[from] std::io::Error),
237    #[cfg(feature = "gltf")]
238    #[error("error while parsing a .gltf file")]
239    Gltf(#[from] ::gltf::Error),
240    #[cfg(feature = "gltf")]
241    #[error("the .gltf file contain corrupt buffer data")]
242    GltfCorruptData,
243    #[cfg(feature = "gltf")]
244    #[error("the .gltf file contain missing buffer data")]
245    GltfMissingData,
246    #[error("the .vol file contain wrong data size")]
247    VolCorruptData,
248    #[cfg(not(target_arch = "wasm32"))]
249    #[error("error while loading the file {0}: {1}")]
250    FailedLoading(String, std::io::Error),
251    #[cfg(feature = "reqwest")]
252    #[error("error while loading the url {0}: {1}")]
253    FailedLoadingUrlWithReqwest(String, reqwest::Error),
254    #[cfg(feature = "reqwest")]
255    #[error("error while loading the url {0}: {1}")]
256    FailedLoadingUrl(String, String),
257    #[cfg(feature = "reqwest")]
258    #[error("error while parsing the url {0}")]
259    FailedParsingUrl(String),
260    #[cfg(feature = "data-url")]
261    #[error("error while parsing data-url {0}: {1}")]
262    FailedParsingDataUrl(String, String),
263    #[error("tried to use {0} which was not loaded or otherwise added to the raw assets")]
264    NotLoaded(String),
265    #[error("the feature {0} is needed")]
266    FeatureMissing(String),
267    #[error("failed to deserialize the file {0}")]
268    FailedDeserialize(String),
269    #[error("failed to serialize the file {0}")]
270    FailedSerialize(String),
271    #[error("failed to find {0} in the file {1}")]
272    FailedConvertion(String, String),
273}