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());