use super::*;
const ROTATE_SPEED: f32 = 0.01;
const PAN_SPEED: f32 = 0.001;
const ZOOM_SPEED: f32 = 0.1;
const PITCH_LIMIT: Angle<f32> = Angle(90.0_f32.to_radians());
#[derive(Clone, Debug)]
pub struct ArcballCamera {
pub pivot: Vec3f,
pub radius: f32,
pub pitch: Angle<f32>,
pub yaw: Angle<f32>,
pub pitch_axis: Vec3f,
pub yaw_axis: Vec3f,
}
impl ArcballCamera {
pub fn new(position: Vec3f, pivot: Vec3f, ref_up: Vec3f) -> ArcballCamera {
let offset = pivot - position;
let (forward, radius) = offset.norm_len();
let yaw_axis = ref_up.norm();
let pitch_axis = forward.cross(yaw_axis).norm();
let yaw = Angle(0.0); let pitch = Angle(forward.dot(yaw_axis).asin());
ArcballCamera { pivot, radius, yaw, pitch, yaw_axis, pitch_axis }
}
pub fn rotate(&mut self, dx: f32, dy: f32) {
self.yaw += Angle(dx * ROTATE_SPEED);
self.pitch += Angle(dy * ROTATE_SPEED);
self.pitch = self.pitch.clamp(-PITCH_LIMIT, PITCH_LIMIT);
}
pub fn pan(&mut self, dx: f32, dy: f32) {
let rotation = Mat3f::rotation(self.yaw_axis, self.yaw) * Mat3f::rotation(self.pitch_axis, self.pitch);
let right = rotation * self.pitch_axis;
let up = rotation * self.yaw_axis;
let pan_speed = self.radius * PAN_SPEED;
let pan = right * (dx * pan_speed) + up * (dy * pan_speed);
self.pivot += pan;
}
pub fn zoom(&mut self, scale: f32) {
self.radius *= 1.0 - scale * ZOOM_SPEED;
}
pub fn position(&self) -> Vec3f {
self.pivot - self.view_dir() * self.radius
}
pub fn view_dir(&self) -> Vec3f {
let forward = self.yaw_axis.cross(self.pitch_axis).norm();
Mat3f::rotation(self.yaw_axis, self.yaw) * Mat3f::rotation(self.pitch_axis, self.pitch) * forward
}
pub fn view_matrix(&self, hand: Hand) -> Transform3f {
let up = Mat3f::rotation(self.yaw_axis, self.yaw) * Mat3f::rotation(self.pitch_axis, self.pitch) * self.yaw_axis;
Transform3f::look_at(self.position(), self.pivot, up, hand)
}
}