model-converter/src/parser/obj.rs

237 lines
8.3 KiB
Rust

//! 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<Vec<f64>, ParserError> {
let mut split = line.split_whitespace();
split.next();
let mut ret = vec![];
for elt in split {
if let Ok(value) = elt.parse::<f64>() {
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<Element, ParserError> {
let values = parse_values(line_number, line, path, 3)?;
Ok(Element::Vertex(Vector3::<f64>::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<Element, ParserError> {
if let Ok(values) = parse_values(line_number,line, path, 2) {
Ok(Element::TextureCoordinate(Vector2::<f64>::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::<f64>::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<Element, ParserError> {
let values = parse_values(line_number, line, path, 3)?;
Ok(Element::Normal(Vector3::<f64>::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<Element, ParserError> {
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::<usize>();
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::<usize>();
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::<usize>();
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<Element, ParserError> {
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)
}
}
}