diff --git a/src/model.rs b/src/model.rs index a260aaf..09d4c75 100644 --- a/src/model.rs +++ b/src/model.rs @@ -171,10 +171,23 @@ impl Part { let mut shape = vec![]; for face in &self.faces { for &&v in &[&face.a, &face.b, &face.c] { + let vertex = vertices[v.vertex].into(); + let tex_coord = if let Some(tex_index) = v.texture_coordinate { + tex_coords[tex_index].into() + } else { + [0.0, 0.0] + }; + + let normal = if let Some(normal_index) = v.normal { + normals[normal_index].into() + } else { + [0.0, 0.0, 0.0] + }; + shape.push(Vertex { - vertex: vertices[v.vertex].into(), - tex_coords: tex_coords[v.texture_coordinate.unwrap()].into(), - normal: normals[v.normal.unwrap()].into(), + vertex: vertex, + tex_coords: tex_coord, + normal: normal, }); } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index ccb158b..0376e7a 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4,7 +4,7 @@ pub mod obj; pub mod mtl; use std::fmt; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::fs::File; use std::io::{BufReader, Error}; use math::vector::{Vector2, Vector3}; @@ -19,6 +19,9 @@ pub enum Element { /// An empty element (correspond to an empty line for example). None, + /// Declares the use of a material file. + MaterialLib(String), + /// Changes the material used for the next faces. UseMaterial(String), @@ -48,6 +51,9 @@ pub enum Element { /// An unknown material instruction that will be copied into the mtl file. UnknownMaterialInstruction(String), + + /// Declares multiple faces. + Faces(Vec), } #[derive(Debug)] @@ -225,6 +231,16 @@ impl Parser for LP { Element::TextureCoordinate(v) => model.texture_coordinates.push(v), Element::Normal(v) => model.normals.push(v), + Element::MaterialLib(ref material_path) => { + // Append material_path to path + let mut path = PathBuf::from(path); + path.pop(); + path.push(material_path); + if let Err(err) = parse_into_model(path.to_str().unwrap(), model) { + return Err(err); + } + }, + Element::Face(f) => { if let Err(ModelError::IndexError(index, size)) = model.add_face(f) { return Err(ParserError::IndexOutOfBound(path.to_owned(), num, index, size)); @@ -274,7 +290,16 @@ impl Parser for LP { } current_material.unwrap().add_unknown_instruction(s); + }, + + Element::Faces(faces) => { + for f in faces { + if let Err(ModelError::IndexError(index, size)) = model.add_face(f) { + return Err(ParserError::IndexOutOfBound(path.to_owned(), num, index, size)); + } + } } + } } diff --git a/src/parser/obj.rs b/src/parser/obj.rs index 1e63eca..645d2c1 100644 --- a/src/parser/obj.rs +++ b/src/parser/obj.rs @@ -153,7 +153,6 @@ impl ObjParser { 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() @@ -185,10 +184,15 @@ impl ObjParser { } - if face_vertices.len() != 3 { - Err(ParserError::TooManyVertices(path.to_owned(), line_number, face_vertices.len())) - } else { + 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())) } @@ -203,12 +207,14 @@ impl LineParser for ObjParser { 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" | "mtllib" => Ok(Element::None), + "#" | "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), } diff --git a/src/programs/viewer.rs b/src/programs/viewer.rs index 7783d77..0dad6b8 100644 --- a/src/programs/viewer.rs +++ b/src/programs/viewer.rs @@ -1,6 +1,9 @@ extern crate glium; extern crate model_converter; +use std::env; +use std::process::exit; + use glium::Display; use glium::glutin; use glium::glutin::{ @@ -14,15 +17,45 @@ use glium::glutin::VirtualKeyCode; use model_converter::math::vector::Vector3; -use model_converter::parser::{parse, parse_into_model}; +use model_converter::parser::parse_into_model; use model_converter::renderer::Renderer; use model_converter::renderer::controls::OrbitControls; use model_converter::renderer::camera::Camera; +use model_converter::model::Model; fn main() { - let mut model = parse("./assets/models/link/link.mtl").unwrap(); - parse_into_model("./assets/models/link/link.obj", &mut model).unwrap(); + let mut model = Model::new(); + let mut inputs = vec![]; + let mut iterator = env::args(); + + // Skip the name of the program + iterator.next(); + + while let Some(argument) = iterator.next() { + match argument.as_ref() { + "-i" | "--input" => { + if let Some(path) = iterator.next() { + inputs.push(path); + } else { + eprintln!("Expecting path after {}", argument); + exit(-1); + } + }, + + arg => { + eprintln!("Argument unkown: {}", arg); + exit(-1); + }, + } + } + + for input in inputs { + if let Err(e) = parse_into_model(&input, &mut model) { + eprintln!("Error while parsing file: {}", e); + exit(1); + } + } let mut events_loop = EventsLoop::new(); let window = WindowBuilder::new(); diff --git a/src/renderer/camera.rs b/src/renderer/camera.rs index 1f02996..950e166 100644 --- a/src/renderer/camera.rs +++ b/src/renderer/camera.rs @@ -11,24 +11,8 @@ pub trait RenderCamera { fn get_view_matrix(&self) -> [[f32; 4]; 4]; /// Returns the perspective matrix of the camera. - fn get_perspective_matrix(&self, dimensions: (u32, u32)) -> [[f32; 4]; 4] { - let (width, height) = dimensions; - let aspect_ratio = height as f32 / width as f32; + fn get_perspective_matrix(&self) -> [[f32; 4]; 4] ; - let fov = 3.141592 / 3.0; - let zfar = 1024.0; - let znear = 0.1; - - use num::Float; - let f = 1.0 / (fov / 2.0).tan(); - - [ - [f * aspect_ratio , 0.0, 0.0 , 0.0], - [ 0.0 , f , 0.0 , 0.0], - [ 0.0 , 0.0, (zfar+znear)/(zfar-znear) , 1.0], - [ 0.0 , 0.0, -(2.0*zfar*znear)/(zfar-znear), 0.0], - ] - } } /// Creates a look at matrix from the center, the target pointed by the camera and the up vector. @@ -71,6 +55,21 @@ pub fn look_at_matrix(position: [f32; 3], target: [f32; 3], up: [f32; 3]) -> [[f } +/// Creates a perspective matrix of a camera. +pub fn perspective_matrix(aspect_ratio: f32, z_near: f32, z_far: f32) -> [[f32; 4]; 4] { + let fov = 3.141592 / 3.0; + + use num::Float; + let f = 1.0 / (fov / 2.0).tan(); + + [ + [f / aspect_ratio , 0.0, 0.0 , 0.0], + [ 0.0 , f , 0.0 , 0.0], + [ 0.0 , 0.0, (z_far+z_near)/(z_far-z_near) , 1.0], + [ 0.0 , 0.0, -(2.0*z_far*z_near)/(z_far-z_near), 0.0], + ] +} + /// A simple camera with its position, target and up vector. pub struct Camera { /// The position of the center of the camera. @@ -81,6 +80,16 @@ pub struct Camera { /// The up vector of the camera. pub up: Vector3, + + /// The minimum depth for visible things. + pub z_near: f32, + + /// The maximum depth for visible things. + pub z_far: f32, + + /// The aspect ratio of the camera. + pub aspect_ratio: f32, + } impl Camera { @@ -90,6 +99,9 @@ impl Camera { position: position, target: target, up: up, + z_near: 0.01, + z_far: 1000.0, + aspect_ratio: 16.0 / 9.0 } } } @@ -98,5 +110,9 @@ impl RenderCamera for Camera { fn get_view_matrix(&self) -> [[f32; 4]; 4] { look_at_matrix(self.position.into(), self.target.into(), self.up.into()) } + + fn get_perspective_matrix(&self) -> [[f32; 4]; 4] { + perspective_matrix(self.aspect_ratio, self.z_near, self.z_far) + } } diff --git a/src/renderer/controls.rs b/src/renderer/controls.rs index bf43b89..a0dc244 100644 --- a/src/renderer/controls.rs +++ b/src/renderer/controls.rs @@ -99,6 +99,12 @@ impl Controls for OrbitControls { self.pressed = state == ElementState::Pressed; }, + Event::WindowEvent { + event: WindowEvent::Resized(width, height), .. + } => { + camera.aspect_ratio = width as f32 / height as f32; + }, + Event::WindowEvent { event: WindowEvent::MouseWheel { delta: MouseScrollDelta::LineDelta(_, y), .. diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 5b1bf3f..75ce89e 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -171,7 +171,7 @@ impl<'a, D: Drawer + Facade + Sized> Renderer<'a, D> { for &(ref material, ref buffer) in buffers { - let perspective = camera.get_perspective_matrix(target.get_dimensions()); + let perspective = camera.get_perspective_matrix(); let view = camera.get_view_matrix(); if let &Some(ref texture) = &material.texture {