//! Module containing the Wavefront OBJ parser. use parser::{Element, ParserError, LineParser}; use math::vector::{Vector2, Vector3}; use model::face::{FaceVertex, Face}; /// Parses a certain number of value in a line, and returns a Vec containing the values. /// /// Will return an error if the number of values expected is incorrect. pub fn parse_values(line_number: usize, line: &str, path: &str, number_of_values: usize) -> Result, ParserError> { let mut split = line.split_whitespace(); split.next(); let mut ret = vec![]; for elt in split { if let Ok(value) = elt.parse::() { ret.push(value); } else { return Err(ParserError::ParseNumberError(path.to_owned(), line_number, elt.to_owned())); } } if ret.len() == number_of_values { Ok(ret) } else { Err(ParserError::IncorrectNumberOfParameters(path.to_owned(), line_number, number_of_values, ret.len())) } } /// The wavefront OBJ format parser. pub struct ObjParser { } impl ObjParser { /// Creates a new parser. pub fn new() -> ObjParser { ObjParser { } } /// Parses an obj vertex line. /// /// ``` /// # use model_converter::math::vector::Vector3; /// # use model_converter::parser::obj::ObjParser; /// # use model_converter::parser::Element::Vertex; /// let obj = ObjParser::new(); /// let vertex = obj.parse_vertex(0, "v 1 2 3", "file.obj").unwrap(); /// assert_eq!(vertex, Vertex(Vector3::new(1.0, 2.0, 3.0))); /// ``` pub fn parse_vertex(&self, line_number: usize, line: &str, path: &str) -> Result { let values = parse_values(line_number, line, path, 3)?; Ok(Element::Vertex(Vector3::::new( values[0], values[1], values[2], ))) } /// Parses an obj texture coordinate line. /// /// ``` /// # use model_converter::math::vector::Vector2; /// # use model_converter::parser::obj::ObjParser; /// # use model_converter::parser::Element::TextureCoordinate; /// let obj = ObjParser::new(); /// let texture_coordinate = obj.parse_texture_coordinate(0, "vt 1 2", "file.obj").unwrap(); /// assert_eq!(texture_coordinate, TextureCoordinate(Vector2::new(1.0, 2.0))); /// ``` pub fn parse_texture_coordinate(&self, line_number: usize, line: &str, path: &str) -> Result { if let Ok(values) = parse_values(line_number,line, path, 2) { Ok(Element::TextureCoordinate(Vector2::::new( values[0], values[1], ))) } else { let values = parse_values(line_number, line, path, 3)?; warn!("Warning: expected 2 coordinates, but received 3, ignoring the last one..."); Ok(Element::TextureCoordinate(Vector2::::new( values[0], values[1], ))) } } /// Parses an obj normal line. /// /// ``` /// # use model_converter::math::vector::Vector3; /// # use model_converter::parser::obj::ObjParser; /// # use model_converter::parser::Element::Normal; /// let obj = ObjParser::new(); /// let normal = obj.parse_normal(0, "vn 1 2 3", "file.obj").unwrap(); /// assert_eq!(normal, Normal(Vector3::new(1.0, 2.0, 3.0))); /// ``` pub fn parse_normal(&self, line_number: usize, line: &str, path: &str) -> Result { let values = parse_values(line_number, line, path, 3)?; Ok(Element::Normal(Vector3::::new( values[0], values[1], values[2], ))) } /// Parses an obj face line. /// /// The index is changed to start from 0. /// ``` /// # use model_converter::math::vector::Vector3; /// # use model_converter::parser::obj::ObjParser; /// # use model_converter::parser::Element; /// # use model_converter::model::{Face, FaceVertex}; /// let obj = ObjParser::new(); /// let face1 = obj.parse_face(0, "f 1 2 3", "file.obj").unwrap(); /// let face2 = Element::Face(Face::new( /// FaceVertex::new(0, None, None), /// FaceVertex::new(1, None, None), /// FaceVertex::new(2, None, None), /// )); /// assert_eq!(face1, face2); /// /// let face1 = obj.parse_face(0, "f 1/1/1 2/2/2 3/3/3", "file.obj").unwrap(); /// let face2 = Element::Face(Face::new( /// FaceVertex::new(0, Some(0), Some(0)), /// FaceVertex::new(1, Some(1), Some(1)), /// FaceVertex::new(2, Some(2), Some(2)), /// )); /// assert_eq!(face1, face2); /// ``` pub fn parse_face(&self, line_number: usize, line: &str, path: &str) -> Result { let mut split = line.split_whitespace(); split.next(); let mut face_vertices = vec![]; for elt in split { let mut resplit = elt.split('/'); let vertex_string = resplit.nth(0).unwrap(); let vertex = vertex_string.parse::(); if vertex.is_err() { return Err(ParserError::ParseNumberError(path.to_owned(), line_number, vertex_string.to_owned())); } let vertex = vertex.ok().unwrap() - 1; let texture_coordinate_string = resplit.nth(0); let mut texture_coordinate = None; if let Some(texture_coordinate_string) = texture_coordinate_string { if texture_coordinate_string.len() != 0 { let texture_coordinate_index = texture_coordinate_string.parse::(); if texture_coordinate_index.is_err() { return Err(ParserError::ParseNumberError( path.to_owned(),line_number, texture_coordinate_string.to_owned() )); } else { texture_coordinate = Some(texture_coordinate_index.ok().unwrap() - 1); } } } let normal_string = resplit.nth(0); let mut normal = None; if let Some(normal_string) = normal_string { if normal_string.len() != 0 { let normal_index = normal_string.parse::(); if normal_index.is_err() { return Err(ParserError::ParseNumberError( path.to_owned(), line_number, normal_string.to_owned() )); } else { normal = Some(normal_index.ok().unwrap() - 1); } } } face_vertices.push(FaceVertex::new(vertex, texture_coordinate, normal)); } if face_vertices.len() == 3 { Ok(Element::Face(Face::new(face_vertices[0], face_vertices[1], face_vertices[2]))) } else if face_vertices.len() == 4 { Ok(Element::Faces(vec![ Face::new(face_vertices[0], face_vertices[1], face_vertices[2]), Face::new(face_vertices[0], face_vertices[2], face_vertices[3]), ])) } else { Err(ParserError::TooManyVertices(path.to_owned(), line_number, face_vertices.len())) } } } impl LineParser for ObjParser { fn parse_line(&mut self, line_number:usize, line: &str, path: &str) -> Result { let mut split = line.split_whitespace(); if let Some(first) = split.nth(0) { match first { "mtllib" => Ok(Element::MaterialLib(split.nth(0).unwrap().to_owned())), "v" => self.parse_vertex(line_number, line, path), "vt" => self.parse_texture_coordinate(line_number, line, path), "vn" => self.parse_normal(line_number, line, path), "usemtl" => Ok(Element::UseMaterial(split.nth(0).unwrap().to_owned())), "f" => self.parse_face(line_number, line, path), "g" | "s" | "o" => Ok(Element::None), first if first.starts_with('#') => Ok(Element::None), key if key.len() != 0 => Err(ParserError::UnexpectedKeyword(path.to_owned(), line_number, key.to_owned())), _ => Ok(Element::None), } } else { Ok(Element::None) } } }