use glium::draw_parameters::DepthTest; use glium::texture::{ RawImage2d, Texture2d, }; use glium::index::{ NoIndices, PrimitiveType }; use glium::{ Program, Display, VertexBuffer, Frame, DrawParameters, Depth, }; use image; use model::{Model, Vertex}; use math::vector::Vector3; pub struct RenderMaterial { texture: Option, } impl RenderMaterial { pub fn new() -> RenderMaterial { RenderMaterial { texture: None } } pub fn from_texture_name(path: &str, display: &Display) -> RenderMaterial { let image = image::open(path); if let Ok(image) = image { let image = image.to_rgba(); let dim = image.dimensions(); let image = RawImage2d::from_raw_rgba_reversed(&image.into_raw(), dim); RenderMaterial { texture: Texture2d::new(display, image).ok() } } else { RenderMaterial { texture: None } } } } pub struct Camera { position: Vector3, target: Vector3, up: Vector3, } impl Camera { pub fn new(position: Vector3, target: Vector3, up: Vector3) -> Camera { Camera { position: position, target: target, up: up, } } pub fn get_view_matrix(&self) -> [[f32; 4]; 4] { let f = { let f = self.target - self.position; let len = f[0] * f[0] + f[1] * f[1] + f[2] * f[2]; let len = len.sqrt(); [f[0] / len, f[1] / len, f[2] / len] }; let s = [self.up[1] * f[2] - self.up[2] * f[1], self.up[2] * f[0] - self.up[0] * f[2], self.up[0] * f[1] - self.up[1] * f[0]]; let s_norm = { let len = s[0] * s[0] + s[1] * s[1] + s[2] * s[2]; let len = len.sqrt(); [s[0] / len, s[1] / len, s[2] / len] }; let u = [f[1] * s_norm[2] - f[2] * s_norm[1], f[2] * s_norm[0] - f[0] * s_norm[2], f[0] * s_norm[1] - f[1] * s_norm[0]]; let p = [-self.position[0] * s_norm[0] - self.position[1] * s_norm[1] - self.position[2] * s_norm[2], -self.position[0] * u[0] - self.position[1] * u[1] - self.position[2] * u[2], -self.position[0] * f[0] - self.position[1] * f[1] - self.position[2] * f[2]]; [ [s_norm[0], u[0], f[0], 0.0], [s_norm[1], u[1], f[1], 0.0], [s_norm[2], u[2], f[2], 0.0], [p[0], p[1], p[2], 1.0], ] } } pub struct Renderer<'a> { display: Display, program: Program, models: Vec<(&'a Model, Vec<(RenderMaterial, VertexBuffer)>)>, } impl<'a> Renderer<'a> { pub fn new(display: Display) -> Renderer<'a> { let program = Program::from_source( &display, include_str!("../assets/shaders/shader.vert"), include_str!("../assets/shaders/shader.frag"), None ).unwrap(); Renderer { display: display, program: program, models: vec![], } } pub fn get_perspective_matrix(&self, dimensions: (u32, u32)) -> [[f32; 4]; 4] { let (width, height) = dimensions; let aspect_ratio = height as f32 / width as f32; 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], ] } pub fn add_model(&mut self, model: &'a Model) { let mut buffers = vec![]; for part in &model.parts { let material = if let Some(ref material_name) = part.material_name { if let Some(material) = model.materials.get(material_name) { if let Some(path) = material.textures.get("map_Kd") { RenderMaterial::from_texture_name(path, &self.display) } else { RenderMaterial::new() } } else { RenderMaterial::new() } } else { RenderMaterial::new() }; let shape = part.build_shape(&model.vertices, &model.texture_coordinates, &model.normals); buffers.push((material, VertexBuffer::new(&self.display, &shape).unwrap())); } self.models.push((model, buffers)); } pub fn draw(&self) -> Frame { self.display.draw() } pub fn render(&self, camera: &Camera, target: &mut Frame) { use glium::Surface; target.clear_color_and_depth((0.0, 0.0, 1.0, 1.0), 1.0); let params = DrawParameters { depth: Depth { test: DepthTest::IfLess, write: true, .. Default::default() }, .. Default::default() }; for &(_, ref buffers) in &self.models { for &(ref material, ref buffer) in buffers { let perspective = self.get_perspective_matrix(target.get_dimensions()); let view = camera.get_view_matrix(); if let &Some(ref texture) = &material.texture { target.draw( buffer, NoIndices(PrimitiveType::TrianglesList), &self.program, &uniform!( tex: texture, perspective: perspective, view: view, ), ¶ms, ).unwrap(); } else { target.draw( buffer, NoIndices(PrimitiveType::TrianglesList), &self.program, &uniform!( perspective: perspective, view: view, ), ¶ms, ).unwrap(); }; } } } }