diff options
Diffstat (limited to 'engine/src/file_format')
| -rw-r--r-- | engine/src/file_format/wavefront/mtl.rs | 211 | ||||
| -rw-r--r-- | engine/src/file_format/wavefront/obj.rs | 54 | 
2 files changed, 145 insertions, 120 deletions
diff --git a/engine/src/file_format/wavefront/mtl.rs b/engine/src/file_format/wavefront/mtl.rs index ef6e894..f3c7a64 100644 --- a/engine/src/file_format/wavefront/mtl.rs +++ b/engine/src/file_format/wavefront/mtl.rs @@ -2,7 +2,7 @@  //!  //! File format documentation: <https://paulbourke.net/dataformats/mtl> -use std::path::Path; +use std::path::{Path, PathBuf};  use crate::color::Color;  use crate::file_format::wavefront::common::{ @@ -11,8 +11,6 @@ use crate::file_format::wavefront::common::{      ParsingError,      Statement,  }; -use crate::material::{Builder as MaterialBuilder, Material}; -use crate::texture::{Error as TextureError, Texture};  /// Parses the content of a Wavefront `.mtl`.  /// @@ -44,25 +42,47 @@ pub fn parse(obj_content: &str) -> Result<Vec<NamedMaterial>, Error>          .filter(|(_, statement)| matches!(statement.keyword, Keyword::Newmtl))          .count(); -    #[cfg(feature = "debug")]      tracing::debug!("Material count: {material_cnt}");      statements_to_materials(statements, material_cnt)  }  #[derive(Debug, Clone)] +#[non_exhaustive]  pub struct NamedMaterial  {      pub name: String, -    pub material: Material, +    pub ambient: Color<f32>, +    pub diffuse: Color<f32>, +    pub specular: Color<f32>, +    pub ambient_map: Option<TextureMap>, +    pub diffuse_map: Option<TextureMap>, +    pub specular_map: Option<TextureMap>, +    pub shininess: f32, +} + +impl Default for NamedMaterial +{ +    fn default() -> Self +    { +        Self { +            name: String::new(), +            ambient: Color::WHITE_F32, +            diffuse: Color::WHITE_F32, +            specular: Color::WHITE_F32, +            ambient_map: None, +            diffuse_map: None, +            specular_map: None, +            shininess: 0.0, +        } +    }  }  #[derive(Debug, Clone)] -pub struct UnfinishedNamedMaterial +#[non_exhaustive] +pub struct TextureMap  { -    name: String, -    material_builder: MaterialBuilder, -    ready: bool, +    pub path: PathBuf,  }  #[derive(Debug, thiserror::Error)] @@ -71,8 +91,14 @@ pub enum Error      #[error(transparent)]      ParsingError(#[from] ParsingError), -    #[error("Failed to open texture")] -    TextureError(#[from] TextureError), +    #[error( +        "A material start statement (newmtl) is expected before statement at line {}", +        line_no +    )] +    ExpectedMaterialStartStmtBeforeStmt +    { +        line_no: usize +    },      #[error(          "Unsupported number of arguments ({arg_count}) to {keyword} at line {line_no}" @@ -93,7 +119,7 @@ pub enum Error      },  } -#[cfg_attr(feature = "debug", tracing::instrument(skip_all))] +#[tracing::instrument(skip_all)]  fn statements_to_materials(      statements: impl IntoIterator<Item = (usize, Statement<Keyword>)>,      material_cnt: usize, @@ -101,63 +127,52 @@ fn statements_to_materials(  {      let mut materials = Vec::<NamedMaterial>::with_capacity(material_cnt); -    let mut curr_material = UnfinishedNamedMaterial { -        name: String::new(), -        material_builder: MaterialBuilder::new(), -        ready: false, -    }; -      for (line_no, statement) in statements {          if statement.keyword == Keyword::Newmtl { -            if curr_material.ready { -                #[cfg(feature = "debug")] -                tracing::debug!("Building material"); - -                let material = curr_material.material_builder.clone().build(); - -                materials.push(NamedMaterial { name: curr_material.name, material }); -            } -              let name = statement.get_text_arg(0, line_no)?; -            curr_material.name = name.to_string(); -            curr_material.ready = true; +            materials.push(NamedMaterial { +                name: name.to_string(), +                ..Default::default() +            });              continue;          } -        if !curr_material.ready { -            // Discard statements not belonging to a material -            continue; +        let Some(curr_material) = materials.last_mut() else { +            return Err(Error::ExpectedMaterialStartStmtBeforeStmt { line_no });          };          match statement.keyword {              Keyword::Ka => {                  let color = get_color_from_statement(&statement, line_no)?; -                #[cfg(feature = "debug")] -                tracing::debug!("Adding ambient color"); +                tracing::debug!( +                    "Adding ambient color {color:?} to material {}", +                    curr_material.name +                ); -                curr_material.material_builder = -                    curr_material.material_builder.ambient(color); +                curr_material.ambient = color;              }              Keyword::Kd => {                  let color = get_color_from_statement(&statement, line_no)?; -                #[cfg(feature = "debug")] -                tracing::debug!("Adding diffuse color"); +                tracing::debug!( +                    "Adding diffuse color {color:?} to material {}", +                    curr_material.name +                ); -                curr_material.material_builder = -                    curr_material.material_builder.diffuse(color); +                curr_material.diffuse = color;              }              Keyword::Ks => {                  let color = get_color_from_statement(&statement, line_no)?; -                #[cfg(feature = "debug")] -                tracing::debug!("Adding specular color"); +                tracing::debug!( +                    "Adding specular color {color:?} to material {}", +                    curr_material.name +                ); -                curr_material.material_builder = -                    curr_material.material_builder.specular(color); +                curr_material.specular = color;              }              Keyword::MapKa => {                  if statement.arguments.len() > 1 { @@ -170,55 +185,75 @@ fn statements_to_materials(                  let texture_file_path = statement.get_text_arg(0, line_no)?; -                let texture = Texture::open(Path::new(texture_file_path))?; - -                #[cfg(feature = "debug")] -                tracing::debug!("Adding ambient map"); +                tracing::debug!( +                    "Adding ambient map {texture_file_path} to material {}", +                    curr_material.name +                ); -                let texture_id = texture.id(); - -                curr_material.material_builder = curr_material -                    .material_builder -                    .texture(texture) -                    .ambient_map(texture_id); +                curr_material.ambient_map = Some(TextureMap { +                    path: Path::new(texture_file_path).to_path_buf(), +                });              }              Keyword::MapKd => { -                let texture = get_map_from_texture(&statement, line_no)?; +                if statement.arguments.len() > 1 { +                    return Err(Error::UnsupportedArgumentCount { +                        keyword: statement.keyword.to_string(), +                        arg_count: statement.arguments.len(), +                        line_no, +                    }); +                } -                #[cfg(feature = "debug")] -                tracing::debug!("Adding diffuse map"); +                let texture_file_path = statement.get_text_arg(0, line_no)?; -                let texture_id = texture.id(); +                tracing::debug!( +                    "Adding diffuse map {texture_file_path} to material {}", +                    curr_material.name +                ); -                curr_material.material_builder = curr_material -                    .material_builder -                    .texture(texture) -                    .diffuse_map(texture_id); +                curr_material.diffuse_map = Some(TextureMap { +                    path: Path::new(texture_file_path).to_path_buf(), +                });              }              Keyword::MapKs => { -                let texture = get_map_from_texture(&statement, line_no)?; +                if statement.arguments.len() > 1 { +                    return Err(Error::UnsupportedArgumentCount { +                        keyword: statement.keyword.to_string(), +                        arg_count: statement.arguments.len(), +                        line_no, +                    }); +                } -                #[cfg(feature = "debug")] -                tracing::debug!("Adding specular map"); +                let texture_file_path = statement.get_text_arg(0, line_no)?; -                let texture_id = texture.id(); +                tracing::debug!( +                    "Adding specular map {texture_file_path} to material {}", +                    curr_material.name +                ); -                curr_material.material_builder = curr_material -                    .material_builder -                    .texture(texture) -                    .specular_map(texture_id); +                curr_material.specular_map = Some(TextureMap { +                    path: Path::new(texture_file_path).to_path_buf(), +                });              } -            Keyword::Newmtl => {} -        } -    } +            Keyword::Ns => { +                if statement.arguments.len() != 1 { +                    return Err(Error::UnsupportedArgumentCount { +                        keyword: statement.keyword.to_string(), +                        arg_count: statement.arguments.len(), +                        line_no, +                    }); +                } -    if curr_material.ready { -        #[cfg(feature = "debug")] -        tracing::debug!("Building last material"); +                let shininess = statement.get_float_arg(0, line_no)?; -        let material = curr_material.material_builder.build(); +                tracing::debug!( +                    "Adding shininess {shininess} to material {}", +                    curr_material.name +                ); -        materials.push(NamedMaterial { name: curr_material.name, material }); +                curr_material.shininess = shininess; +            } +            Keyword::Newmtl => {} +        }      }      Ok(materials) @@ -244,24 +279,6 @@ fn get_color_from_statement(      Ok(Color { red, green, blue })  } -fn get_map_from_texture( -    statement: &Statement<Keyword>, -    line_no: usize, -) -> Result<Texture, Error> -{ -    if statement.arguments.len() > 1 { -        return Err(Error::UnsupportedArgumentCount { -            keyword: statement.keyword.to_string(), -            arg_count: statement.arguments.len(), -            line_no, -        }); -    } - -    let texture_file_path = statement.get_text_arg(0, line_no)?; - -    Ok(Texture::open(Path::new(texture_file_path))?) -} -  keyword! {      #[derive(Debug, PartialEq, Eq, Clone, Copy)]      enum Keyword { @@ -280,5 +297,7 @@ keyword! {          #[keyword(rename = "map_Ks")]          MapKs, + +        Ns,      }  } diff --git a/engine/src/file_format/wavefront/obj.rs b/engine/src/file_format/wavefront/obj.rs index 88e6580..88d566c 100644 --- a/engine/src/file_format/wavefront/obj.rs +++ b/engine/src/file_format/wavefront/obj.rs @@ -2,6 +2,7 @@  //!  //! File format documentation: <https://paulbourke.net/dataformats/obj> +use std::collections::HashMap;  use std::fs::read_to_string;  use std::path::PathBuf; @@ -12,10 +13,9 @@ use crate::file_format::wavefront::common::{      Statement,      Triplet,  }; -use crate::mesh::Mesh; +use crate::mesh::{Mesh, Vertex};  use crate::util::try_option;  use crate::vector::{Vec2, Vec3}; -use crate::vertex::{Builder as VertexBuilder, Vertex};  /// Parses the content of a Wavefront `.obj`.  /// @@ -82,26 +82,32 @@ impl Obj      /// - A face index does not fit in a [`u32`]      pub fn to_mesh(&self) -> Result<Mesh, Error>      { -        let vertices = self -            .faces -            .iter() -            .flat_map(|face| face.vertices.clone()) -            .map(|face_vertex| face_vertex.to_vertex(self)) -            .collect::<Result<Vec<_>, Error>>()?; - -        Ok(Mesh::new( -            vertices, -            Some( -                self.faces -                    .iter() -                    .flat_map(|face| face.vertices.clone()) -                    .enumerate() -                    .map(|(index, _)| { -                        u32::try_from(index).map_err(|_| Error::FaceIndexTooBig(index)) -                    }) -                    .collect::<Result<Vec<_>, _>>()?, -            ), -        )) +        let mut vertices = Vec::<Vertex>::with_capacity(self.faces.len() * 3); +        let mut indices = Vec::<u32>::with_capacity(self.faces.len() * 3); + +        let mut added_face_vertices = +            HashMap::<FaceVertex, u32>::with_capacity(self.faces.len() * 3); + +        for face in &self.faces { +            for face_vertex in &face.vertices { +                if let Some(index) = added_face_vertices.get(&face_vertex) { +                    indices.push(*index); + +                    continue; +                } + +                vertices.push(face_vertex.to_vertex(self)?); + +                let vertex_index = u32::try_from(vertices.len() - 1) +                    .map_err(|_| Error::FaceIndexTooBig(vertices.len() - 1))?; + +                indices.push(vertex_index); + +                added_face_vertices.insert(face_vertex.clone(), vertex_index); +            } +        } + +        Ok(Mesh::new(vertices, Some(indices)))      }      /// Reads and parses the material libraries of this `Obj`. @@ -142,7 +148,7 @@ pub struct Face      pub material_name: Option<String>,  } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)]  pub struct FaceVertex  {      pub position: u32, @@ -161,7 +167,7 @@ impl FaceVertex      /// - The face's vertex normal cannot be found in the given [`Obj`]      pub fn to_vertex(&self, obj: &Obj) -> Result<Vertex, Error>      { -        let mut vertex_builder = VertexBuilder::default(); +        let mut vertex_builder = Vertex::builder();          let vertex_pos = *obj.vertex_positions.get(self.position as usize - 1).ok_or(              Error::FaceVertexPositionNotFound { vertex_pos_index: self.position },  | 
