use crate::Camera;
use super::action::Action;
use super::action_frame::ActionFrame;
use super::context::ViewportContext;
use super::event::ViewportEvent;
use super::mode::NavigationMode;
use super::preset::{BindingPreset, viewport_all_bindings, viewport_primitives_bindings};
use super::viewport_input::ViewportInput;
pub struct OrbitCameraController {
input: ViewportInput,
pub navigation_mode: NavigationMode,
pub fly_speed: f32,
pub orbit_sensitivity: f32,
pub zoom_sensitivity: f32,
pub gesture_sensitivity: f32,
viewport_size: [f32; 2],
}
impl OrbitCameraController {
pub const DEFAULT_ORBIT_SENSITIVITY: f32 = 0.005;
pub const DEFAULT_ZOOM_SENSITIVITY: f32 = 0.001;
pub const DEFAULT_GESTURE_SENSITIVITY: f32 = 1.0;
pub const DEFAULT_FLY_SPEED: f32 = 0.1;
pub fn new(preset: BindingPreset) -> Self {
let bindings = match preset {
BindingPreset::ViewportPrimitives => viewport_primitives_bindings(),
BindingPreset::ViewportAll => viewport_all_bindings(),
};
Self {
input: ViewportInput::new(bindings),
navigation_mode: NavigationMode::Arcball,
fly_speed: Self::DEFAULT_FLY_SPEED,
orbit_sensitivity: Self::DEFAULT_ORBIT_SENSITIVITY,
zoom_sensitivity: Self::DEFAULT_ZOOM_SENSITIVITY,
gesture_sensitivity: Self::DEFAULT_GESTURE_SENSITIVITY,
viewport_size: [1.0, 1.0],
}
}
pub fn viewport_primitives() -> Self {
Self::new(BindingPreset::ViewportPrimitives)
}
pub fn viewport_all() -> Self {
Self::new(BindingPreset::ViewportAll)
}
pub fn begin_frame(&mut self, ctx: ViewportContext) {
self.viewport_size = ctx.viewport_size;
self.input.begin_frame(ctx);
}
pub fn push_event(&mut self, event: ViewportEvent) {
self.input.push_event(event);
}
pub fn resolve(&self) -> ActionFrame {
self.input.resolve()
}
pub fn apply_to_camera(&mut self, camera: &mut Camera) -> ActionFrame {
let frame = self.input.resolve();
let nav = &frame.navigation;
let h = self.viewport_size[1];
match self.navigation_mode {
NavigationMode::Arcball => {
if nav.orbit != glam::Vec2::ZERO {
camera.orbit(
nav.orbit.x * self.orbit_sensitivity,
nav.orbit.y * self.orbit_sensitivity,
);
}
if nav.twist != 0.0 && self.gesture_sensitivity != 0.0 {
camera.orbit(nav.twist * self.gesture_sensitivity, 0.0);
}
if nav.pan != glam::Vec2::ZERO {
camera.pan_pixels(nav.pan, h);
}
if nav.zoom != 0.0 {
camera.zoom_by_factor(1.0 - nav.zoom * self.zoom_sensitivity);
}
}
NavigationMode::Turntable => {
if nav.orbit != glam::Vec2::ZERO {
let yaw = nav.orbit.x * self.orbit_sensitivity;
let pitch = nav.orbit.y * self.orbit_sensitivity;
apply_turntable(camera, yaw, pitch);
}
if nav.twist != 0.0 && self.gesture_sensitivity != 0.0 {
apply_turntable(camera, nav.twist * self.gesture_sensitivity, 0.0);
}
if nav.pan != glam::Vec2::ZERO {
camera.pan_pixels(nav.pan, h);
}
if nav.zoom != 0.0 {
camera.zoom_by_factor(1.0 - nav.zoom * self.zoom_sensitivity);
}
}
NavigationMode::Planar => {
if nav.pan != glam::Vec2::ZERO {
camera.pan_pixels(nav.pan, h);
}
if nav.zoom != 0.0 {
camera.zoom_by_factor(1.0 - nav.zoom * self.zoom_sensitivity);
}
}
NavigationMode::FirstPerson => {
if nav.orbit != glam::Vec2::ZERO {
let yaw = nav.orbit.x * self.orbit_sensitivity;
let pitch = nav.orbit.y * self.orbit_sensitivity;
apply_firstperson_look(camera, yaw, pitch);
}
if nav.twist != 0.0 && self.gesture_sensitivity != 0.0 {
apply_firstperson_look(camera, nav.twist * self.gesture_sensitivity, 0.0);
}
let forward = -(camera.orientation * glam::Vec3::Z);
let right = camera.orientation * glam::Vec3::X;
let up = camera.orientation * glam::Vec3::Y;
let speed = self.fly_speed;
let mut move_delta = glam::Vec3::ZERO;
if frame.is_active(Action::FlyForward) {
move_delta += forward * speed;
}
if frame.is_active(Action::FlyBackward) {
move_delta -= forward * speed;
}
if frame.is_active(Action::FlyRight) {
move_delta += right * speed;
}
if frame.is_active(Action::FlyLeft) {
move_delta -= right * speed;
}
if frame.is_active(Action::FlyUp) {
move_delta += up * speed;
}
if frame.is_active(Action::FlyDown) {
move_delta -= up * speed;
}
camera.center += move_delta;
}
}
frame
}
}
fn apply_turntable(camera: &mut Camera, yaw: f32, pitch: f32) {
if yaw != 0.0 {
camera.orientation = (glam::Quat::from_rotation_z(-yaw) * camera.orientation).normalize();
}
if pitch != 0.0 {
let proposed = (camera.orientation * glam::Quat::from_rotation_x(-pitch)).normalize();
let max_sin_el = 89.0_f32.to_radians().sin(); let eye_z = (proposed * glam::Vec3::Z).z;
if eye_z.abs() <= max_sin_el {
camera.orientation = proposed;
}
}
}
fn apply_firstperson_look(camera: &mut Camera, yaw: f32, pitch: f32) {
let eye = camera.eye_position();
camera.orientation = (glam::Quat::from_rotation_z(-yaw)
* camera.orientation
* glam::Quat::from_rotation_x(-pitch))
.normalize();
camera.center = eye - camera.orientation * (glam::Vec3::Z * camera.distance);
}