1use glam::{Mat4, Vec3};
2use crate::scene::Scene;
3use crate::aabb::Aabb;
4
5#[derive(Debug, Clone)]
6pub struct OrbitCamera {
7 pub target: Vec3,
9 pub yaw: f32,
11 pub pitch: f32,
13 pub distance: f32,
15 pub fov_y: f32,
17 pub near: f32,
19 pub far: f32,
21 pub aspect_ratio: f32,
23}
24
25impl OrbitCamera {
26 pub fn new(target: Vec3, distance: f32) -> Self {
27 Self {
28 target,
29 yaw: -0.785, pitch: 0.615, distance,
32 fov_y: std::f32::consts::FRAC_PI_4, near: 0.01,
34 far: 1000.0,
35 aspect_ratio: 16.0 / 9.0,
36 }
37 }
38
39 pub fn looking_at(target: Vec3, distance: f32) -> Self {
41 Self {
42 target,
43 yaw: 0.0,
44 pitch: 0.0,
45 distance,
46 ..Default::default()
47 }
48 }
49
50 pub fn position(&self) -> Vec3 {
52 let cos_pitch = self.pitch.cos();
53 let offset = Vec3::new(
54 self.distance * cos_pitch * self.yaw.sin(),
55 self.distance * self.pitch.sin(),
56 self.distance * cos_pitch * self.yaw.cos(),
57 );
58 self.target + offset
59 }
60
61 pub fn right(&self) -> Vec3 {
63 let forward = (self.target - self.position()).normalize();
64 forward.cross(Vec3::Y).normalize()
65 }
66
67 pub fn up(&self) -> Vec3 {
69 let forward = (self.target - self.position()).normalize();
70 let right = forward.cross(Vec3::Y).normalize();
71 right.cross(forward).normalize()
72 }
73
74 pub fn view_matrix(&self) -> Mat4 {
76 Mat4::look_at_rh(self.position(), self.target, Vec3::Y)
77 }
78
79 pub fn projection_matrix(&self) -> Mat4 {
81 Mat4::perspective_rh(self.fov_y, self.aspect_ratio, self.near, self.far)
82 }
83
84 pub fn view_projection(&self) -> Mat4 {
86 self.projection_matrix() * self.view_matrix()
87 }
88
89 pub fn orbit(&mut self, delta_yaw: f32, delta_pitch: f32) {
91 self.yaw += delta_yaw;
92 self.pitch = (self.pitch + delta_pitch).clamp(
93 -std::f32::consts::FRAC_PI_2 + 0.01,
94 std::f32::consts::FRAC_PI_2 - 0.01,
95 );
96 }
97
98 pub fn pan(&mut self, delta_x: f32, delta_y: f32) {
100 let right = self.right();
101 let up = self.up();
102 self.target += right * delta_x + up * delta_y;
103 }
104
105 pub fn zoom(&mut self, delta: f32) {
107 self.distance *= 1.0 - delta;
108 self.distance = self.distance.clamp(0.1, 500.0);
109 }
110
111 pub fn fit_to_aabb(&mut self, aabb: Aabb) {
113 if !aabb.is_valid() {
114 return;
115 }
116 self.target = aabb.center();
117 let radius = aabb.diagonal() * 0.5;
118 let min_dist = (radius / (self.fov_y * 0.5).sin()).max(0.5);
119 self.distance = min_dist * 1.4; self.near = (min_dist - radius * 2.0).max(0.01);
121 self.far = (min_dist + radius * 4.0).max(10.0);
122 }
123
124 pub fn fit_to_scene(&mut self, scene: &Scene) {
126 self.fit_to_aabb(scene.compute_aabb());
127 }
128
129 pub fn view_front(&mut self) {
131 self.yaw = 0.0;
132 self.pitch = 0.0;
133 }
134
135 pub fn view_top(&mut self) {
137 self.yaw = 0.0;
138 self.pitch = std::f32::consts::FRAC_PI_2 - 0.01;
139 }
140
141 pub fn view_right(&mut self) {
143 self.yaw = -std::f32::consts::FRAC_PI_2;
144 self.pitch = 0.0;
145 }
146
147 pub fn view_iso(&mut self) {
149 self.yaw = -0.785;
150 self.pitch = 0.615;
151 }
152}
153
154impl Default for OrbitCamera {
155 fn default() -> Self {
156 Self::new(Vec3::ZERO, 5.0)
157 }
158}