summaryrefslogtreecommitdiff
path: root/engine/src/model.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/model.rs')
-rw-r--r--engine/src/model.rs176
1 files changed, 176 insertions, 0 deletions
diff --git a/engine/src/model.rs b/engine/src/model.rs
new file mode 100644
index 0000000..9f5840c
--- /dev/null
+++ b/engine/src/model.rs
@@ -0,0 +1,176 @@
+use std::borrow::Cow;
+use std::collections::HashMap;
+use std::fs::read_to_string;
+use std::path::Path;
+
+use ecs::Component;
+
+use crate::asset::{Assets, Handle as AssetHandle, Submitter as AssetSubmitter};
+use crate::material::Material;
+use crate::mesh::Mesh;
+use crate::texture::Texture;
+
+#[derive(Debug, Clone, Component)]
+#[non_exhaustive]
+pub struct Model
+{
+ pub asset_handle: AssetHandle<Data>,
+}
+
+impl Model
+{
+ pub fn new(asset_handle: AssetHandle<Data>) -> Self
+ {
+ Self { asset_handle }
+ }
+}
+
+#[derive(Debug, Default, Clone)]
+#[non_exhaustive]
+pub struct Data
+{
+ pub mesh: Mesh,
+ pub materials: HashMap<String, Material>,
+}
+
+impl Data
+{
+ pub fn builder() -> DataBuilder
+ {
+ DataBuilder::default()
+ }
+}
+
+#[derive(Debug, Default, Clone)]
+pub struct DataBuilder
+{
+ mesh: Mesh,
+ materials: HashMap<String, Material>,
+}
+
+impl DataBuilder
+{
+ pub fn mesh(mut self, mesh: Mesh) -> Self
+ {
+ self.mesh = mesh;
+
+ self
+ }
+
+ pub fn material<'name>(
+ mut self,
+ name: impl Into<Cow<'name, str>>,
+ material: Material,
+ ) -> Self
+ {
+ self.materials.insert(name.into().into_owned(), material);
+
+ self
+ }
+
+ pub fn build(self) -> Data
+ {
+ Data {
+ mesh: self.mesh,
+ materials: self.materials,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct Settings {}
+
+#[derive(Debug, thiserror::Error)]
+enum Error
+{
+ #[error("Failed to read model file")]
+ ReadModelFileFailed(#[source] std::io::Error),
+
+ #[error("Failed to read material file")]
+ ReadMaterialFileFailed(#[source] std::io::Error),
+
+ #[error("Failed to parse model file")]
+ ParsingFailed(#[from] ParsingError),
+}
+
+pub fn set_asset_importers(assets: &mut Assets)
+{
+ assets.set_importer(["obj"], import_wavefront_obj_asset);
+}
+
+#[derive(Debug, thiserror::Error)]
+enum ParsingError
+{
+ #[error(transparent)]
+ Obj(#[from] crate::file_format::wavefront::obj::Error),
+
+ #[error(transparent)]
+ Mtl(#[from] crate::file_format::wavefront::mtl::Error),
+}
+
+fn import_wavefront_obj_asset(
+ asset_submitter: &mut AssetSubmitter<'_>,
+ path: &Path,
+ _settings: Option<&'_ Settings>,
+) -> Result<(), Error>
+{
+ let obj = crate::file_format::wavefront::obj::parse(
+ &read_to_string(path).map_err(Error::ReadModelFileFailed)?,
+ )
+ .map_err(|err| Error::ParsingFailed(ParsingError::Obj(err)))?;
+
+ let mesh = obj
+ .to_mesh()
+ .map_err(|err| Error::ParsingFailed(ParsingError::Obj(err)))?;
+
+ let mut materials =
+ HashMap::<String, Material>::with_capacity(obj.mtl_libs.iter().flatten().count());
+
+ for mtl_lib_path in obj.mtl_libs.iter().flatten() {
+ materials.extend(import_mtl(asset_submitter, &mtl_lib_path)?);
+ }
+
+ asset_submitter.submit_store(Data { mesh, materials });
+
+ Ok(())
+}
+
+fn import_mtl<'a>(
+ asset_submitter: &'a AssetSubmitter<'_>,
+ path: &Path,
+) -> Result<impl Iterator<Item = (String, Material)> + 'a, Error>
+{
+ let named_materials = crate::file_format::wavefront::mtl::parse(
+ &read_to_string(path).map_err(Error::ReadMaterialFileFailed)?,
+ )
+ .map_err(|err| Error::ParsingFailed(ParsingError::Mtl(err)))?;
+
+ Ok(named_materials.into_iter().map(|named_material| {
+ let mut material_builder = Material::builder()
+ .ambient(named_material.ambient)
+ .diffuse(named_material.diffuse)
+ .specular(named_material.specular)
+ .shininess(named_material.shininess);
+
+ if let Some(ambient_map) = named_material.ambient_map {
+ material_builder = material_builder.ambient_map(Texture::new(
+ asset_submitter.submit_load_other(ambient_map.path.as_path()),
+ ));
+ }
+
+ if let Some(diffuse_map) = named_material.diffuse_map {
+ material_builder = material_builder.diffuse_map(Texture::new(
+ asset_submitter.submit_load_other(diffuse_map.path.as_path()),
+ ));
+ }
+
+ if let Some(specular_map) = named_material.specular_map {
+ material_builder = material_builder.specular_map(Texture::new(
+ asset_submitter.submit_load_other(specular_map.path.as_path()),
+ ));
+ }
+
+ (named_material.name, material_builder.build())
+ }))
+}