use std::fs::read_to_string; use std::path::{Path, PathBuf}; use crate::asset::{ Assets, Handle as AssetHandle, Label as AssetLabel, Submitter as AssetSubmitter, }; use crate::mesh::Mesh; use crate::model::{MaterialDescription, Model}; #[derive(Debug, Clone)] #[non_exhaustive] pub struct Settings { /// Whether all UVs should be flipped on the Y axis. This is necessary if the UVs /// have their origin at the bottom left corner of the image /// /// The default is `true`. pub y_flip_uvs: bool, } impl Settings { /// Whether all UVs should be flipped on the Y axis. This is necessary if the UVs have /// their origin at the bottom left corner of the image /// /// The default is `true`. #[must_use] pub fn y_flip_uvs(mut self, y_flip_uvs: bool) -> Self { self.y_flip_uvs = y_flip_uvs; self } } impl Default for Settings { fn default() -> Self { Self { y_flip_uvs: true } } } pub fn add_importers(assets: &mut Assets) { assets.set_importer::(import); assets.set_importer::(import); } fn import( asset_submitter: &mut AssetSubmitter<'_>, path: &Path, settings: Option<&Settings>, ) -> Result<(), Error> { if path.extension().is_none_or(|ext| ext != "obj") { return Err(Error::UnsupportedFileType); } let settings = match settings { Some(settings) => settings, None => &Settings::default(), }; let parent_path = path .parent() .ok_or_else(|| Error::InvalidPath(path.to_path_buf()))?; let obj = crate::file_format::wavefront::obj::parse( &read_to_string(path) .map_err(|err| Error::ReadFailed(err, path.to_path_buf()))?, )?; if obj.mtl_libs.len() > 1 { return Err(Error::MoreThanOneMaterialLibrary); } let mesh = obj.to_mesh( crate::file_format::wavefront::obj::ToMeshOptions::default() .y_flip_uvs(settings.y_flip_uvs), )?; asset_submitter.submit_store_named("mesh", mesh); let materials = obj .unique_used_material_names .into_iter() .zip(std::iter::repeat(obj.mtl_libs.iter()).flatten()) .map(|(material_name, mtl_lib)| { (material_name.into_string(), parent_path.join(mtl_lib)) }); for (material_name, material_path) in materials { asset_submitter.submit_store_named( format!("material_{material_name}"), MaterialDescription::new(AssetHandle::invalid()).with_loading_material( AssetLabel { path: material_path.into(), name: Some(material_name.into()), }, ), ); } asset_submitter.submit_store(Model::default()); Ok(()) } #[derive(Debug, thiserror::Error)] enum Error { #[error("File type is unsupported")] UnsupportedFileType, #[error("Invalid path '{}'", .0.display())] InvalidPath(PathBuf), #[error("Failed to read file {}", .1.display())] ReadFailed(#[source] std::io::Error, PathBuf), #[error("More than one material library is specified. This is not supported")] MoreThanOneMaterialLibrary, #[error(transparent)] Other(#[from] crate::file_format::wavefront::obj::Error), }