From 80b397d3ecbb50a013a70b2aa8e4928b38adddfc Mon Sep 17 00:00:00 2001 From: Thomas Forgione Date: Mon, 26 Feb 2018 11:45:53 +0100 Subject: [PATCH 1/4] Starting to work on rendering --- .gitignore | 2 ++ assets/shaders/shader.frag | 20 ++--------- assets/shaders/shader.vert | 13 +++---- src/math/vector.rs | 6 ++++ src/model.rs | 26 ++++++++++++++ src/programs/viewer.rs | 22 ++++++------ src/renderer.rs | 69 +++++++++++++++++++++++++++++++++++++- 7 files changed, 120 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index 143b1ca..21b615b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ /target/ **/*.rs.bk Cargo.lock + +assets/models diff --git a/assets/shaders/shader.frag b/assets/shaders/shader.frag index b850034..984541d 100644 --- a/assets/shaders/shader.frag +++ b/assets/shaders/shader.frag @@ -1,22 +1,8 @@ -varying vec3 fNormal; -varying vec4 fFrontColor; +#version 140 -vec3 ambientLight = vec3(0.2,0.2,0.2); -vec3 directionnalLight = normalize(vec3(10,5,7)); -vec3 directionnalLightFactor = vec3(0.5,0.5,0.5); - -uniform sampler2D tex; +out vec4 color; void main() { - - vec3 ambientFactor = ambientLight; - vec3 lambertFactor = max(vec3(0.0,0.0,0.0), dot(directionnalLight, fNormal) * directionnalLightFactor); - vec4 noTexColor = vec4(ambientFactor + lambertFactor, 1.0); - - vec4 color = texture2D(tex, gl_TexCoord[0].st); - - vec4 fragColor = noTexColor * color; - gl_FragColor = fragColor * fFrontColor; - + color = vec4(1.0, 0.0, 0.0, 1.0); } diff --git a/assets/shaders/shader.vert b/assets/shaders/shader.vert index a50f4ef..6134956 100644 --- a/assets/shaders/shader.vert +++ b/assets/shaders/shader.vert @@ -1,12 +1,7 @@ -varying vec3 fNormal; -varying vec4 fTexCoord; -varying vec4 fFrontColor; +#version 140 + +in vec3 vertex; void main() { - - fNormal = gl_Normal; - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; - fFrontColor = gl_Color; - + gl_Position = vec4(vertex, 1.0); } diff --git a/src/math/vector.rs b/src/math/vector.rs index f953223..e1745ab 100644 --- a/src/math/vector.rs +++ b/src/math/vector.rs @@ -31,6 +31,12 @@ macro_rules! make_vector { } } + impl Into<[T; $number]> for $name { + fn into(self) -> [T; $number] { + self.data + } + } + impl Into<($( $t ) ,* )> for $name { fn into(self) -> ($( $t ) ,* ) { ( $( self.data[$y] ), *) diff --git a/src/model.rs b/src/model.rs index f5435e5..111dbad 100644 --- a/src/model.rs +++ b/src/model.rs @@ -8,6 +8,15 @@ use parser::{parse, ParserError}; use math::vector::{Vector2, Vector3}; use math::bounding_box::BoundingBox3; +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Vertex { + vertex: [f64; 3], + tex_coord: [f64; 2], + normal: [f64; 3], +} + +implement_vertex!(Vertex, vertex, tex_coord, normal); + #[derive(Copy, Clone, PartialEq)] /// The indices needed for each vertex of a face. pub struct FaceVertex { @@ -154,6 +163,23 @@ impl Part { f.material_name = self.material_name.clone(); self.faces.push(f); } + + pub fn build_shape(&self, vertices: &Vec>, tex_coords: &Vec>, normals: &Vec>) -> Vec { + + let mut shape = vec![]; + 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(), + }); + } + } + + shape + } + } /// A 3D model. diff --git a/src/programs/viewer.rs b/src/programs/viewer.rs index 011eff6..97e6168 100644 --- a/src/programs/viewer.rs +++ b/src/programs/viewer.rs @@ -1,4 +1,5 @@ extern crate glium; +extern crate model_converter; use glium::Display; use glium::glutin; @@ -12,6 +13,9 @@ use glium::glutin::Event; use glium::glutin::WindowEvent; use glium::glutin::VirtualKeyCode; +use model_converter::parser::parse; +use model_converter::renderer::Renderer; + fn main() { let mut events_loop = EventsLoop::new(); @@ -19,20 +23,16 @@ fn main() { let context = ContextBuilder::new(); let display = Display::new(window, context, &events_loop).unwrap(); - let program = glium::Program::from_source( - &display, - include_str!("../../assets/shaders/shader.vert"), - include_str!("../../assets/shaders/shader.frag"), - None - ).unwrap(); - let mut closed = false; - while !closed { - let mut target = display.draw(); + let model = parse("./assets/models/toonlink/link.obj").unwrap(); + let mut renderer = Renderer::new(display); + renderer.add_model(&model); - use glium::Surface; - target.clear_color(0.0, 0.0, 1.0, 1.0); + while !closed { + + let mut target = renderer.draw(); + renderer.render(&mut target); target.finish().unwrap(); diff --git a/src/renderer.rs b/src/renderer.rs index 0fc0ebb..c7f745a 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1 +1,68 @@ -pub struct Renderer; +use glium::index::{ + NoIndices, + PrimitiveType +}; +use glium::{ + Program, + Display, + VertexBuffer, + Frame, +}; + +use model::{Model, Vertex}; + +pub struct Renderer<'a> { + display: Display, + program: Program, + models: Vec<(&'a Model, Vec>)>, +} + +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 add_model(&mut self, model: &'a Model) { + let mut buffers = vec![]; + + for part in &model.parts { + let shape = part.build_shape(&model.vertices, &model.texture_coordinates, &model.normals); + buffers.push(VertexBuffer::new(&self.display, &shape).unwrap()); + } + + self.models.push((model, buffers)); + } + + pub fn draw(&self) -> Frame { + self.display.draw() + } + + pub fn render(&self, target: &mut Frame) { + use glium::Surface; + target.clear_color(0.0, 0.0, 0.0, 1.0); + + for &(_, ref buffers) in &self.models { + for buffer in buffers { + target.draw( + buffer, + NoIndices(PrimitiveType::TrianglesList), + &self.program, + &uniform!(), + &Default::default(), + ).unwrap(); + } + } + } +} From b25dddaa9c6eac319006a4fbd99fa756ae06e820 Mon Sep 17 00:00:00 2001 From: Thomas Forgione Date: Mon, 26 Feb 2018 14:59:32 +0100 Subject: [PATCH 2/4] 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(); + }; } } } From b9bdac0dae2e81082b505de61cdb35152aec0b27 Mon Sep 17 00:00:00 2001 From: Thomas Forgione Date: Tue, 27 Feb 2018 11:42:35 +0100 Subject: [PATCH 3/4] Cleaning, fixed textures bug --- assets/shaders/shader.frag | 19 ++++- assets/shaders/shader.vert | 10 ++- src/programs/viewer.rs | 12 ++- src/renderer/camera.rs | 117 +++++++++++++++++++++++++++ src/{renderer.rs => renderer/mod.rs} | 90 +++------------------ 5 files changed, 155 insertions(+), 93 deletions(-) create mode 100644 src/renderer/camera.rs rename src/{renderer.rs => renderer/mod.rs} (58%) diff --git a/assets/shaders/shader.frag b/assets/shaders/shader.frag index dfb0e23..7f336ae 100644 --- a/assets/shaders/shader.frag +++ b/assets/shaders/shader.frag @@ -1,11 +1,22 @@ #version 140 -in vec2 v_tex_coords; -out vec4 color; - uniform sampler2D tex; +in vec3 v_normal; +in vec2 v_tex_coords; + +out vec4 color; + +vec3 ambientLight = vec3(0.3,0.3,0.3); +vec3 directionnalLight = normalize(vec3(10,5,7)); +vec3 directionnalLightFactor = vec3(0.6,0.6,0.6); + void main() { - color = texture(tex, v_tex_coords); + vec3 lambertComponent = dot(directionnalLight, v_normal) * directionnalLightFactor; + lambertComponent = max(vec3(0.0, 0.0, 0.0), lambertComponent); + + vec4 factor = vec4(ambientLight + lambertComponent, 1.0); + + color = factor * texture(tex, v_tex_coords); } diff --git a/assets/shaders/shader.vert b/assets/shaders/shader.vert index 22c4c7c..ec26b6b 100644 --- a/assets/shaders/shader.vert +++ b/assets/shaders/shader.vert @@ -1,14 +1,18 @@ #version 140 -in vec3 vertex; -in vec2 tex_coords; - uniform mat4 perspective; uniform mat4 view; +in vec3 vertex; +in vec2 tex_coords; +in vec3 normal; + out vec2 v_tex_coords; +out vec3 v_normal; void main() { + v_normal = transpose(inverse(mat3(view))) * normal; v_tex_coords = tex_coords; + gl_Position = perspective * view * vec4(vertex, 1.0); } diff --git a/src/programs/viewer.rs b/src/programs/viewer.rs index bf868e0..46473eb 100644 --- a/src/programs/viewer.rs +++ b/src/programs/viewer.rs @@ -12,9 +12,9 @@ use glium::glutin::Event; use glium::glutin::WindowEvent; use glium::glutin::VirtualKeyCode; -use model_converter::math::vector::Vector3; use model_converter::parser::{parse, parse_into_model}; -use model_converter::renderer::{Camera, Renderer}; +use model_converter::renderer::Renderer; +use model_converter::renderer::camera::RotatingCamera; fn main() { @@ -31,14 +31,12 @@ fn main() { 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), - ); + let mut camera = RotatingCamera::new(50.0); while !closed { + camera.increase_theta(0.025); + let mut target = renderer.draw(); renderer.render(&camera, &mut target); diff --git a/src/renderer/camera.rs b/src/renderer/camera.rs new file mode 100644 index 0000000..10a6be1 --- /dev/null +++ b/src/renderer/camera.rs @@ -0,0 +1,117 @@ +use math::vector::Vector3; + +pub trait Camera { + fn get_view_matrix(&self) -> [[f32; 4]; 4]; + 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 look_at_matrix(position: [f32; 3], target: [f32; 3], up: [f32; 3]) -> [[f32; 4]; 4] { + let f = { + let f = [ + target[0] - position[0], + target[1] - position[1], + target[2] - position[2], + ]; + 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 = [up[1] * f[2] - up[2] * f[1], + up[2] * f[0] - up[0] * f[2], + up[0] * f[1] - 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 = [-position[0] * s_norm[0] - position[1] * s_norm[1] - position[2] * s_norm[2], + -position[0] * u[0] - position[1] * u[1] - position[2] * u[2], + -position[0] * f[0] - position[1] * f[1] - 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 FixedCamera { + position: Vector3, + target: Vector3, + up: Vector3, +} + +impl FixedCamera { + pub fn new(position: Vector3, target: Vector3, up: Vector3) -> FixedCamera { + FixedCamera { + position: position, + target: target, + up: up, + } + } +} + +impl Camera for FixedCamera { + fn get_view_matrix(&self) -> [[f32; 4]; 4] { + look_at_matrix(self.position.into(), self.target.into(), self.up.into()) + } +} + +pub struct RotatingCamera { + distance: f32, + theta: f32, +} + +impl RotatingCamera { + pub fn new(distance: f32) -> RotatingCamera { + RotatingCamera { + distance: distance, + theta: 0.0, + } + } + + pub fn increase_theta(&mut self, dt: f32) { + self.theta += dt; + } +} + +impl Camera for RotatingCamera { + fn get_view_matrix(&self) -> [[f32; 4]; 4] { + let position = Vector3::new( + self.distance * self.theta.cos(), + 0.0, + self.distance * self.theta.sin(), + ); + + let target = Vector3::new(0.0, 0.0, 0.0); + let up = Vector3::new(0.0, 1.0, 0.0); + + look_at_matrix(position.into(), target.into(), up.into()) + } +} diff --git a/src/renderer.rs b/src/renderer/mod.rs similarity index 58% rename from src/renderer.rs rename to src/renderer/mod.rs index d587804..61f28c3 100644 --- a/src/renderer.rs +++ b/src/renderer/mod.rs @@ -1,7 +1,9 @@ +pub mod camera; + use glium::draw_parameters::DepthTest; use glium::texture::{ RawImage2d, - Texture2d, + SrgbTexture2d, }; use glium::index::{ NoIndices, @@ -18,10 +20,10 @@ use glium::{ use image; use model::{Model, Vertex}; -use math::vector::Vector3; +use renderer::camera::Camera; pub struct RenderMaterial { - texture: Option, + texture: Option, } impl RenderMaterial { @@ -40,7 +42,7 @@ impl RenderMaterial { let dim = image.dimensions(); let image = RawImage2d::from_raw_rgba_reversed(&image.into_raw(), dim); RenderMaterial { - texture: Texture2d::new(display, image).ok() + texture: SrgbTexture2d::new(display, image).ok() } } else { RenderMaterial { @@ -50,56 +52,6 @@ impl RenderMaterial { } } -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, @@ -111,8 +63,8 @@ impl<'a> Renderer<'a> { let program = Program::from_source( &display, - include_str!("../assets/shaders/shader.vert"), - include_str!("../assets/shaders/shader.frag"), + include_str!("../../assets/shaders/shader.vert"), + include_str!("../../assets/shaders/shader.frag"), None ).unwrap(); @@ -123,26 +75,6 @@ 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![]; @@ -173,9 +105,9 @@ impl<'a> Renderer<'a> { self.display.draw() } - pub fn render(&self, camera: &Camera, target: &mut Frame) { + pub fn render(&self, camera: &C, target: &mut Frame) { use glium::Surface; - target.clear_color_and_depth((0.0, 0.0, 1.0, 1.0), 1.0); + target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0); let params = DrawParameters { depth: Depth { @@ -189,7 +121,7 @@ impl<'a> Renderer<'a> { for &(_, ref buffers) in &self.models { for &(ref material, ref buffer) in buffers { - let perspective = self.get_perspective_matrix(target.get_dimensions()); + let perspective = camera.get_perspective_matrix(target.get_dimensions()); let view = camera.get_view_matrix(); if let &Some(ref texture) = &material.texture { From 376a38c4dc0ef4d9ad73b1093b9522f7f6c7b0f9 Mon Sep 17 00:00:00 2001 From: Thomas Forgione Date: Tue, 10 Apr 2018 15:13:35 +0200 Subject: [PATCH 4/4] Starting to work --- src/math/vector.rs | 5 ++ src/programs/viewer.rs | 34 +++++++---- src/renderer/camera.rs | 20 +++---- src/renderer/controls.rs | 119 +++++++++++++++++++++++++++++++++++++++ src/renderer/mod.rs | 6 +- 5 files changed, 161 insertions(+), 23 deletions(-) create mode 100644 src/renderer/controls.rs diff --git a/src/math/vector.rs b/src/math/vector.rs index e1745ab..2408a1a 100644 --- a/src/math/vector.rs +++ b/src/math/vector.rs @@ -66,6 +66,11 @@ macro_rules! make_vector { pub fn $x(&self) -> T { self.data[$y] } + + /// Get a mut ref to the coordinate of the vector. + pub fn $x_mut(&mut self) -> &mut T { + &mut self.data[$y] + } )* } diff --git a/src/programs/viewer.rs b/src/programs/viewer.rs index 46473eb..8bb6b30 100644 --- a/src/programs/viewer.rs +++ b/src/programs/viewer.rs @@ -12,14 +12,17 @@ use glium::glutin::Event; use glium::glutin::WindowEvent; use glium::glutin::VirtualKeyCode; + +use model_converter::math::vector::Vector3; use model_converter::parser::{parse, parse_into_model}; use model_converter::renderer::Renderer; -use model_converter::renderer::camera::RotatingCamera; +use model_converter::renderer::controls::OrbitControls; +use model_converter::renderer::camera::Camera; 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 model = parse("./assets/models/cube/cube.mtl").unwrap(); + parse_into_model("./assets/models/cube/cube.obj", &mut model).unwrap(); let mut events_loop = EventsLoop::new(); let window = WindowBuilder::new(); @@ -31,18 +34,21 @@ fn main() { let mut renderer = Renderer::new(display); renderer.add_model(&model); - let mut camera = RotatingCamera::new(50.0); + let mut camera = Camera::new( + Vector3::new( 0.0, 0.0, 0.0), + Vector3::new( 0.0, 0.0, 0.0), + Vector3::new( 0.0, 1.0, 0.0), + ); + + let mut controls = OrbitControls::new(&mut camera); while !closed { - camera.increase_theta(0.025); - - let mut target = renderer.draw(); - renderer.render(&camera, &mut target); - - target.finish().unwrap(); - events_loop.poll_events(|ev| { + + use model_converter::renderer::controls::Controls; + controls.manage_event(&ev, &mut camera); + match ev { // Close window Event::WindowEvent { @@ -61,5 +67,11 @@ fn main() { _ => (), } }); + + + let mut target = renderer.draw(); + renderer.render(&camera, &mut target); + target.finish().unwrap(); + } } diff --git a/src/renderer/camera.rs b/src/renderer/camera.rs index 10a6be1..68c640d 100644 --- a/src/renderer/camera.rs +++ b/src/renderer/camera.rs @@ -1,6 +1,6 @@ use math::vector::Vector3; -pub trait Camera { +pub trait RenderCamera { fn get_view_matrix(&self) -> [[f32; 4]; 4]; fn get_perspective_matrix(&self, dimensions: (u32, u32)) -> [[f32; 4]; 4] { let (width, height) = dimensions; @@ -61,15 +61,15 @@ pub fn look_at_matrix(position: [f32; 3], target: [f32; 3], up: [f32; 3]) -> [[f } -pub struct FixedCamera { - position: Vector3, - target: Vector3, - up: Vector3, +pub struct Camera { + pub position: Vector3, + pub target: Vector3, + pub up: Vector3, } -impl FixedCamera { - pub fn new(position: Vector3, target: Vector3, up: Vector3) -> FixedCamera { - FixedCamera { +impl Camera { + pub fn new(position: Vector3, target: Vector3, up: Vector3) -> Camera { + Camera { position: position, target: target, up: up, @@ -77,7 +77,7 @@ impl FixedCamera { } } -impl Camera for FixedCamera { +impl RenderCamera for Camera { fn get_view_matrix(&self) -> [[f32; 4]; 4] { look_at_matrix(self.position.into(), self.target.into(), self.up.into()) } @@ -101,7 +101,7 @@ impl RotatingCamera { } } -impl Camera for RotatingCamera { +impl RenderCamera for RotatingCamera { fn get_view_matrix(&self) -> [[f32; 4]; 4] { let position = Vector3::new( self.distance * self.theta.cos(), diff --git a/src/renderer/controls.rs b/src/renderer/controls.rs new file mode 100644 index 0000000..b15ef85 --- /dev/null +++ b/src/renderer/controls.rs @@ -0,0 +1,119 @@ +const EPSILON: f32 = 0.001; + +use glium::glutin::{ + Event, + WindowEvent, + ElementState, + MouseButton, + MouseScrollDelta, +}; + +use math::vector::Vector2; +use renderer::camera::Camera; + +/// The trait that all controls should implement. +pub trait Controls { + + /// Modifies the camera depending on the event. + fn manage_event(&mut self, event: &Event, camera: &mut Camera); +} + +/// An orbit controls allowing to orbit around an object. +/// +/// Only object centered are supported. +pub struct OrbitControls { + /// The last position of the mouse. + mouse_position: Vector2, + + /// Wether the left click of the mouse is pressed or not. + pressed: bool, + + /// The theta angle of the position of the camera in spheric coordinates. + theta: f32, + + /// The phi angle of the position of the camera in spheric coordinates. + phi: f32, + + /// The distance between the camera and the origin. + distance: f32, + + /// The sensitiviy of the rotation of the mouse. + sensitivity: f32, +} + +impl OrbitControls { + + /// Creates a new orbit controls, and initializes the camera. + pub fn new(camera: &mut Camera) -> OrbitControls { + let controls = OrbitControls { + mouse_position: Vector2::new(0.0, 0.0), + pressed: false, + theta: 0.0, + phi: 0.0, + distance: 5.0, + sensitivity: 200.0, + }; + + *camera.position.x_mut() = controls.distance * controls.theta.cos(); + *camera.position.y_mut() = 0.0; + *camera.position.z_mut() = controls.distance * controls.phi.sin(); + controls + } +} + +impl Controls for OrbitControls { + fn manage_event(&mut self, event: &Event, camera: &mut Camera) { + match *event { + + Event::WindowEvent { + event: WindowEvent::MouseInput { + button: MouseButton::Left, + state, .. + }, .. + } => { + self.pressed = state == ElementState::Pressed; + }, + + Event::WindowEvent { + event: WindowEvent::MouseWheel { + delta: MouseScrollDelta::LineDelta(_, y), .. + }, .. + } => { + self.distance -= y; + + *camera.position.x_mut() = self.distance * self.phi.cos() * self.theta.cos(); + *camera.position.y_mut() = self.distance * self.phi.sin(); + *camera.position.z_mut() = self.distance * self.phi.cos() * self.theta.sin(); + }, + + Event::WindowEvent{ + event: WindowEvent::CursorMoved { + position: (x, y), .. + }, .. + } => { + let current_position = Vector2::new(x as f32, y as f32); + + if self.pressed { + let difference = (current_position - self.mouse_position) / self.sensitivity; + + self.theta += difference.x(); + self.phi += difference.y(); + + use std::f32::consts::PI; + self.phi = self.phi.max(- PI/2.0 + EPSILON); + self.phi = self.phi.min( PI/2.0 - EPSILON); + + *camera.position.x_mut() = self.distance * self.phi.cos() * self.theta.cos(); + *camera.position.y_mut() = self.distance * self.phi.sin(); + *camera.position.z_mut() = self.distance * self.phi.cos() * self.theta.sin(); + + } + + // Record new position + self.mouse_position = current_position; + }, + + _ => (), + } + } +} diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 61f28c3..5cd23b7 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -1,4 +1,5 @@ pub mod camera; +pub mod controls; use glium::draw_parameters::DepthTest; use glium::texture::{ @@ -20,7 +21,7 @@ use glium::{ use image; use model::{Model, Vertex}; -use renderer::camera::Camera; +use renderer::camera::RenderCamera; pub struct RenderMaterial { texture: Option, @@ -105,7 +106,7 @@ impl<'a> Renderer<'a> { self.display.draw() } - pub fn render(&self, camera: &C, target: &mut Frame) { + pub fn render(&self, camera: &C, target: &mut Frame) { use glium::Surface; target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0); @@ -119,6 +120,7 @@ impl<'a> Renderer<'a> { }; for &(_, ref buffers) in &self.models { + for &(ref material, ref buffer) in buffers { let perspective = camera.get_perspective_matrix(target.get_dimensions());