use nalgebra_glm::{Vec2, Vec3};
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
pub enum PanOrbitButton {
#[default]
Left,
Right,
Middle,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum PanOrbitModifier {
Shift,
Control,
Alt,
}
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct PanOrbitCamera {
pub focus: Vec3,
pub radius: f32,
pub yaw: f32,
pub pitch: f32,
pub target_focus: Vec3,
pub target_radius: f32,
pub target_yaw: f32,
pub target_pitch: f32,
pub pitch_upper_limit: f32,
pub pitch_lower_limit: f32,
pub zoom_upper_limit: Option<f32>,
pub zoom_lower_limit: f32,
pub button_orbit: PanOrbitButton,
pub button_pan: PanOrbitButton,
pub modifier_orbit: Option<PanOrbitModifier>,
pub modifier_pan: Option<PanOrbitModifier>,
pub orbit_sensitivity: f32,
pub pan_sensitivity: f32,
pub zoom_sensitivity: f32,
pub orbit_smoothness: f32,
pub pan_smoothness: f32,
pub zoom_smoothness: f32,
pub gamepad_orbit_sensitivity: f32,
pub gamepad_pan_sensitivity: f32,
pub gamepad_zoom_sensitivity: f32,
pub gamepad_deadzone: f32,
pub gamepad_smoothness: f32,
pub smoothed_gamepad_orbit: Vec2,
pub smoothed_gamepad_pan: Vec2,
pub enabled: bool,
pub is_upside_down: bool,
pub allow_upside_down: bool,
}
impl Default for PanOrbitCamera {
fn default() -> Self {
Self {
focus: Vec3::zeros(),
radius: 10.0,
yaw: 0.0,
pitch: 0.0,
target_focus: Vec3::zeros(),
target_radius: 10.0,
target_yaw: 0.0,
target_pitch: 0.0,
pitch_upper_limit: std::f32::consts::FRAC_PI_2 - 0.01,
pitch_lower_limit: -(std::f32::consts::FRAC_PI_2 - 0.01),
zoom_upper_limit: None,
zoom_lower_limit: 0.05,
button_orbit: PanOrbitButton::Middle,
button_pan: PanOrbitButton::Middle,
modifier_orbit: None,
modifier_pan: Some(PanOrbitModifier::Shift),
orbit_sensitivity: 1.0,
pan_sensitivity: 1.0,
zoom_sensitivity: 1.0,
orbit_smoothness: 0.1,
pan_smoothness: 0.02,
zoom_smoothness: 0.1,
gamepad_orbit_sensitivity: 2.0,
gamepad_pan_sensitivity: 10.0,
gamepad_zoom_sensitivity: 5.0,
gamepad_deadzone: 0.15,
gamepad_smoothness: 0.06,
smoothed_gamepad_orbit: Vec2::zeros(),
smoothed_gamepad_pan: Vec2::zeros(),
enabled: true,
is_upside_down: false,
allow_upside_down: false,
}
}
}
impl PanOrbitCamera {
pub fn new(focus: Vec3, radius: f32) -> Self {
Self {
focus,
radius,
target_focus: focus,
target_radius: radius,
..Default::default()
}
}
pub fn with_yaw_pitch(mut self, yaw: f32, pitch: f32) -> Self {
self.yaw = yaw;
self.pitch = pitch;
self.target_yaw = yaw;
self.target_pitch = pitch;
self
}
pub fn with_zoom_limits(mut self, min: f32, max: Option<f32>) -> Self {
self.zoom_lower_limit = min;
self.zoom_upper_limit = max;
self
}
pub fn with_pitch_limits(mut self, min: f32, max: f32) -> Self {
self.pitch_lower_limit = min;
self.pitch_upper_limit = max;
self
}
pub fn with_smoothness(mut self, orbit: f32, pan: f32, zoom: f32) -> Self {
self.orbit_smoothness = orbit;
self.pan_smoothness = pan;
self.zoom_smoothness = zoom;
self
}
pub fn with_buttons(mut self, orbit: PanOrbitButton, pan: PanOrbitButton) -> Self {
self.button_orbit = orbit;
self.button_pan = pan;
self
}
pub fn with_modifiers(
mut self,
orbit: Option<PanOrbitModifier>,
pan: Option<PanOrbitModifier>,
) -> Self {
self.modifier_orbit = orbit;
self.modifier_pan = pan;
self
}
pub fn with_upside_down(mut self, allow: bool) -> Self {
self.allow_upside_down = allow;
self
}
pub fn compute_camera_transform(&self) -> (Vec3, nalgebra_glm::Quat) {
compute_pan_orbit_transform(self.focus, self.yaw, self.pitch, self.radius)
}
pub fn compute_target_camera_transform(&self) -> (Vec3, nalgebra_glm::Quat) {
compute_pan_orbit_transform(
self.target_focus,
self.target_yaw,
self.target_pitch,
self.target_radius,
)
}
}
pub fn compute_pan_orbit_transform(
focus: Vec3,
yaw: f32,
pitch: f32,
radius: f32,
) -> (Vec3, nalgebra_glm::Quat) {
let yaw_quat = nalgebra_glm::quat_angle_axis(yaw, &Vec3::y());
let pitch_quat = nalgebra_glm::quat_angle_axis(-pitch, &Vec3::x());
let rotation = yaw_quat * pitch_quat;
let position = focus + nalgebra_glm::quat_rotate_vec3(&rotation, &Vec3::new(0.0, 0.0, radius));
(position, rotation)
}