use std::fs::read_to_string; use std::path::{Path, PathBuf}; use ecs::util::Either; use crate::asset::{Assets, Label as AssetLabel, Submitter as AssetSubmitter}; use crate::material::Material; use crate::model::{MaterialDescription, Spec}; #[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`. 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(["obj"], import_wavefront_obj_asset); } fn import_wavefront_obj_asset( asset_submitter: &mut AssetSubmitter<'_>, path: &Path, settings: Option<&Settings>, ) -> Result<(), Error> { 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()))?, )?; let mesh = obj.to_mesh( crate::file_format::wavefront::obj::ToMeshOptions::default() .y_flip_uvs(settings.y_flip_uvs), )?; let mesh_asset = asset_submitter.submit_store_named("mesh", mesh); if obj.mtl_libs.len() > 1 { return Err(Error::MoreThanOneMaterialLibrary); } asset_submitter.submit_store( Spec::builder() .mesh(mesh_asset) .materials(if obj.mtl_libs.is_empty() { Either::A([].into_iter()) } else { Either::B( obj.unique_used_material_names .iter() .zip(std::iter::repeat(obj.mtl_libs.iter()).flatten()) .map(|(material_name, mtl_lib)| { MaterialDescription::new( asset_submitter.submit_load_other::( AssetLabel { path: parent_path.join(mtl_lib).into(), name: Some(material_name.as_ref().into()), }, ), ) }), ) }) .build(), ); Ok(()) } #[derive(Debug, thiserror::Error)] enum Error { #[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), }