use super::Controls;
use glam::{Mat4, Vec3};
#[derive(Clone, Debug, Default)]
pub struct OrbitInputs {
pub rotate_x: f32,
pub rotate_y: f32,
pub zoom: f32,
}
#[derive(Clone, Debug)]
pub struct OrbitSettings {
pub sensitivity: f32,
pub zoom_speed: f32,
pub min_polar: f32,
pub max_polar: f32,
pub min_radius: f32,
pub max_radius: f32,
}
impl Default for OrbitSettings {
fn default() -> Self {
Self {
sensitivity: 1.0,
zoom_speed: 1.0,
min_polar: (0.0_f32).to_radians(),
max_polar: (89.0_f32).to_radians(),
min_radius: 1.0,
max_radius: 10.0,
}
}
}
#[derive(Clone, Debug)]
pub struct OrbitControls {
inputs: OrbitInputs,
settings: OrbitSettings,
center: Vec3,
azimuth: f32,
polar: f32,
radius: f32,
view: Mat4,
eye: Vec3,
changed: bool,
}
impl OrbitControls {
pub fn azimuth(&self) -> f32 {
self.azimuth
}
pub fn center(&self) -> [f32; 3] {
self.center.into()
}
pub fn inputs(&self) -> &OrbitInputs {
&self.inputs
}
pub fn inputs_mut(&mut self) -> &mut OrbitInputs {
self.changed = true;
&mut self.inputs
}
pub fn settings(&self) -> &OrbitSettings {
&self.settings
}
pub fn settings_mut(&mut self) -> &mut OrbitSettings {
self.changed = true;
&mut self.settings
}
pub fn new(settings: OrbitSettings, center: [f32; 3], radius: f32) -> Self {
let radius = radius.clamp(settings.min_radius, settings.max_radius);
Self {
inputs: OrbitInputs::default(),
settings,
center: center.into(),
azimuth: 0.0,
polar: 0.0,
radius,
view: Mat4::IDENTITY,
eye: Vec3::ZERO,
changed: true,
}
}
pub fn polar(&self) -> f32 {
self.polar
}
pub fn radius(&self) -> f32 {
self.radius
}
pub fn set_aizmuth(&mut self, azimuth: f32) {
self.azimuth = azimuth.rem_euclid(2.0 * std::f32::consts::PI);
self.changed = true;
}
pub fn set_center(&mut self, center: [f32; 3]) {
self.center = center.into();
self.changed = true;
}
pub fn set_inputs(&mut self, inputs: OrbitInputs) {
self.inputs = inputs;
self.changed = true;
}
pub fn set_polar(&mut self, polar: f32) {
self.polar =
polar.clamp(self.settings.min_polar, self.settings.max_polar);
self.changed = true;
}
pub fn set_radius(&mut self, radius: f32) {
self.radius =
radius.clamp(self.settings.min_radius, self.settings.max_radius);
self.changed = true;
}
}
impl Controls for OrbitControls {
fn eye(&self) -> [f32; 3] {
self.eye.into()
}
fn update(&mut self, delta: f32) -> bool {
let mut changed = self.changed;
if self.inputs.rotate_x != 0.0 || self.inputs.rotate_y != 0.0 {
self.azimuth -=
self.settings.sensitivity * delta * self.inputs.rotate_x;
self.polar +=
self.settings.sensitivity * delta * self.inputs.rotate_y;
self.polar = self
.polar
.clamp(self.settings.min_polar, self.settings.max_polar);
self.azimuth = self.azimuth.rem_euclid(2.0 * std::f32::consts::PI);
self.inputs.rotate_x = 0.0;
self.inputs.rotate_y = 0.0;
changed = true;
}
if self.inputs.zoom != 0.0 {
self.radius -= self.settings.zoom_speed * self.inputs.zoom;
self.radius = self
.radius
.clamp(self.settings.min_radius, self.settings.max_radius);
self.inputs.zoom = 0.0;
changed = true;
}
if changed {
let (azimuth_sin, azimuth_cos) = self.azimuth.sin_cos();
let (polar_sin, polar_cos) = self.polar.sin_cos();
let x = self.center.x + self.radius * polar_cos * azimuth_cos;
let y = self.center.y + self.radius * polar_sin;
let z = self.center.z + self.radius * polar_cos * azimuth_sin;
self.eye = Vec3::new(x, y, z);
let forward = (self.eye - self.center).normalize();
let sideward = Vec3::Y.cross(forward).normalize();
let upward = forward.cross(sideward).normalize();
self.view = Mat4::look_at_rh(self.eye, self.center, upward);
}
self.changed = false;
changed
}
fn view(&self) -> Mat4 {
self.view
}
}