threecrate_visualization/
camera.rs1use nalgebra::{Point3, Vector3, Matrix4, Perspective3};
4use std::f32::consts::PI;
5
6#[derive(Debug, Clone)]
8pub struct Camera {
9 pub position: Point3<f32>,
10 pub target: Point3<f32>,
11 pub up: Vector3<f32>,
12 pub fov: f32,
13 pub fovy_degrees: f32, pub aspect_ratio: f32,
15 pub near: f32,
16 pub far: f32,
17
18 pub radius: f32,
20 pub theta: f32, pub phi: f32, default_position: Point3<f32>,
25 default_target: Point3<f32>,
26 default_up: Vector3<f32>,
27}
28
29impl Camera {
30 pub fn new(
32 position: Point3<f32>,
33 target: Point3<f32>,
34 up: Vector3<f32>,
35 fovy_degrees: f32,
36 aspect_ratio: f32,
37 near: f32,
38 far: f32,
39 ) -> Self {
40 let direction = position - target;
41 let radius = direction.magnitude();
42 let theta = direction.z.atan2(direction.x);
43 let phi = (direction.y / radius).asin();
44 let fov = fovy_degrees.to_radians();
45
46 Self {
47 position,
48 target,
49 up,
50 fov,
51 fovy_degrees,
52 aspect_ratio,
53 near,
54 far,
55 radius,
56 theta,
57 phi,
58 default_position: position,
59 default_target: target,
60 default_up: up,
61 }
62 }
63
64 pub fn view_matrix(&self) -> Matrix4<f32> {
66 Matrix4::look_at_rh(&self.position, &self.target, &self.up)
67 }
68
69 pub fn projection_matrix(&self) -> Matrix4<f32> {
71 let perspective = Perspective3::new(self.aspect_ratio, self.fov, self.near, self.far);
72 perspective.into_inner()
73 }
74
75 pub fn move_forward(&mut self, distance: f32) {
77 self.radius = (self.radius - distance).max(0.1);
78 self.update_position_from_spherical();
79 }
80
81 pub fn orbit(&mut self, delta_theta: f32, delta_phi: f32) {
83 self.theta += delta_theta;
84 self.phi = (self.phi + delta_phi).clamp(-PI/2.0 + 0.1, PI/2.0 - 0.1);
85 self.update_position_from_spherical();
86 }
87
88 pub fn pan(&mut self, delta_x: f32, delta_y: f32) {
90 let forward = (self.target - self.position).normalize();
91 let right = forward.cross(&self.up).normalize();
92 let up = right.cross(&forward).normalize();
93
94 let pan_vector = right * delta_x + up * delta_y;
95 self.position += pan_vector;
96 self.target += pan_vector;
97 }
98
99 pub fn zoom_fov(&mut self, delta: f32) {
101 self.fov = (self.fov + delta).clamp(0.1, PI - 0.1);
102 self.fovy_degrees = self.fov.to_degrees();
103 }
104
105 pub fn zoom(&mut self, delta: f32) {
107 self.radius = (self.radius - delta).max(0.1);
108 self.update_position_from_spherical();
109 }
110
111 pub fn reset(&mut self) {
113 self.position = self.default_position;
114 self.target = self.default_target;
115 self.up = self.default_up;
116
117 let direction = self.position - self.target;
118 self.radius = direction.magnitude();
119 self.theta = direction.z.atan2(direction.x);
120 self.phi = (direction.y / self.radius).asin();
121 }
122
123 fn update_position_from_spherical(&mut self) {
125 let x = self.radius * self.phi.cos() * self.theta.cos();
126 let y = self.radius * self.phi.sin();
127 let z = self.radius * self.phi.cos() * self.theta.sin();
128
129 self.position = self.target + Vector3::new(x, y, z);
130 }
131
132 pub fn reset_to_target(&mut self, target: Point3<f32>, radius: f32) {
134 self.target = target;
135 self.radius = radius;
136 self.theta = 0.0;
137 self.phi = 0.0;
138 self.update_position_from_spherical();
139 }
140
141 pub fn forward(&self) -> Vector3<f32> {
143 (self.target - self.position).normalize()
144 }
145
146 pub fn right(&self) -> Vector3<f32> {
148 self.forward().cross(&self.up).normalize()
149 }
150
151 pub fn camera_up(&self) -> Vector3<f32> {
153 self.right().cross(&self.forward()).normalize()
154 }
155
156 pub fn set_aspect_ratio(&mut self, aspect_ratio: f32) {
158 self.aspect_ratio = aspect_ratio;
159 }
160
161 pub fn distance_to_target(&self) -> f32 {
163 (self.position - self.target).magnitude()
164 }
165
166 pub fn set_fov_degrees(&mut self, fovy_degrees: f32) {
168 self.fovy_degrees = fovy_degrees.clamp(1.0, 179.0);
169 self.fov = self.fovy_degrees.to_radians();
170 }
171
172 pub fn fov_degrees(&self) -> f32 {
174 self.fovy_degrees
175 }
176}
177
178impl Default for Camera {
179 fn default() -> Self {
180 Self::new(
181 Point3::new(0.0, 0.0, 5.0),
182 Point3::new(0.0, 0.0, 0.0),
183 Vector3::new(0.0, 1.0, 0.0),
184 45.0, 16.0 / 9.0,
186 0.1,
187 100.0,
188 )
189 }
190}