modelz/lib.rs
1#![deny(clippy::all)]
2#![warn(clippy::pedantic)]
3#![warn(clippy::nursery)]
4#![warn(clippy::cargo)]
5#![expect(clippy::single_call_fn)]
6#![expect(clippy::exhaustive_enums)]
7#![expect(clippy::exhaustive_structs)]
8
9use std::path::{Path, PathBuf};
10
11#[cfg(feature = "gltf")]
12mod gltf;
13#[cfg(feature = "obj")]
14mod obj;
15#[cfg(feature = "stl")]
16mod ply;
17#[cfg(feature = "ply")]
18mod stl;
19
20pub struct Model3D {
21 /// All meshes the Model has.
22 ///
23 /// Some 3D Formats do not have multiple meshes and have just vertices, In this case there will be one Mesh with all the Vertices
24 pub meshes: Vec<Mesh>,
25 /// All Materials the Model has.
26 ///
27 /// Some 3D Formats do not support Materials/Textures, In this case the Vec will be empty
28 pub materials: Vec<Material>,
29
30 /// The format which was used to load the Model
31 pub format: ModelFormat,
32}
33
34impl Model3D {
35 /// Load an Full 3D Model from the Given File extension
36 ///
37 /// # Examples
38 ///
39 /// ```
40 /// use modelz::Model3D;
41 ///
42 /// let model = Model3D::load("model.gltf");
43 ///
44 /// for mesh in model.meshes {
45 /// println!("{}", mesh.name.unwrap());
46 /// for vert in mesh.vertices {
47 /// println!("{:?}", vert)
48 /// }
49 /// }
50 /// ```
51 ///
52 /// # Errors
53 ///
54 /// Returns an Error is loading the Model was unsuccessful
55 pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, ModelError> {
56 let format = get_format(&path)?;
57 Self::from_format(path, &format)
58 }
59
60 /// Load an Full 3D Model from the Given `ModelFormat`
61 ///
62 /// # Examples
63 ///
64 /// ```
65 /// use modelz::Model3D;
66 ///
67 /// let model = Model3D::from_format("model.gltf", ModelFormat::GLTF);
68 ///
69 /// let model = Model3D::from_format("model", ModelFormat::GLTF);
70 /// ```
71 /// # Errors
72 ///
73 /// Returns an Error is loading the Model was unsuccessful
74 pub fn from_format<P: AsRef<Path>>(path: P, format: &ModelFormat) -> Result<Self, ModelError> {
75 match format {
76 #[cfg(feature = "obj")]
77 ModelFormat::OBJ => obj::load(path.as_ref()),
78 #[cfg(feature = "gltf")]
79 ModelFormat::GLTF => gltf::load(path.as_ref()),
80 #[cfg(feature = "stl")]
81 ModelFormat::STL => stl::load(path.as_ref()),
82 #[cfg(feature = "ply")]
83 ModelFormat::PLY => ply::load(path.as_ref()),
84 }
85 }
86}
87
88#[non_exhaustive]
89/// `ModelFormat` represents the 3D Format being used to Load an File
90pub enum ModelFormat {
91 #[cfg(feature = "obj")]
92 // Wavefront obj, .obj
93 OBJ,
94 #[cfg(feature = "gltf")]
95 // gltf 2.0, .gltf | .glb
96 GLTF,
97 #[cfg(feature = "stl")]
98 // STL .stl
99 STL,
100 #[cfg(feature = "ply")]
101 // Polygon File Format .ply
102 PLY,
103}
104
105#[derive(Debug)]
106#[non_exhaustive]
107pub enum ModelError {
108 // Format is not supported for you may have to enable it as a crate feature
109 UnknowFormat,
110 // Given file does not exist
111 FileNotExists,
112 // Failed to open file
113 OpenFile(String),
114 // Error while loading general 3D File
115 ModelParsing(String),
116 // Error loading Material
117 MaterialLoad(String),
118}
119
120fn get_format<P: AsRef<Path>>(path: &P) -> Result<ModelFormat, ModelError> {
121 let path = path.as_ref();
122 if !path.exists() {
123 return Err(ModelError::FileNotExists);
124 }
125
126 let extension = path
127 .extension()
128 .and_then(|ext| return ext.to_str())
129 .expect("Failed to get File extension");
130 match extension {
131 #[cfg(feature = "obj")]
132 "obj" => Ok(ModelFormat::OBJ),
133 #[cfg(feature = "gltf")]
134 "gltf" | "glb" => Ok(ModelFormat::GLTF),
135 #[cfg(feature = "stl")]
136 "stl" => Ok(ModelFormat::STL),
137 _ => Err(ModelError::UnknowFormat),
138 }
139}
140
141pub struct Mesh {
142 /// All the Vertices the Mesh has.
143 pub vertices: Vec<Vertex>,
144 /// All the Indices the Mesh has.
145 pub indices: Option<Indices>,
146 /// The Render Mode that should be used to Render the Mesh
147 pub mode: RenderMode,
148 /// The index from the Vec Materials Vec in `Model3D`
149 ///
150 /// # Examples
151 ///
152 /// ```
153 /// let material = model.materials[mesh.material_index];
154 /// ```
155 pub material_index: Option<usize>,
156 /// Name of the Mesh.
157 ///
158 /// Some File Formats do not support Mesh names, In this case this will be `None`
159 pub name: Option<String>,
160}
161
162#[non_exhaustive]
163pub struct Material {
164 /// The optional diffuse Texture
165 pub diffuse_texture: Option<Texture>,
166 /// The alpha rendering mode of the material. The material's alpha rendering
167 /// mode enumeration specifying the interpretation of the alpha value of the main
168 /// factor and texture.
169 pub alpha_mode: AlphaMode,
170 /// The Alpha cutoff value of the material.
171 pub alpha_cutoff: Option<f32>,
172 /// Specifies whether the material is double-sided.
173 ///
174 /// When disabled, back-face culling is enabled
175 /// When enabled, back-face culling is disabled
176 pub double_sided: bool,
177 /// The Base color of the Material.
178 ///
179 /// Usally used to mutiple the diffuse texture
180 ///
181 /// Some File Formats do not support Material names, In this case this will be `None`
182 /// # Examples
183 ///
184 /// ```
185 /// vec4 texture = texture(texture_diffuse, tex_coord) * material.base_color;
186 /// ```
187 pub base_color: Option<[f32; 4]>,
188 /// Name of the Material.
189 ///
190 /// Some File Formats do not support Material names, In this case this will be `None`
191 pub name: Option<String>,
192}
193
194pub struct Texture {
195 /// The image from the `image` crate, Which is loaded into RAM
196 pub image: Image,
197 /// Sampler which beining used on the Image
198 pub sampler: Sampler,
199 /// Name of the Texture.
200 ///
201 /// Some File Formats do not support Texture names, In this case this will be `None`
202 pub name: Option<String>,
203}
204
205pub enum Image {
206 Memory {
207 data: Vec<u8>,
208 mime_type: Option<String>,
209 },
210 Path {
211 path: PathBuf,
212 mime_type: Option<String>,
213 },
214}
215
216#[derive(Default)]
217pub struct Sampler {
218 pub mag_filter: Option<MagFilter>,
219 pub min_filter: Option<MinFilter>,
220 pub wrap_s: WrappingMode,
221 pub wrap_t: WrappingMode,
222 pub name: Option<String>,
223}
224
225/// Mag Filter
226///
227/// # Rendering
228///
229/// Vulkan: Corresponds to `vk::Filter`
230/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkFilter.html>
231#[derive(Clone, Copy, Debug, Eq, PartialEq)]
232pub enum MagFilter {
233 /// Corresponds to `GL_NEAREST` or `vk::Filter::NEAREST`.
234 Nearest = 1,
235
236 /// Corresponds to `GL_LINEAR` or `vk::Filter::LINEAR`.
237 Linear,
238}
239
240/// Mag Filter
241///
242/// # Rendering
243///
244/// Vulkan: Corresponds to `vk::Filter` & `vk::SamplerMipmapMode`
245/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkFilter.html>
246/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSamplerMipmapMode.html>
247#[derive(Clone, Copy, Debug, Eq, PartialEq)]
248pub enum MinFilter {
249 /// Corresponds to `GL_NEAREST` or `vk::Filter::NEAREST`.
250 Nearest = 1,
251
252 /// Corresponds to `GL_LINEAR` or `vk::Filter::LINEAR`.
253 Linear,
254
255 /// Corresponds to `GL_NEAREST_MIPMAP_NEAREST` or (`vk::Filter::NEAREST`, `vk::SamplerMipmapMode::NEAREST`).
256 NearestMipmapNearest,
257
258 /// Corresponds to `GL_LINEAR_MIPMAP_NEAREST` or (`vk::Filter::LINEAR`, `vk::SamplerMipmapMode::NEAREST`).
259 LinearMipmapNearest,
260
261 /// Corresponds to `GL_NEAREST_MIPMAP_LINEAR` or (`vk::Filter::NEAREST`, `vk::SamplerMipmapMode::LINEAR`).
262 NearestMipmapLinear,
263
264 /// Corresponds to `GL_LINEAR_MIPMAP_LINEAR` or (`vk::Filter::LINEAR`, `vk::SamplerMipmapMode::LINEAR`).
265 LinearMipmapLinear,
266}
267
268/// Wrapping Mode
269///
270/// # Rendering
271///
272/// Vulkan: Corresponds to `vk::SamplerAddressMode`
273/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSamplerAddressMode.html>
274#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
275pub enum WrappingMode {
276 /// Corresponds to `GL_CLAMP_TO_EDGE` or `vk::SamplerAddressMode::CLAMP_TO_EDGE`.
277 ClampToEdge = 1,
278
279 /// Corresponds to [`GL_MIRRORED_REPEAT`] or [`vk::SamplerAddressMode::MIRRORED_REPEAT`].
280 MirroredRepeat,
281
282 /// Corresponds to `GL_REPEAT` or `vk::SamplerAddressMode::REPEAT`.
283 #[default]
284 Repeat,
285}
286
287#[derive(Clone, Copy, Debug, Eq, PartialEq)]
288pub enum AlphaMode {
289 /// The alpha value is ignored and the rendered output is fully opaque.
290 Opaque = 1,
291
292 /// The rendered output is either fully opaque or fully transparent depending on
293 /// the alpha value and the specified alpha cutoff value.
294 Mask,
295
296 /// The alpha value is used, to determine the transparency of the rendered output.
297 /// The alpha cutoff value is ignored.
298 Blend,
299}
300
301/// The type of primitives to render.
302///
303/// # Rendering
304///
305/// Vulkan: Corresponds to `vk::PrimitiveTopology`
306/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPrimitiveTopology.html>
307#[derive(Clone, Copy, Debug, Eq, PartialEq)]
308pub enum RenderMode {
309 /// Corresponds to `GL_POINTS` or `vk::PrimitiveTopology::POINT_LIST`.
310 Points = 1,
311
312 /// Corresponds to `GL_LINES` or `vk::PrimitiveTopology::LINE_LIST`.
313 Lines,
314
315 /// Corresponds to [`GL_LINE_LOOP`] or [`vk::PrimitiveTopology::LINE_LIST`].
316 LineLoop,
317
318 /// Corresponds to `GL_LINE_STRIP` or `vk::PrimitiveTopology::LINE_STRIP`.
319 LineStrip,
320
321 /// Corresponds to `GL_TRIANGLES` or `vk::PrimitiveTopology::TRIANGLE_LIST`.
322 Triangles,
323
324 /// Corresponds to `GL_TRIANGLE_STRIP`or `vk::PrimitiveTopology::TRIANGLE_STRIP`.
325 TriangleStrip,
326
327 /// Corresponds to `GL_TRIANGLE_FAN` or `vk::PrimitiveTopology::TRIANGLE_FAN`.
328 TriangleFan,
329}
330
331#[derive(Clone, Debug)]
332pub struct Vertex {
333 pub position: [f32; 3],
334 pub color: Option<[f32; 4]>, // rgba f32
335 pub tex_coord: Option<[f32; 2]>,
336 pub normal: Option<[f32; 3]>,
337}
338
339#[derive(Clone, Debug)]
340/// Indicies
341///
342/// # Rendering
343///
344/// Vulkan: Corresponds to `vk::IndexType`
345/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkIndexType.html>
346pub enum Indices {
347 U8(Vec<u8>),
348 U16(Vec<u16>),
349 U32(Vec<u32>),
350}