Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use generic BaseFloat #3

Merged
merged 1 commit into from
Jan 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ Cargo.lock
*.swp

.DS_Store
.idea
80 changes: 36 additions & 44 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,93 +5,85 @@
extern crate cgmath;

use cgmath::prelude::*;
use cgmath::{Matrix4, Quaternion, Vector2, Vector3};
use cgmath::{BaseFloat, Matrix4, Quaternion, Vector2, Vector3};
use cgmath::num_traits::clamp;

/// The Shoemake Arcball camera.
pub struct ArcballCamera {
look_at: Matrix4<f32>,
translation: Matrix4<f32>,
rotation: Quaternion<f32>,
camera: Matrix4<f32>,
inv_camera: Matrix4<f32>,
motion_speed: f32,
zoom_speed: f32,
inv_screen: [f32; 2],
pub struct ArcballCamera<F> {
look_at: Matrix4<F>,
translation: Matrix4<F>,
rotation: Quaternion<F>,
camera: Matrix4<F>,
inv_camera: Matrix4<F>,
motion_speed: F,
zoom_speed: F,
inv_screen: [F; 2],
}

impl ArcballCamera {
impl<F: BaseFloat> ArcballCamera<F> {
/// Create a new Arcball camera starting from the look at matrix `look_at`. The `motion_speed`
/// sets the speed for panning and `zoom_speed` the speed for zooming the camera. `screen` should be
/// `[screen_width, screen_height]`.
pub fn new(look_at: &Matrix4<f32>, motion_speed: f32, zoom_speed: f32, screen: [f32; 2]) -> ArcballCamera {
pub fn new(look_at: &Matrix4<F>, motion_speed: F, zoom_speed: F, screen: [F; 2]) -> ArcballCamera<F> {
ArcballCamera {
look_at: *look_at,
translation: Transform::one(),
rotation: Quaternion::new(1.0, 0.0, 0.0, 0.0),
rotation: Quaternion::new(F::one(), F::zero(), F::zero(), F::zero()),
camera: *look_at,
inv_camera: look_at.invert().unwrap(),
motion_speed: motion_speed,
zoom_speed: zoom_speed,
inv_screen: [1.0 / screen[0], 1.0 / screen[1]],
motion_speed,
zoom_speed,
inv_screen: [F::one() / screen[0], F::one() / screen[1]],
}
}
/// Get the view matrix computed by the camera.
pub fn get_mat4(&self) -> Matrix4<f32> {
pub fn get_mat4(&self) -> Matrix4<F> {
self.camera
}
/// Rotate the camera, mouse positions should be in pixel coordinates.
///
/// Rotates from the orientation at the previous mouse position specified by `mouse_prev`
/// to the orientation at the current mouse position, `mouse_cur`.
pub fn rotate(&mut self, mouse_prev: Vector2<f32>, mouse_cur: Vector2<f32>) {
let m_cur = Vector2::new(clamp(mouse_cur.x * 2.0 * self.inv_screen[0] - 1.0, -1.0, 1.0),
clamp(1.0 - 2.0 * mouse_cur.y * self.inv_screen[1], -1.0, 1.0));
let m_prev = Vector2::new(clamp(mouse_prev.x * 2.0 * self.inv_screen[0] - 1.0, -1.0, 1.0),
clamp(1.0 - 2.0 * mouse_prev.y * self.inv_screen[1], -1.0, 1.0));
pub fn rotate(&mut self, mouse_prev: Vector2<F>, mouse_cur: Vector2<F>) {
let one = F::one();
let two = F::from(2.0).unwrap();
let m_cur = Vector2::new(clamp(mouse_cur.x * two * self.inv_screen[0] - one, -one, one),
clamp(one - two * mouse_cur.y * self.inv_screen[1], -one, one));
let m_prev = Vector2::new(clamp(mouse_prev.x * two * self.inv_screen[0] - one, -one, one),
clamp(one - two * mouse_prev.y * self.inv_screen[1], -one, one));
let mouse_cur_ball = ArcballCamera::screen_to_arcball(m_cur);
let mouse_prev_ball = ArcballCamera::screen_to_arcball(m_prev);
self.rotation = mouse_cur_ball * mouse_prev_ball * self.rotation;
self.camera = self.translation * self.look_at * Matrix4::from(self.rotation);
self.inv_camera = self.camera.invert().unwrap();
}
/// Zoom the camera in by some amount. Positive values zoom in, negative zoom out.
pub fn zoom(&mut self, amount: f32, elapsed: f32) {
let motion = Vector3::new(0.0, 0.0, amount);
pub fn zoom(&mut self, amount: F, elapsed: F) {
let motion = Vector3::new(F::zero(), F::zero(), amount);
self.translation = Matrix4::from_translation(motion * self.zoom_speed * elapsed) * self.translation;
self.camera = self.translation * self.look_at * Matrix4::from(self.rotation);
self.inv_camera = self.camera.invert().unwrap();
}
/// Pan the camera following the motion of the mouse. The mouse delta should be in pixels.
pub fn pan(&mut self, mouse_delta: Vector2<f32>, elapsed: f32) {
let motion = Vector3::new(mouse_delta.x, mouse_delta.y, 0.0) * self.motion_speed * elapsed;
pub fn pan(&mut self, mouse_delta: Vector2<F>, elapsed: F) {
let motion = mouse_delta.extend(F::zero()) * self.motion_speed * elapsed;
self.translation = Matrix4::from_translation(motion) * self.translation;
self.camera = self.translation * self.look_at * Matrix4::from(self.rotation);
self.inv_camera = self.camera.invert().unwrap();
}
/// Update the screen dimensions, e.g. if the window has resized.
pub fn update_screen(&mut self, width: f32, height: f32) {
self.inv_screen[0] = 1.0 / width;
self.inv_screen[1] = 1.0 / height;
pub fn update_screen(&mut self, width: F, height: F) {
self.inv_screen[0] = F::one() / width;
self.inv_screen[1] = F::one() / height;
}
fn screen_to_arcball(p: Vector2<f32>) -> Quaternion<f32> {
fn screen_to_arcball(p: Vector2<F>) -> Quaternion<F> {
let dist = cgmath::dot(p, p);
// If we're on/in the sphere return the point on it
if dist <= 1.0 {
Quaternion::new(0.0, p.x, p.y, f32::sqrt(1.0 - dist))
if dist <= F::one() {
Quaternion::new(F::zero(), p.x, p.y, F::sqrt(F::one() - dist))
} else {
let unit_p = p.normalize();
Quaternion::new(0.0, unit_p.x, unit_p.y, 0.0)
Quaternion::new(F::zero(), unit_p.x, unit_p.y, F::zero())
}
}
}

fn clamp(x: f32, min: f32, max: f32) -> f32 {
if x < min {
min
} else if x > max {
max
} else {
x
}
}