rusterix/camera/
d3orbit.rs1use vek::{Mat4, Vec2, Vec3};
2
3use super::{D3Camera, Ray};
4
5#[derive(Clone)]
6pub struct D3OrbitCamera {
7 pub center: Vec3<f32>,
8 pub distance: f32,
9 pub azimuth: f32,
10 pub elevation: f32,
11 pub up: Vec3<f32>,
12
13 pub fov: f32,
14 pub near: f32,
15 pub far: f32,
16}
17
18impl D3Camera for D3OrbitCamera {
19 fn position(&self) -> Vec3<f32> {
20 self.eye_position()
21 }
22
23 fn new() -> Self {
24 Self {
25 center: Vec3::zero(),
26 distance: 20.0,
27 azimuth: std::f32::consts::PI / 2.0,
28 elevation: 0.698,
29 up: Vec3::unit_y(),
30
31 fov: 75.0,
32 near: 0.01,
33 far: 100.0,
34 }
35 }
36
37 fn id(&self) -> String {
38 "orbit".to_string()
39 }
40
41 fn fov(&self) -> f32 {
42 self.fov
43 }
44
45 fn distance(&self) -> f32 {
46 self.distance
47 }
48
49 fn view_matrix(&self) -> Mat4<f32> {
50 let position = self.eye_position();
51 Mat4::look_at_rh(position, self.center, self.up)
52 }
53
54 fn projection_matrix(&self, width: f32, height: f32) -> Mat4<f32> {
55 vek::Mat4::perspective_fov_rh_zo(self.fov.to_radians(), width, height, self.near, self.far)
56 }
57
58 fn set_parameter_f32(&mut self, key: &str, value: f32) {
59 #[allow(clippy::single_match)]
60 match key {
61 "distance" => {
62 self.distance = value;
63 }
64 _ => {}
65 }
66 }
67
68 fn set_parameter_vec2(&mut self, key: &str, value: Vec2<f32>) {
69 #[allow(clippy::single_match)]
70 match key {
71 "from_normalized" => {
72 self.azimuth = std::f32::consts::PI * value.x;
73 self.elevation = std::f32::consts::PI * (value.y - 0.5);
74 }
75 _ => {}
76 }
77 }
78
79 fn set_parameter_vec3(&mut self, key: &str, value: Vec3<f32>) {
80 #[allow(clippy::single_match)]
81 match key {
82 "center" => {
83 self.center = value;
84 }
85 _ => {}
86 }
87 }
88
89 fn rotate(&mut self, delta: Vec2<f32>) {
92 let sensitivity = 0.005;
94
95 self.azimuth -= delta.x * sensitivity;
96 self.elevation += delta.y * sensitivity;
97
98 let epsilon = 0.01;
100 let max_elevation = std::f32::consts::FRAC_PI_2 - epsilon;
101 self.elevation = self.elevation.clamp(-max_elevation, max_elevation);
102 }
103
104 fn zoom(&mut self, delta: f32) {
106 let zoom_sensitivity = 0.05;
107
108 let zoom_factor = (1.0 - delta * zoom_sensitivity).clamp(0.5, 2.0);
110
111 self.distance *= zoom_factor;
112
113 self.distance = self.distance.clamp(0.1, 100.0);
114 }
115
116 fn create_ray(&self, uv: Vec2<f32>, screen: Vec2<f32>, offset: Vec2<f32>) -> Ray {
118 let aspect = screen.x / screen.y;
119 let pixel_size = Vec2::new(1.0 / screen.x, 1.0 / screen.y);
120
121 let mut uv = uv;
122 uv.y = 1.0 - uv.y;
123
124 let position = self.eye_position();
126
127 let forward = (self.center - position).normalized(); let mut right = forward.cross(self.up);
130 if right.magnitude_squared() < 1e-12 {
131 right = Vec3::unit_x();
132 }
133 right = right.normalized();
134 let up = right.cross(forward).normalized();
135
136 let half_height = (self.fov.to_radians() * 0.5).tan();
138 let half_width = half_height * aspect;
139
140 let pixel_ndc = Vec2::new(
142 (pixel_size.x * offset.x + uv.x) * 2.0 - 1.0, (pixel_size.y * offset.y + uv.y) * 2.0 - 1.0,
144 );
145
146 let dir = (forward + right * pixel_ndc.x * half_width - up * pixel_ndc.y * half_height) .normalized();
148
149 Ray {
150 origin: position,
151 dir,
152 }
153 }
154
155 fn basis_vectors(&self) -> (Vec3<f32>, Vec3<f32>, Vec3<f32>) {
156 let position = self.eye_position();
157
158 let forward = (self.center - position).normalized();
159 let mut right = forward.cross(self.up);
160 if right.magnitude_squared() < 1e-12 {
161 right = Vec3::unit_x();
162 }
163 right = right.normalized();
164 let up = right.cross(forward).normalized();
165 (forward, right, up)
166 }
167
168 fn as_scenevm_camera(&self) -> scenevm::Camera3D {
170 let pos = self.eye_position();
171 let (forward, right, up) = self.basis_vectors();
172 scenevm::Camera3D {
173 kind: scenevm::CameraKind::OrbitPersp,
174 pos,
175 forward,
176 right,
177 up,
178 vfov_deg: self.fov,
179 near: self.near,
180 far: self.far,
181 ..Default::default()
182 }
183 }
184}
185
186impl D3OrbitCamera {
187 #[inline]
188 fn eye_position(&self) -> Vec3<f32> {
189 let x = self.distance * self.azimuth.cos() * self.elevation.cos();
191 let y = self.distance * self.elevation.sin();
192 let z = self.distance * self.azimuth.sin() * self.elevation.cos();
193
194 Vec3::new(x, y, z) + self.center
195 }
196}