From 83065cedf2bb3376e815d9dcf6d940e03fe22b11 Mon Sep 17 00:00:00 2001 From: Thomas Forgione Date: Thu, 12 Apr 2018 17:23:40 +0200 Subject: [PATCH] Oh my god, rust is so good --- assets/shaders/shader.frag | 5 ++ src/programs/viewer.rs | 10 ++- src/renderer/controls.rs | 156 ++++++++++++++++++++++++++++++++++++- src/renderer/mod.rs | 87 ++++++++++++++++++++- 4 files changed, 251 insertions(+), 7 deletions(-) diff --git a/assets/shaders/shader.frag b/assets/shaders/shader.frag index 7f336ae..e7dfded 100644 --- a/assets/shaders/shader.frag +++ b/assets/shaders/shader.frag @@ -18,5 +18,10 @@ void main() { vec4 factor = vec4(ambientLight + lambertComponent, 1.0); color = factor * texture(tex, v_tex_coords); + + if (color.a < 0.5) { + discard; + } + } diff --git a/src/programs/viewer.rs b/src/programs/viewer.rs index 7dcba46..a67592d 100644 --- a/src/programs/viewer.rs +++ b/src/programs/viewer.rs @@ -20,6 +20,7 @@ use model_converter::math::vector::Vector3; use model_converter::parser::parse_into_model; use model_converter::renderer::Renderer; use model_converter::renderer::controls::OrbitControls; +use model_converter::renderer::controls::FirstPersonControls; use model_converter::renderer::camera::Camera; use model_converter::model::Model; @@ -75,16 +76,18 @@ fn main() { Vector3::new( 0.0, 1.0, 0.0), ); - let mut controls = OrbitControls::around(&model, &mut camera); + camera.z_near = 0.00001; + + let mut controls = FirstPersonControls::new(); renderer.show(); + use model_converter::renderer::controls::Controls; while !closed { events_loop.poll_events(|ev| { - use model_converter::renderer::controls::Controls; - controls.manage_event(&ev, &mut camera); + controls.manage_event(&ev, &mut camera, &renderer); match ev { // Close window @@ -105,6 +108,7 @@ fn main() { } }); + controls.update(&mut camera, &renderer); let mut target = renderer.draw(); renderer.render(&camera, &mut target); diff --git a/src/renderer/controls.rs b/src/renderer/controls.rs index a0dc244..0617ec6 100644 --- a/src/renderer/controls.rs +++ b/src/renderer/controls.rs @@ -2,23 +2,30 @@ const EPSILON: f32 = 0.001; +use glium::backend::glutin::Display; use glium::glutin::{ Event, WindowEvent, ElementState, MouseButton, MouseScrollDelta, + KeyboardInput, + VirtualKeyCode, }; use math::vector::{Vector2, Vector3}; use renderer::camera::Camera; +use renderer::Renderer; use model::Model; /// 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); + fn manage_event<'a>(&mut self, event: &Event, camera: &mut Camera, renderer: &Renderer<'a, Display>); + + /// Updates the camera depending on time. + fn update<'a>(&mut self, camera: &mut Camera, renderer: &Renderer<'a, Display>); } /// An orbit controls allowing to orbit around an object. @@ -87,7 +94,7 @@ impl OrbitControls { } impl Controls for OrbitControls { - fn manage_event(&mut self, event: &Event, camera: &mut Camera) { + fn manage_event<'a>(&mut self, event: &Event, camera: &mut Camera, _: &Renderer<'a, Display>) { match *event { Event::WindowEvent { @@ -110,7 +117,7 @@ impl Controls for OrbitControls { delta: MouseScrollDelta::LineDelta(_, y), .. }, .. } => { - self.distance -= y; + self.distance -= y / self.sensitivity; *camera.position.x_mut() = self.distance * self.phi.cos() * self.theta.cos(); *camera.position.y_mut() = self.distance * self.phi.sin(); @@ -153,4 +160,147 @@ impl Controls for OrbitControls { _ => (), } } + + fn update<'a>(&mut self, _: &mut Camera, _: &Renderer<'a, Display>) { + + } +} + +/// First person controls, just like in video games. +pub struct FirstPersonControls { + + /// Theta angle of the spheric coordinates of the direction of the camera. + theta: f32, + + /// Phi angle of the spheric coordinates of the direction of the camera. + phi: f32, + + /// Current position of the camera. + position: Vector3, + + /// Vector indicating the direction of the camera. + forward: Vector3, + + /// Speed of the camera. + speed: f32, + + /// Sensitivity of the mouse. + sensitivity: f32, + + /// Wether the forward button is pressed or not. + forward_pressed: bool, + + /// Wether the backward button is pressed or not. + backward_pressed: bool, +} + +impl FirstPersonControls { + /// Creates a new default first person controls. + pub fn new() -> FirstPersonControls { + FirstPersonControls { + theta: 0.0, + phi: 0.0, + position: Vector3::new(0.0, 0.0, 0.0), + forward: Vector3::new(0.0, 0.0, 1.0), + speed: 0.001, + forward_pressed: false, + backward_pressed: false, + sensitivity: 500.0, + } + } + + /// Updates the camera according to the state of the controls. + fn update_camera(&self, camera: &mut Camera) { + camera.position = self.position; + camera.target = self.position + self.forward; + } +} + +impl Controls for FirstPersonControls { + + fn manage_event<'a>(&mut self, event: &Event, camera: &mut Camera, renderer: &Renderer<'a, Display>) { + match *event { + + // On resize window + Event::WindowEvent { + event: WindowEvent::Resized(width, height), .. + } => { + camera.aspect_ratio = width as f32 / height as f32; + }, + + // On Z pressed + Event::WindowEvent { + event: WindowEvent::KeyboardInput { + input: KeyboardInput { + virtual_keycode: Some(VirtualKeyCode::Z), state, .. + }, .. + }, .. + } => { + self.forward_pressed = state == ElementState::Pressed; + }, + + // On S pressed + Event::WindowEvent { + event: WindowEvent::KeyboardInput { + input: KeyboardInput { + virtual_keycode: Some(VirtualKeyCode::S), state, .. + }, .. + }, .. + } => { + self.backward_pressed = state == ElementState::Pressed; + }, + + // On mouse move + Event::WindowEvent { + event: WindowEvent::CursorMoved { + position: (x, y), .. + }, .. + } => { + + let size = renderer.gl_window().window().get_inner_size().unwrap(); + let center = Vector2::new(size.0 as f32 / 2.0, size.1 as f32 / 2.0); + let current_position = Vector2::new(x as f32, y as f32); + let difference = (current_position - center) / 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); + + self.forward = Vector3::new( + self.phi.cos() * self.theta.cos(), + self.phi.sin(), + self.phi.cos() * self.theta.sin(), + ); + + // Move the cursor back to the center + renderer + .gl_window() + .window() + .set_cursor_position(size.0 as i32 / 2, size.1 as i32/ 2) + .unwrap(); + + }, + + + _ => (), + + } + + self.update_camera(camera); + } + + fn update<'a>(&mut self, camera: &mut Camera, _: &Renderer<'a, Display>) { + if self.forward_pressed { + self.position += self.forward * self.speed; + } + + if self.backward_pressed { + self.position -= self.forward * self.speed; + } + + self.update_camera(camera); + } } diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 0da372d..98b5a94 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -3,7 +3,12 @@ pub mod camera; pub mod controls; -use glium::draw_parameters::DepthTest; +use std::cell::Ref; +use std::borrow::Cow; + +use glium; +use glium::glutin::GlWindow; +use glium::draw_parameters::{DepthTest, Blend}; use glium::texture::{ RawImage2d, SrgbTexture2d, @@ -29,6 +34,22 @@ use image; use model::{Model, Vertex}; use renderer::camera::RenderCamera; +struct RGBAImageData { + pub data: Vec<(u8, u8, u8, u8)>, + pub width: u32, + pub height: u32, +} + +impl glium::texture::Texture2dDataSink<(u8, u8, u8, u8)> for RGBAImageData { + fn from_raw(data: Cow<[(u8, u8, u8, u8)]>, width: u32, height: u32) -> Self { + RGBAImageData { + data: data.into_owned(), + width: width, + height: height, + } + } +} + /// Anything on which you can call draw to get a frame. pub trait Drawer { /// Get a frame from the drawer. @@ -163,6 +184,7 @@ impl<'a, D: Drawer + Facade + Sized> Renderer<'a, D> { write: true, .. Default::default() }, + blend: Blend::alpha_blending(), .. Default::default() }; @@ -201,6 +223,64 @@ impl<'a, D: Drawer + Facade + Sized> Renderer<'a, D> { } } + + /// Returns an image of the corresponding frame. + pub fn capture(&self, dimensions: (u32, u32)) -> image::DynamicImage { + + let rect = glium::Rect { + left: 0, + bottom: 0, + width: dimensions.0, + height: dimensions.1, + }; + + let blit_target = glium::BlitTarget { + left: 0, + bottom: 0, + width: dimensions.0 as i32, + height: dimensions.1 as i32, + }; + + // Create temporary texture and blit the front buffer to it + let texture = glium::texture::Texture2d::empty(self.facade(), dimensions.0, dimensions.1) + .unwrap(); + let framebuffer = glium::framebuffer::SimpleFrameBuffer::new(self.facade(), &texture) + .unwrap(); + + use glium::Surface; + framebuffer.blit_from_frame( + &rect, + &blit_target, + glium::uniforms::MagnifySamplerFilter::Nearest + ); + + // Read the texture into new pixel buffer + let pixel_buffer = texture.read_to_pixel_buffer(); + let image_data: RGBAImageData = pixel_buffer.read_as_texture_2d().unwrap(); + + let pixels = { + let mut v = Vec::with_capacity(image_data.data.len() * 4); + for (a, b, c, d) in image_data.data { + v.push(a); + v.push(b); + v.push(c); + v.push(d); + } + v + }; + + + // Create ImageBuffer + let image_buffer = + image::ImageBuffer::from_raw(image_data.width, image_data.height, pixels) + .unwrap(); + + // Save the screenshot to file + let image = image::DynamicImage::ImageRgba8(image_buffer).flipv(); + + image + + } } impl<'a> Renderer<'a, Display> { @@ -214,4 +294,9 @@ impl<'a> Renderer<'a, Display> { self.drawer.gl_window().hide(); } + /// Returns a reference to the gl window. + pub fn gl_window(&self) -> Ref { + self.drawer.gl_window() + } + }