1use glam::{Mat4, Vec3};
2use crate::aabb::Aabb;
3
4#[derive(Debug, Clone)]
5pub struct OrbitCamera {
6 pub target: Vec3,
8 pub yaw: f32,
10 pub pitch: f32,
12 pub distance: f32,
14 pub fov_y: f32,
16 pub near: f32,
18 pub far: f32,
20 pub aspect_ratio: f32,
22}
23
24impl OrbitCamera {
25 pub fn new(target: Vec3, distance: f32) -> Self {
26 Self {
27 target,
28 yaw: -0.785, pitch: 0.615, distance,
31 fov_y: std::f32::consts::FRAC_PI_4, near: 0.01,
33 far: 1000.0,
34 aspect_ratio: 16.0 / 9.0,
35 }
36 }
37
38 pub fn position(&self) -> Vec3 {
40 let cos_pitch = self.pitch.cos();
41 let offset = Vec3::new(
42 self.distance * cos_pitch * self.yaw.sin(),
43 self.distance * self.pitch.sin(),
44 self.distance * cos_pitch * self.yaw.cos(),
45 );
46 self.target + offset
47 }
48
49 pub fn right(&self) -> Vec3 {
51 let forward = (self.target - self.position()).normalize();
52 forward.cross(Vec3::Y).normalize()
53 }
54
55 pub fn up(&self) -> Vec3 {
57 let forward = (self.target - self.position()).normalize();
58 let right = forward.cross(Vec3::Y).normalize();
59 right.cross(forward).normalize()
60 }
61
62 pub fn view_matrix(&self) -> Mat4 {
64 Mat4::look_at_rh(self.position(), self.target, Vec3::Y)
65 }
66
67 pub fn projection_matrix(&self) -> Mat4 {
69 Mat4::perspective_rh(self.fov_y, self.aspect_ratio, self.near, self.far)
70 }
71
72 pub fn view_projection(&self) -> Mat4 {
74 self.projection_matrix() * self.view_matrix()
75 }
76
77 pub fn orbit(&mut self, delta_yaw: f32, delta_pitch: f32) {
79 self.yaw += delta_yaw;
80 self.pitch = (self.pitch + delta_pitch).clamp(
81 -std::f32::consts::FRAC_PI_2 + 0.01,
82 std::f32::consts::FRAC_PI_2 - 0.01,
83 );
84 }
85
86 pub fn pan(&mut self, delta_x: f32, delta_y: f32) {
88 let right = self.right();
89 let up = self.up();
90 self.target += right * delta_x + up * delta_y;
91 }
92
93 pub fn zoom(&mut self, delta: f32) {
95 self.distance *= 1.0 - delta;
96 self.distance = self.distance.clamp(0.1, 500.0);
97 }
98
99 pub fn fit_to_aabb(&mut self, aabb: Aabb) {
101 if !aabb.is_valid() {
102 return;
103 }
104 self.target = aabb.center();
105 let radius = aabb.diagonal() * 0.5;
106 self.distance = (radius / (self.fov_y * 0.5).sin()).max(0.5);
107 }
108
109 pub fn view_front(&mut self) {
111 self.yaw = 0.0;
112 self.pitch = 0.0;
113 }
114
115 pub fn view_top(&mut self) {
117 self.yaw = 0.0;
118 self.pitch = std::f32::consts::FRAC_PI_2 - 0.01;
119 }
120
121 pub fn view_right(&mut self) {
123 self.yaw = -std::f32::consts::FRAC_PI_2;
124 self.pitch = 0.0;
125 }
126
127 pub fn view_iso(&mut self) {
129 self.yaw = -0.785;
130 self.pitch = 0.615;
131 }
132}
133
134impl Default for OrbitCamera {
135 fn default() -> Self {
136 Self::new(Vec3::ZERO, 5.0)
137 }
138}