236 lines
8.2 KiB
Rust
236 lines
8.2 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)?;
|
|
logln!("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),
|
|
key if key.len() != 0 => Err(ParserError::UnexpectedKeyword(path.to_owned(), line_number, key.to_owned())),
|
|
_ => Ok(Element::None),
|
|
}
|
|
} else {
|
|
Ok(Element::None)
|
|
}
|
|
}
|
|
}
|
|
|