//! Module containg the struct for storing 3D model pub mod face; pub mod material; use std::collections::HashMap; use std::collections::hash_map::Entry; use std::rc::Rc; use glium::VertexBuffer; use glium::texture::SrgbTexture2d; use parser::{parse_file, ParserError}; use model::face::{FaceVertex, Face}; use model::material::Material; use math::vector::{Vector2, Vector3}; use math::bounding_box::BoundingBox3; use renderer::Renderer; #[derive(Copy, Clone, Debug, PartialEq)] /// A raw vertex, with its 3D coordinates, texture coordinates and normals. pub struct Vertex { vertex: [f64; 3], tex_coords: [f64; 2], normal: [f64; 3], } implement_vertex!(Vertex, vertex, tex_coords, normal); /// A part of a 3D model. /// /// It contains a list of faces that correspond to the same material. pub struct Part { /// Name of the corresponding material. pub material_name: Option, /// List of all the faces in the part. faces: Vec, /// VertexBuffer for rendering. vertex_buffer: Option>, /// True if the faces have been changed since the last time the buffer was built. needs_update: bool, } impl Part { /// Creates a empty part from its material name. pub fn new(material_name: Option) -> Part { Part { material_name: material_name, faces: vec![], vertex_buffer: None, needs_update: true, } } /// Adds a face to the part. /// /// The face material will be changed according to the part material. pub fn add_face(&mut self, f: Face) { let mut f = f; f.material_name = self.material_name.clone(); self.faces.push(f); self.needs_update = true; } /// Returns a reference to the vertex buffer. pub fn vertex_buffer(&self) -> &Option> { &self.vertex_buffer } /// Returns a reference to the faces. pub fn faces(&self) -> &Vec { &self.faces } } /// A 3D model. /// /// It contains all the data needed to do a rendering. pub struct Model { /// List of all vertices. pub vertices: Vec>, /// List of all texture coordinates. pub texture_coordinates: Vec>, /// List of all normals. pub normals: Vec>, /// Map associating the name of a material to the real material. pub materials: HashMap, /// Map associating the name of a texture and the real texture. pub textures: HashMap>, /// The list of parts of the model. pub parts: Vec, /// The list of faces, in the order of the file pub faces: Vec, /// The part that is currently selected. current_part_index: Option, } /// Error when adding a face to a model. pub enum ModelError { /// Index (first usize) received but greater than size (second usize). IndexError(usize, usize), } impl Model { /// Creates an empty model. pub fn new() -> Model { Model { vertices: vec![], texture_coordinates: vec![], normals: vec![], materials: HashMap::new(), parts: vec![], faces: vec![], current_part_index: None, textures: HashMap::new(), } } /// Copies the materials and textures from the other model into self. pub fn merge_material_and_textures(&mut self, other: &Model) { // Merge the materials for (name, material) in &other.materials { let entry = self.materials.entry(name.clone()); if let Entry::Occupied(_) = entry { eprintln!("Warning: materials merged but sharing the same name"); // Return error } else { entry.or_insert(material.clone()); } } // Merge the textures for (name, texture) in &other.textures { let entry = self.textures.entry(name.clone()); if let Entry::Occupied(_) = entry { eprintln!("Warning: textures merged but sharing the same name"); // return Error; } else { entry.or_insert(texture.clone()); } } } /// Merges the model passed as parameter into new parts of the current model. pub fn merge_in_new_parts(&mut self, other: Model) { let mut other = other; // Merge the materials for (name, material) in other.materials { let entry = self.materials.entry(name); if let Entry::Occupied(_) = entry { eprintln!("Warning: materials merged but sharing the same name"); // Return error } else { entry.or_insert(material); } } // Merge the textures for (name, texture) in other.textures { let entry = self.textures.entry(name); if let Entry::Occupied(_) = entry { eprintln!("Warning: textures merged but sharing the same name"); // return Error; } else { entry.or_insert(texture); } } let vertex_offset = self.vertices.len(); let tex_coord_offset = self.texture_coordinates.len(); let normal_offset = self.normals.len(); let face_offset = self.faces.len(); // Merge the elements self.vertices.append(&mut other.vertices); self.texture_coordinates.append(&mut other.texture_coordinates); self.normals.append(&mut other.normals); // Merge the parts and faces for part in &mut other.parts { let mut new_part = Part::new(part.material_name.clone()); for face in &mut part.faces { for fv in &mut [&mut face.a, &mut face.b, &mut face.c] { fv.vertex += vertex_offset; if let Some(tex_coord) = fv.texture_coordinate { fv.texture_coordinate = Some(tex_coord + tex_coord_offset); } if let Some(normal) = fv.normal { fv.normal = Some(normal + normal_offset); } } if let Some(id) = face.id { face.id = Some(id + face_offset); } face.material_name = new_part.material_name.clone(); new_part.faces.push(face.clone()); self.faces.push(face.clone()); } new_part.needs_update = true; self.parts.push(new_part); } } /// Merges the model passed as parameter into existing parts of the current model. pub fn merge_in_existing_parts(&mut self, other: Model) { let mut other = other; // Merge the materials for (name, material) in other.materials { let entry = self.materials.entry(name); if let Entry::Occupied(_) = entry { eprintln!("Warning: materials merged but sharing the same name"); // Return error } else { entry.or_insert(material); } } // Merge the textures for (name, texture) in other.textures { let entry = self.textures.entry(name); if let Entry::Occupied(_) = entry { eprintln!("Warning: textures merged but sharing the same name"); // return Error; } else { entry.or_insert(texture); } } let vertex_offset = self.vertices.len(); let tex_coord_offset = self.texture_coordinates.len(); let normal_offset = self.normals.len(); let face_offset = self.faces.len(); // Merge the elements self.vertices.append(&mut other.vertices); self.texture_coordinates.append(&mut other.texture_coordinates); self.normals.append(&mut other.normals); // Merge the parts and faces for part in &mut other.parts { self.change_part(part.material_name.clone()); let mut new_faces = vec![]; { let new_part = self.current_part_mut().unwrap(); for face in &mut part.faces { for fv in &mut [&mut face.a, &mut face.b, &mut face.c] { fv.vertex += vertex_offset; if let Some(tex_coord) = fv.texture_coordinate { fv.texture_coordinate = Some(tex_coord + tex_coord_offset); } if let Some(normal) = fv.normal { fv.normal = Some(normal + normal_offset); } } if let Some(id) = face.id { face.id = Some(id + face_offset); } face.material_name = new_part.material_name.clone(); new_part.faces.push(face.clone()); new_faces.push(face); } new_part.needs_update = true; } for face in new_faces { self.faces.push(face.clone()); } } } /// Creates a model from a file. pub fn from(path: &str) -> Result { parse_file(path) } /// Computes the area of a 3D face of the model. pub fn face_area(&self, f: &Face) -> f64 { self.face_vertex_area(&f.a, &f.b, &f.c) } /// Computes the 3D area of a face from 3 face vertices. pub fn face_vertex_area(&self, a: &FaceVertex, b: &FaceVertex, c: &FaceVertex) -> f64 { let v1 = self.vertices[a.vertex]; let v2 = self.vertices[b.vertex]; let v3 = self.vertices[c.vertex]; Vector3::area(v1, v2, v3) } /// Returns the name of the current material, i.e. the material corresponding to the current /// part. pub fn current_material_name(&self) -> Option { if let Some(p) = self.current_part() { p.material_name.clone() } else { None } } /// Changes the part in order to select a specific material. /// /// If no part with such material exists, it will be created. pub fn change_part(&mut self, material_name: Option) { self.current_part_index = None; for (index, part) in self.parts.iter().enumerate() { if part.material_name == material_name { self.current_part_index = Some(index); } } if self.current_part_index.is_none() { // Create a new part self.create_part(material_name.clone()); self.current_part_index = Some(self.parts.len() - 1); } } /// Creates a part with a specific material. pub fn create_part(&mut self, material_name: Option) { self.parts.push(Part::new(material_name)); } /// Returns the current selected part. /// /// Returns None if the model contains no part. pub fn current_part(&self) -> Option<&Part> { if let Some(index) = self.current_part_index { Some(&self.parts[index]) } else { None } } /// Returns the current selected part. /// /// Returns None if the model contains no part. pub fn current_part_mut(&mut self) -> Option<&mut Part> { if let Some(index) = self.current_part_index { Some(&mut self.parts[index]) } else { None } } /// Adds a face to the current part of the model. /// /// Returns a ModelError if the face has out of bounds face vertices. pub fn add_face(&mut self, f: Face) -> Result<(), ModelError> { // Check that the indices are correct for face_vertex in [&f.a, &f.b, &f.c].iter() { if face_vertex.vertex >= self.vertices.len() { return Err(ModelError::IndexError(face_vertex.vertex, self.vertices.len())); } if let Some(tc) = face_vertex.texture_coordinate { if tc >= self.texture_coordinates.len() { return Err(ModelError::IndexError(tc, self.texture_coordinates.len())); } } if let Some(n) = face_vertex.normal { if n >= self.normals.len() { return Err(ModelError::IndexError(n, self.normals.len())); } } } let material_name = self.current_material_name(); self.change_part(material_name.clone()); // Add the id and the material name let mut f = f; f.id = Some(self.faces.len()); f.material_name = material_name; self.current_part_mut().unwrap().add_face(f.clone()); self.faces.push(f); Ok(()) } /// Computes the bounding box of the model. /// /// This function works on the vertices. If there is an unused vertex, it will still count in /// the bounding box. pub fn bounding_box(&self) -> BoundingBox3 { let mut bbox = BoundingBox3::new(self.vertices[0].clone(), self.vertices[0].clone()); for vertex in &self.vertices { bbox.add_point(&vertex); } bbox } /// Computes stats on the area of the faces /// /// Returns a pair containing the average and the standard deviation. pub fn get_area_stats(&self) -> (f64, f64) { let mut areas = vec![]; let mut avg = 0.0; for (index, face) in self.faces.iter().enumerate() { let area = Vector3::area( self.vertices[face.a.vertex], self.vertices[face.b.vertex], self.vertices[face.c.vertex], ); areas.push((index, area)); avg += area; } avg /= areas.len() as f64; let mut sd = 0.0; for &(_, area) in &areas { sd += (area - avg) * (area - avg); } sd = (sd / areas.len() as f64).sqrt(); (avg, sd) } /// Centers and scales the model from a bounding box. pub fn center_and_scale_from_box(&mut self, bbox: &BoundingBox3) { // let bbox = self.bounding_box(); let center = (bbox.min() + bbox.max()) / 2.0; let size = (bbox.max() - bbox.min()).norm(); for vertex in &mut self.vertices { *vertex -= center; *vertex /= size; } } /// Centers and scales the model from its own bounding box. pub fn center_and_scale(&mut self) { let bbox = self.bounding_box(); self.center_and_scale_from_box(&bbox); } /// Creates vertex buffers for each part of the model. pub fn build_vertex_buffers(&mut self, renderer: &Renderer) { for part in &mut self.parts { Model::build_vertex_buffer_for_part( &self.vertices, &self.texture_coordinates, &self.normals, part, renderer); } } /// Creates only non-existant vertex buffers, doesn't update the old ones. pub fn build_new_vertex_buffers(&mut self, renderer: &Renderer) { for part in &mut self.parts { if part.vertex_buffer.is_none() || part.needs_update { Model::build_vertex_buffer_for_part( &self.vertices, &self.texture_coordinates, &self.normals, part, renderer); } } } /// Creates a vertex buffer that can be rendered for a part of the model. pub fn build_vertex_buffer_for_part( vertices: &Vec>, texture_coordinates: &Vec>, normals: &Vec>, part: &mut Part, renderer: &Renderer) { let mut vertex_buffer = vec![]; for face in part.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 { texture_coordinates[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] }; vertex_buffer.push(Vertex { vertex: vertex, tex_coords: tex_coord, normal: normal, }); } } part.vertex_buffer = Some(renderer.build_vertex_buffer(&vertex_buffer)); part.needs_update = false; } /// Builds the textures of all materials. pub fn build_textures(&mut self, renderer: &Renderer) { for (_, material) in self.materials.clone() { self.build_texture_for_material(&material, renderer); } } /// Builds the SrgbTextures for rendering. pub fn build_texture_for_material(&mut self, material: &Material, renderer: &Renderer) { if let Some((path, _)) = material.textures.get("map_Kd") { let texture = renderer.make_texture(path); // Don't need to insert multiple times the same texture self.textures.entry(path.to_owned()).or_insert(Rc::new(texture)); }; } /// Returns the rendering texture. pub fn get_texture_by_name(&self, name: &str) -> Option<&Rc> { self.textures.get(name) } /// Returns the material. pub fn get_material_by_name(&self, name: &str) -> Option<&Material> { self.materials.get(name) } /// Returns a ref mut to the table of textures. pub fn textures_mut(&mut self) -> &mut HashMap> { &mut self.textures } }