mirror of https://github.com/Leinnan/doppler.git
Camera
This commit is contained in:
parent
0c53ca90e0
commit
7ce2f9830a
|
|
@ -0,0 +1,141 @@
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use cgmath;
|
||||||
|
use cgmath::prelude::*;
|
||||||
|
use cgmath::vec3;
|
||||||
|
|
||||||
|
type Point3 = cgmath::Point3<f32>;
|
||||||
|
type Vector3 = cgmath::Vector3<f32>;
|
||||||
|
type Matrix4 = cgmath::Matrix4<f32>;
|
||||||
|
|
||||||
|
// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
|
||||||
|
#[derive(PartialEq, Clone, Copy)]
|
||||||
|
pub enum Camera_Movement {
|
||||||
|
FORWARD,
|
||||||
|
BACKWARD,
|
||||||
|
LEFT,
|
||||||
|
RIGHT,
|
||||||
|
}
|
||||||
|
use self::Camera_Movement::*;
|
||||||
|
|
||||||
|
// Default camera values
|
||||||
|
const YAW: f32 = -90.0;
|
||||||
|
const PITCH: f32 = 0.0;
|
||||||
|
const SPEED: f32 = 2.5;
|
||||||
|
const SENSITIVTY: f32 = 0.1;
|
||||||
|
const ZOOM: f32 = 45.0;
|
||||||
|
|
||||||
|
pub struct Camera {
|
||||||
|
// Camera Attributes
|
||||||
|
pub Position: Point3,
|
||||||
|
pub Front: Vector3,
|
||||||
|
pub Up: Vector3,
|
||||||
|
pub Right: Vector3,
|
||||||
|
pub WorldUp: Vector3,
|
||||||
|
// Euler Angles
|
||||||
|
pub Yaw: f32,
|
||||||
|
pub Pitch: f32,
|
||||||
|
// Camera options
|
||||||
|
pub MovementSpeed: f32,
|
||||||
|
pub MouseSensitivity: f32,
|
||||||
|
pub Zoom: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Camera {
|
||||||
|
fn default() -> Camera {
|
||||||
|
let mut camera = Camera {
|
||||||
|
Position: Point3::new(0.0, 0.0, 0.0),
|
||||||
|
Front: vec3(0.0, 0.0, -1.0),
|
||||||
|
Up: Vector3::zero(), // initialized later
|
||||||
|
Right: Vector3::zero(), // initialized later
|
||||||
|
WorldUp: Vector3::unit_y(),
|
||||||
|
Yaw: YAW,
|
||||||
|
Pitch: PITCH,
|
||||||
|
MovementSpeed: SPEED,
|
||||||
|
MouseSensitivity: SENSITIVTY,
|
||||||
|
Zoom: ZOOM,
|
||||||
|
};
|
||||||
|
camera.update_camera_vectors();
|
||||||
|
camera
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Camera {
|
||||||
|
/// Returns the view matrix calculated using Eular Angles and the LookAt Matrix
|
||||||
|
pub fn get_view_matrix(&self) -> Matrix4 {
|
||||||
|
Matrix4::look_at(self.Position, self.Position + self.Front, self.Up)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
|
||||||
|
pub fn process_keyboard(&mut self, direction: Camera_Movement, deltaTime: f32) {
|
||||||
|
let velocity = self.MovementSpeed * deltaTime;
|
||||||
|
if direction == FORWARD {
|
||||||
|
self.Position += self.Front * velocity;
|
||||||
|
}
|
||||||
|
if direction == BACKWARD {
|
||||||
|
self.Position += -(self.Front * velocity);
|
||||||
|
}
|
||||||
|
if direction == LEFT {
|
||||||
|
self.Position += -(self.Right * velocity);
|
||||||
|
}
|
||||||
|
if direction == RIGHT {
|
||||||
|
self.Position += self.Right * velocity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Processes input received from a mouse input system. Expects the offset value in both the x and y direction.
|
||||||
|
pub fn process_mouse_movement(
|
||||||
|
&mut self,
|
||||||
|
mut xoffset: f32,
|
||||||
|
mut yoffset: f32,
|
||||||
|
constrainPitch: bool,
|
||||||
|
) {
|
||||||
|
xoffset *= self.MouseSensitivity;
|
||||||
|
yoffset *= self.MouseSensitivity;
|
||||||
|
|
||||||
|
self.Yaw += xoffset;
|
||||||
|
self.Pitch += yoffset;
|
||||||
|
|
||||||
|
// Make sure that when pitch is out of bounds, screen doesn't get flipped
|
||||||
|
if constrainPitch {
|
||||||
|
if self.Pitch > 89.0 {
|
||||||
|
self.Pitch = 89.0;
|
||||||
|
}
|
||||||
|
if self.Pitch < -89.0 {
|
||||||
|
self.Pitch = -89.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Front, Right and Up Vectors using the updated Eular angles
|
||||||
|
self.update_camera_vectors();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
|
||||||
|
pub fn process_mouse_scroll(&mut self, yoffset: f32) {
|
||||||
|
if self.Zoom >= 1.0 && self.Zoom <= 45.0 {
|
||||||
|
self.Zoom -= yoffset;
|
||||||
|
}
|
||||||
|
if self.Zoom <= 1.0 {
|
||||||
|
self.Zoom = 1.0;
|
||||||
|
}
|
||||||
|
if self.Zoom >= 45.0 {
|
||||||
|
self.Zoom = 45.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the front vector from the Camera's (updated) Eular Angles
|
||||||
|
fn update_camera_vectors(&mut self) {
|
||||||
|
// Calculate the new Front vector
|
||||||
|
let front = Vector3 {
|
||||||
|
x: self.Yaw.to_radians().cos() * self.Pitch.to_radians().cos(),
|
||||||
|
y: self.Pitch.to_radians().sin(),
|
||||||
|
z: self.Yaw.to_radians().sin() * self.Pitch.to_radians().cos(),
|
||||||
|
};
|
||||||
|
self.Front = front.normalize();
|
||||||
|
// Also re-calculate the Right and Up vector
|
||||||
|
self.Right = self.Front.cross(self.WorldUp).normalize(); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
|
||||||
|
self.Up = self.Right.cross(self.Front).normalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
116
src/main.rs
116
src/main.rs
|
|
@ -2,6 +2,7 @@ extern crate gl;
|
||||||
extern crate glfw;
|
extern crate glfw;
|
||||||
extern crate image;
|
extern crate image;
|
||||||
|
|
||||||
|
use self::camera::*;
|
||||||
use self::gl::types::*;
|
use self::gl::types::*;
|
||||||
use self::glfw::{Action, Context, Key};
|
use self::glfw::{Action, Context, Key};
|
||||||
use cgmath::prelude::*;
|
use cgmath::prelude::*;
|
||||||
|
|
@ -13,6 +14,7 @@ use std::mem;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
|
mod camera;
|
||||||
mod consts;
|
mod consts;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod shader;
|
mod shader;
|
||||||
|
|
@ -32,6 +34,18 @@ const CUBES_POS: [Vector3<f32>; 10] = [
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
setup_panic!();
|
setup_panic!();
|
||||||
|
let mut camera = Camera {
|
||||||
|
Position: Point3::new(0.0, 0.0, 3.0),
|
||||||
|
..Camera::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut first_mouse = true;
|
||||||
|
let mut last_x: f32 = consts::SCR_WIDTH as f32 / 2.0;
|
||||||
|
let mut last_y: f32 = consts::SCR_HEIGHT as f32 / 2.0;
|
||||||
|
|
||||||
|
// timing
|
||||||
|
let mut delta_time: f32; // time between current frame and last frame
|
||||||
|
let mut last_frame: f32 = 0.0;
|
||||||
// glfw: initialize and configure
|
// glfw: initialize and configure
|
||||||
// ------------------------------
|
// ------------------------------
|
||||||
let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
|
let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
|
||||||
|
|
@ -153,16 +167,26 @@ pub fn main() {
|
||||||
let color_r = 0.188;
|
let color_r = 0.188;
|
||||||
let color_g = 0.22;
|
let color_g = 0.22;
|
||||||
let color_b = 0.235;
|
let color_b = 0.235;
|
||||||
let mut view_modifier: f32 = 0.5;
|
|
||||||
let mut view_multiplier = 0.03;
|
|
||||||
while !window.should_close() {
|
while !window.should_close() {
|
||||||
view_modifier = view_modifier + view_multiplier;
|
// per-frame time logic
|
||||||
if view_modifier > 5.0 || view_modifier < 0.0 {
|
// --------------------
|
||||||
view_multiplier = -view_multiplier;
|
let cur_frame = glfw.get_time() as f32;
|
||||||
}
|
delta_time = cur_frame - last_frame;
|
||||||
|
last_frame = cur_frame;
|
||||||
|
|
||||||
// events
|
// events
|
||||||
// -----
|
// -----
|
||||||
process_events(&mut window, &events);
|
process_events(
|
||||||
|
&events,
|
||||||
|
&mut first_mouse,
|
||||||
|
&mut last_x,
|
||||||
|
&mut last_y,
|
||||||
|
&mut camera,
|
||||||
|
);
|
||||||
|
|
||||||
|
// input
|
||||||
|
// -----
|
||||||
|
process_input(&mut window, delta_time, &mut camera);
|
||||||
|
|
||||||
// render
|
// render
|
||||||
// ------
|
// ------
|
||||||
|
|
@ -173,34 +197,19 @@ pub fn main() {
|
||||||
gl::BindTexture(gl::TEXTURE_2D, texture);
|
gl::BindTexture(gl::TEXTURE_2D, texture);
|
||||||
shader_object.useProgram();
|
shader_object.useProgram();
|
||||||
|
|
||||||
// create transformations
|
// pass projection matrix to shader (note that in this case it could change every frame)
|
||||||
// NOTE: cgmath requires axis vectors to be normalized!
|
|
||||||
let model: Matrix4<f32> = Matrix4::from_axis_angle(
|
|
||||||
vec3(0.5, 1.0, 0.0).normalize(),
|
|
||||||
Rad(glfw.get_time() as f32),
|
|
||||||
);
|
|
||||||
// camera/view transformation
|
|
||||||
let radius: f32 = 10.0;
|
|
||||||
let time = glfw.get_time() as f32;
|
|
||||||
let cam_pos = Point3::new(time.sin() * radius, view_modifier, time.cos() * radius);
|
|
||||||
let view: Matrix4<f32> =
|
|
||||||
Matrix4::look_at(cam_pos, Point3::new(0.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
|
|
||||||
|
|
||||||
let projection: Matrix4<f32> = perspective(
|
let projection: Matrix4<f32> = perspective(
|
||||||
Deg(45.0),
|
Deg(camera.Zoom),
|
||||||
consts::SCR_WIDTH as f32 / consts::SCR_HEIGHT as f32,
|
consts::SCR_WIDTH as f32 / consts::SCR_HEIGHT as f32,
|
||||||
0.1,
|
0.1,
|
||||||
100.0,
|
100.0,
|
||||||
);
|
);
|
||||||
// retrieve the matrix uniform locations
|
|
||||||
let model_loc = gl::GetUniformLocation(shader_object.ID, c_str!("model").as_ptr());
|
|
||||||
let view_loc = gl::GetUniformLocation(shader_object.ID, c_str!("view").as_ptr());
|
|
||||||
// pass them to the shaders (3 different ways)
|
|
||||||
gl::UniformMatrix4fv(model_loc, 1, gl::FALSE, model.as_ptr());
|
|
||||||
gl::UniformMatrix4fv(view_loc, 1, gl::FALSE, &view[0][0]);
|
|
||||||
// note: currently we set the projection matrix each frame, but since the projection matrix rarely changes it's often best practice to set it outside the main loop only once.
|
|
||||||
shader_object.setMat4(c_str!("projection"), &projection);
|
shader_object.setMat4(c_str!("projection"), &projection);
|
||||||
|
|
||||||
|
// camera/view transformation
|
||||||
|
let view = camera.get_view_matrix();
|
||||||
|
shader_object.setMat4(c_str!("view"), &view);
|
||||||
|
|
||||||
gl::BindVertexArray(vao);
|
gl::BindVertexArray(vao);
|
||||||
for (i, position) in CUBES_POS.iter().enumerate() {
|
for (i, position) in CUBES_POS.iter().enumerate() {
|
||||||
// calculate the model matrix for each object and pass it to shader before drawing
|
// calculate the model matrix for each object and pass it to shader before drawing
|
||||||
|
|
@ -226,8 +235,13 @@ pub fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: not the same version as in common.rs!
|
pub fn process_events(
|
||||||
fn process_events(window: &mut glfw::Window, events: &Receiver<(f64, glfw::WindowEvent)>) {
|
events: &Receiver<(f64, glfw::WindowEvent)>,
|
||||||
|
first_mouse: &mut bool,
|
||||||
|
last_x: &mut f32,
|
||||||
|
last_y: &mut f32,
|
||||||
|
camera: &mut Camera,
|
||||||
|
) {
|
||||||
for (_, event) in glfw::flush_messages(events) {
|
for (_, event) in glfw::flush_messages(events) {
|
||||||
match event {
|
match event {
|
||||||
glfw::WindowEvent::FramebufferSize(width, height) => {
|
glfw::WindowEvent::FramebufferSize(width, height) => {
|
||||||
|
|
@ -235,11 +249,47 @@ fn process_events(window: &mut glfw::Window, events: &Receiver<(f64, glfw::Windo
|
||||||
// height will be significantly larger than specified on retina displays.
|
// height will be significantly larger than specified on retina displays.
|
||||||
unsafe { gl::Viewport(0, 0, width, height) }
|
unsafe { gl::Viewport(0, 0, width, height) }
|
||||||
}
|
}
|
||||||
glfw::WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
|
glfw::WindowEvent::CursorPos(xpos, ypos) => {
|
||||||
window.set_should_close(true)
|
let (xpos, ypos) = (xpos as f32, ypos as f32);
|
||||||
|
if *first_mouse {
|
||||||
|
*last_x = xpos;
|
||||||
|
*last_y = ypos;
|
||||||
|
*first_mouse = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let xoffset = xpos - *last_x;
|
||||||
|
let yoffset = *last_y - ypos; // reversed since y-coordinates go from bottom to top
|
||||||
|
|
||||||
|
*last_x = xpos;
|
||||||
|
*last_y = ypos;
|
||||||
|
|
||||||
|
camera.process_mouse_movement(xoffset, yoffset, true);
|
||||||
|
}
|
||||||
|
glfw::WindowEvent::Scroll(_xoffset, yoffset) => {
|
||||||
|
camera.process_mouse_scroll(yoffset as f32);
|
||||||
}
|
}
|
||||||
glfw::WindowEvent::Key(_, _, Action::Press, _) => println!("Other key pressed"),
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Input processing function as introduced in 1.7.4 (Camera Class) and used in
|
||||||
|
/// most later tutorials
|
||||||
|
pub fn process_input(window: &mut glfw::Window, delta_time: f32, camera: &mut Camera) {
|
||||||
|
if window.get_key(Key::Escape) == Action::Press {
|
||||||
|
window.set_should_close(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if window.get_key(Key::W) == Action::Press {
|
||||||
|
camera.process_keyboard(Camera_Movement::FORWARD, delta_time);
|
||||||
|
}
|
||||||
|
if window.get_key(Key::S) == Action::Press {
|
||||||
|
camera.process_keyboard(Camera_Movement::BACKWARD, delta_time);
|
||||||
|
}
|
||||||
|
if window.get_key(Key::A) == Action::Press {
|
||||||
|
camera.process_keyboard(Camera_Movement::LEFT, delta_time);
|
||||||
|
}
|
||||||
|
if window.get_key(Key::D) == Action::Press {
|
||||||
|
camera.process_keyboard(Camera_Movement::RIGHT, delta_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue