From b25dddaa9c6eac319006a4fbd99fa756ae06e820 Mon Sep 17 00:00:00 2001 From: Thomas Forgione Date: Mon, 26 Feb 2018 14:59:32 +0100 Subject: [PATCH] Starting to support textures --- Cargo.toml | 1 + assets/shaders/shader.frag | 5 +- assets/shaders/shader.vert | 9 +- src/lib.rs | 1 + src/model.rs | 14 +-- src/programs/viewer.rs | 20 +++-- src/renderer.rs | 178 ++++++++++++++++++++++++++++++++++--- 7 files changed, 201 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 817ca38..642e0c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Thomas Forgione "] [dependencies] num = "*" glium = "*" +image = "*" verbose-log = { git = "https://gitea.tforgione.fr/dash-3d/verbose-log" } [[bin]] diff --git a/assets/shaders/shader.frag b/assets/shaders/shader.frag index 984541d..dfb0e23 100644 --- a/assets/shaders/shader.frag +++ b/assets/shaders/shader.frag @@ -1,8 +1,11 @@ #version 140 +in vec2 v_tex_coords; out vec4 color; +uniform sampler2D tex; + void main() { - color = vec4(1.0, 0.0, 0.0, 1.0); + color = texture(tex, v_tex_coords); } diff --git a/assets/shaders/shader.vert b/assets/shaders/shader.vert index 6134956..22c4c7c 100644 --- a/assets/shaders/shader.vert +++ b/assets/shaders/shader.vert @@ -1,7 +1,14 @@ #version 140 in vec3 vertex; +in vec2 tex_coords; + +uniform mat4 perspective; +uniform mat4 view; + +out vec2 v_tex_coords; void main() { - gl_Position = vec4(vertex, 1.0); + v_tex_coords = tex_coords; + gl_Position = perspective * view * vec4(vertex, 1.0); } diff --git a/src/lib.rs b/src/lib.rs index 68d7bed..f0878a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ extern crate num; +extern crate image; #[macro_use] extern crate verbose_log; #[macro_use] extern crate glium; diff --git a/src/model.rs b/src/model.rs index 111dbad..a21c7a0 100644 --- a/src/model.rs +++ b/src/model.rs @@ -10,12 +10,12 @@ use math::bounding_box::BoundingBox3; #[derive(Copy, Clone, Debug, PartialEq)] pub struct Vertex { - vertex: [f64; 3], - tex_coord: [f64; 2], - normal: [f64; 3], + vertex: [f64; 3], + tex_coords: [f64; 2], + normal: [f64; 3], } -implement_vertex!(Vertex, vertex, tex_coord, normal); +implement_vertex!(Vertex, vertex, tex_coords, normal); #[derive(Copy, Clone, PartialEq)] /// The indices needed for each vertex of a face. @@ -170,9 +170,9 @@ impl Part { for face in &self.faces { for &&v in &[&face.a, &face.b, &face.c] { shape.push(Vertex { - vertex: vertices[v.vertex].into(), - tex_coord: tex_coords[v.texture_coordinate.unwrap()].into(), - normal: normals[v.normal.unwrap()].into(), + vertex: vertices[v.vertex].into(), + tex_coords: tex_coords[v.texture_coordinate.unwrap()].into(), + normal: normals[v.normal.unwrap()].into(), }); } } diff --git a/src/programs/viewer.rs b/src/programs/viewer.rs index 97e6168..bf868e0 100644 --- a/src/programs/viewer.rs +++ b/src/programs/viewer.rs @@ -6,33 +6,41 @@ use glium::glutin; use glium::glutin::{ EventsLoop, WindowBuilder, - ContextBuilder, }; use glium::glutin::Event; use glium::glutin::WindowEvent; use glium::glutin::VirtualKeyCode; -use model_converter::parser::parse; -use model_converter::renderer::Renderer; +use model_converter::math::vector::Vector3; +use model_converter::parser::{parse, parse_into_model}; +use model_converter::renderer::{Camera, Renderer}; fn main() { + let mut model = parse("./assets/models/toonlink/link.mtl").unwrap(); + parse_into_model("./assets/models/toonlink/link.obj", &mut model).unwrap(); + let mut events_loop = EventsLoop::new(); let window = WindowBuilder::new(); - let context = ContextBuilder::new(); + let context = glutin::ContextBuilder::new().with_depth_buffer(24); let display = Display::new(window, context, &events_loop).unwrap(); let mut closed = false; - let model = parse("./assets/models/toonlink/link.obj").unwrap(); let mut renderer = Renderer::new(display); renderer.add_model(&model); + let camera = Camera::new( + Vector3::new(50.0, 0.0, 25.0), + Vector3::new(0.0, 0.0, 0.0), + Vector3::new(0.0, 1.0, 0.0), + ); + while !closed { let mut target = renderer.draw(); - renderer.render(&mut target); + renderer.render(&camera, &mut target); target.finish().unwrap(); diff --git a/src/renderer.rs b/src/renderer.rs index c7f745a..d587804 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,3 +1,8 @@ +use glium::draw_parameters::DepthTest; +use glium::texture::{ + RawImage2d, + Texture2d, +}; use glium::index::{ NoIndices, PrimitiveType @@ -7,14 +12,98 @@ use glium::{ 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>)>, + models: Vec<(&'a Model, Vec<(RenderMaterial, VertexBuffer)>)>, } impl<'a> Renderer<'a> { @@ -34,12 +123,47 @@ impl<'a> Renderer<'a> { } } + 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(VertexBuffer::new(&self.display, &shape).unwrap()); + buffers.push((material, VertexBuffer::new(&self.display, &shape).unwrap())); } self.models.push((model, buffers)); @@ -49,19 +173,49 @@ impl<'a> Renderer<'a> { self.display.draw() } - pub fn render(&self, target: &mut Frame) { + pub fn render(&self, camera: &Camera, target: &mut Frame) { use glium::Surface; - target.clear_color(0.0, 0.0, 0.0, 1.0); + 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 buffer in buffers { - target.draw( - buffer, - NoIndices(PrimitiveType::TrianglesList), - &self.program, - &uniform!(), - &Default::default(), - ).unwrap(); + 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(); + }; } } }