khora_core/ui/editor/
camera.rs1use crate::math::{Mat4, Vec3, FRAC_PI_4};
22use crate::renderer::api::resource::view::ViewInfo;
23
24#[derive(Debug, Clone)]
29pub struct EditorCamera {
30 pub target: Vec3,
32 pub yaw: f32,
34 pub pitch: f32,
36 pub distance: f32,
38 pub fov_y: f32,
40 pub near: f32,
42 pub far: f32,
44 pub orbit_speed: f32,
46 pub pan_speed: f32,
48 pub zoom_speed: f32,
50 pub min_distance: f32,
52 pub max_distance: f32,
54}
55
56impl Default for EditorCamera {
57 fn default() -> Self {
58 Self {
59 target: Vec3::ZERO,
60 yaw: 0.4, pitch: -0.4, distance: 10.0,
63 fov_y: FRAC_PI_4, near: 0.1,
65 far: 1000.0,
66 orbit_speed: 0.005,
67 pan_speed: 0.01,
68 zoom_speed: 1.0,
69 min_distance: 0.5,
70 max_distance: 500.0,
71 }
72 }
73}
74
75impl EditorCamera {
76 pub fn position(&self) -> Vec3 {
78 let x = self.distance * self.pitch.cos() * self.yaw.sin();
79 let y = self.distance * (-self.pitch).sin();
80 let z = self.distance * self.pitch.cos() * self.yaw.cos();
81 self.target + Vec3::new(x, y, z)
82 }
83
84 pub fn orbit(&mut self, delta_x: f32, delta_y: f32) {
86 self.yaw += delta_x * self.orbit_speed;
87 self.pitch -= delta_y * self.orbit_speed;
88 let limit = 89.0_f32.to_radians();
90 self.pitch = self.pitch.clamp(-limit, limit);
91 }
92
93 pub fn pan(&mut self, delta_x: f32, delta_y: f32) {
95 let right = self.right();
96 let up = self.up();
97 let speed = self.pan_speed * self.distance * 0.1;
98 self.target = self.target - right * delta_x * speed + up * delta_y * speed;
99 }
100
101 pub fn zoom(&mut self, delta: f32) {
103 self.distance -= delta * self.zoom_speed * self.distance * 0.1;
104 self.distance = self.distance.clamp(self.min_distance, self.max_distance);
105 }
106
107 pub fn focus_on(&mut self, point: Vec3) {
109 self.target = point;
110 }
111
112 pub fn forward(&self) -> Vec3 {
114 (self.target - self.position()).normalize()
115 }
116
117 pub fn right(&self) -> Vec3 {
119 self.forward().cross(Vec3::Y).normalize()
120 }
121
122 pub fn up(&self) -> Vec3 {
124 self.right().cross(self.forward()).normalize()
125 }
126
127 pub fn view_info(&self, width: f32, height: f32) -> ViewInfo {
129 let pos = self.position();
130 let view_matrix = Mat4::look_at_rh(pos, self.target, Vec3::Y).unwrap_or(Mat4::IDENTITY);
131 let aspect = if height > 0.0 { width / height } else { 1.0 };
132 let projection_matrix = Mat4::perspective_rh_zo(self.fov_y, aspect, self.near, self.far);
133
134 ViewInfo {
135 view_matrix,
136 projection_matrix,
137 camera_position: pos,
138 }
139 }
140
141 pub fn screen_to_ray(&self, x: f32, y: f32, width: f32, height: f32) -> crate::physics::Ray {
146 let view_info = self.view_info(width, height);
147 let vp = view_info.view_projection_matrix();
148 let inv_vp = vp.inverse().unwrap_or(Mat4::IDENTITY);
149
150 let ndc_x = (2.0 * x / width) - 1.0;
152 let ndc_y = 1.0 - (2.0 * y / height);
153
154 let near_ndc = crate::math::Vec4::new(ndc_x, ndc_y, 0.0, 1.0);
155 let far_ndc = crate::math::Vec4::new(ndc_x, ndc_y, 1.0, 1.0);
156
157 let near_world = inv_vp * near_ndc;
158 let far_world = inv_vp * far_ndc;
159
160 let near_pt = near_world.truncate() / near_world.w;
161 let far_pt = far_world.truncate() / far_world.w;
162
163 let direction = (far_pt - near_pt).normalize();
164
165 crate::physics::Ray {
166 origin: near_pt,
167 direction,
168 }
169 }
170}