#![deny(clippy::all)]
#![warn(clippy::pedantic)]
#![warn(clippy::nursery)]
#![warn(clippy::cargo)]
#![expect(clippy::single_call_fn)]
#![expect(clippy::exhaustive_enums)]
#![expect(clippy::exhaustive_structs)]
use std::path::{Path, PathBuf};
#[cfg(feature = "gltf")]
mod gltf;
#[cfg(feature = "obj")]
mod obj;
#[cfg(feature = "stl")]
mod ply;
#[cfg(feature = "ply")]
mod stl;
pub struct Model3D {
pub meshes: Vec<Mesh>,
pub materials: Vec<Material>,
pub format: ModelFormat,
}
impl Model3D {
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, ModelError> {
let format = get_format(&path)?;
Self::from_format(path, &format)
}
pub fn from_format<P: AsRef<Path>>(path: P, format: &ModelFormat) -> Result<Self, ModelError> {
match format {
#[cfg(feature = "obj")]
ModelFormat::OBJ => obj::load(path.as_ref()),
#[cfg(feature = "gltf")]
ModelFormat::GLTF => gltf::load(path.as_ref()),
#[cfg(feature = "stl")]
ModelFormat::STL => stl::load(path.as_ref()),
#[cfg(feature = "ply")]
ModelFormat::PLY => ply::load(path.as_ref()),
}
}
}
#[non_exhaustive]
pub enum ModelFormat {
#[cfg(feature = "obj")]
OBJ,
#[cfg(feature = "gltf")]
GLTF,
#[cfg(feature = "stl")]
STL,
#[cfg(feature = "ply")]
PLY,
}
#[derive(Debug)]
#[non_exhaustive]
pub enum ModelError {
UnknowFormat,
FileNotExists,
OpenFile(String),
ModelParsing(String),
MaterialLoad(String),
}
fn get_format<P: AsRef<Path>>(path: &P) -> Result<ModelFormat, ModelError> {
let path = path.as_ref();
if !path.exists() {
return Err(ModelError::FileNotExists);
}
let extension = path
.extension()
.and_then(|ext| return ext.to_str())
.expect("Failed to get File extension");
match extension {
#[cfg(feature = "obj")]
"obj" => Ok(ModelFormat::OBJ),
#[cfg(feature = "gltf")]
"gltf" | "glb" => Ok(ModelFormat::GLTF),
#[cfg(feature = "stl")]
"stl" => Ok(ModelFormat::STL),
_ => Err(ModelError::UnknowFormat),
}
}
pub struct Mesh {
pub vertices: Vec<Vertex>,
pub indices: Option<Indices>,
pub mode: RenderMode,
pub material_index: Option<usize>,
pub name: Option<String>,
}
#[non_exhaustive]
pub struct Material {
pub diffuse_texture: Option<Texture>,
pub alpha_mode: AlphaMode,
pub alpha_cutoff: Option<f32>,
pub double_sided: bool,
pub base_color: Option<[f32; 4]>,
pub name: Option<String>,
}
pub struct Texture {
pub image: Image,
pub sampler: Sampler,
pub name: Option<String>,
}
pub enum Image {
Memory {
data: Vec<u8>,
mime_type: Option<String>,
},
Path {
path: PathBuf,
mime_type: Option<String>,
},
}
#[derive(Default)]
pub struct Sampler {
pub mag_filter: Option<MagFilter>,
pub min_filter: Option<MinFilter>,
pub wrap_s: WrappingMode,
pub wrap_t: WrappingMode,
pub name: Option<String>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MagFilter {
Nearest = 1,
Linear,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MinFilter {
Nearest = 1,
Linear,
NearestMipmapNearest,
LinearMipmapNearest,
NearestMipmapLinear,
LinearMipmapLinear,
}
#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
pub enum WrappingMode {
ClampToEdge = 1,
MirroredRepeat,
#[default]
Repeat,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AlphaMode {
Opaque = 1,
Mask,
Blend,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RenderMode {
Points = 1,
Lines,
LineLoop,
LineStrip,
Triangles,
TriangleStrip,
TriangleFan,
}
#[derive(Clone, Debug)]
pub struct Vertex {
pub position: [f32; 3],
pub color: Option<[f32; 4]>, pub tex_coord: Option<[f32; 2]>,
pub normal: Option<[f32; 3]>,
}
#[derive(Clone, Debug)]
pub enum Indices {
U8(Vec<u8>),
U16(Vec<u16>),
U32(Vec<u32>),
}