I have this basic orbit camera controller set up. The pitch works perfectly fine, but I am failing to properly get the Yaw to orbit around the object horizontally (like a model viewer or blender orbit camera). It appears to just Roll in place, instead of Yaw. I have tried multiple implementations, but none actually stop the camera from rolling. Thank you for any assistance.
use cgmath::{Matrix4, Point3, Vector3, InnerSpace, EuclideanSpace, Rad, Angle, MetricSpace};
pub struct Camera {
pub position: Point3<f32>,
pub target: Point3<f32>,
pub up: Vector3<f32>,
pub fov: f32,
pub aspect_ratio: f32,
pub near: f32,
pub far: f32,
pub yaw: f32,
pub pitch: f32,
}
impl Camera {
pub fn new(position: Point3<f32>, target: Point3<f32>, up: Vector3<f32>, aspect_ratio: f32) -> Self {
Self {
position,
target,
up,
fov: 45.0,
aspect_ratio,
near: 0.1,
far: 100.0,
yaw: -90.0,
pitch: 0.0,
}
}
pub fn get_view_matrix(&self) -> Matrix4<f32> {
Matrix4::look_at_rh(self.position, self.target, self.up)
}
pub fn get_projection_matrix(&self) -> Matrix4<f32> {
cgmath::perspective(cgmath::Deg(self.fov), self.aspect_ratio, self.near, self.far)
}
pub fn rotate(&mut self, delta_yaw: f32, delta_pitch: f32) {
self.yaw += delta_yaw;
self.pitch += delta_pitch;
if self.pitch > 89.0 {
self.pitch = 89.0;
} else if self.pitch < -89.0 {
self.pitch = -89.0;
}
let yaw_rad = Rad(self.yaw.to_radians());
let pitch_rad = Rad(self.pitch.to_radians());
let x = yaw_rad.cos() * pitch_rad.cos();
let y = pitch_rad.sin();
let z = yaw_rad.sin() * pitch_rad.cos();
let front = Vector3::new(x, y, z).normalize();
self.position = Point3::origin() - front * self.position.distance(Point3::origin());
self.target = Point3::origin();
self.up = Vector3::unit_y();
}
pub fn zoom(&mut self, delta: f32) {
let direction = (self.target - self.position).normalize();
self.position += direction * delta;
}
}
The snippet called for main.rs would be. But I believe the issue lies within the camera.rs code above.
let mut last_frame_time = Instant::now();
let mut frame_times = vec![];
let mut width = 800.0;
let mut height = 600.0;
let mut camera = Camera::new(
Point3::new(0.0, 0.0, 3.0),
Point3::new(0.0, 0.0, 0.0),
Vector3::new(0.0, 1.0, 0.0),
width / height,
);
let mut is_rotating = false;
let mut last_mouse_position = (0.0, 0.0);
el.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
Event::LoopDestroyed => return,
Event::MainEventsCleared => {
windowed_context.window().request_redraw();
},
Event::RedrawRequested(_) => {
let current_frame_time = Instant::now();
let delta_time = current_frame_time.duration_since(last_frame_time).as_secs_f32();
last_frame_time = current_frame_time;
frame_times.push(delta_time);
if frame_times.len() > 100 {
frame_times.remove(0);
}
let fps = if !frame_times.is_empty() {
1.0 / (frame_times.iter().sum::<f32>() / frame_times.len() as f32)
} else {
0.0
};
println!("FPS: {:.2}", fps);
println!("Model count: {}", models.len());
let view = camera.get_view_matrix();
let projection = camera.get_projection_matrix();
unsafe {
gl::Viewport(0, 0, width as GLsizei, height as GLsizei);
gl::ClearColor(0.2, 0.3, 0.3, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
gl::Enable(gl::DEPTH_TEST);
}
renderer.render(&models, &view, &projection);
windowed_context.swap_buffers().unwrap();
},
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
windowed_context.resize(physical_size);
width = physical_size.width as f32;
height = physical_size.height as f32;
camera.aspect_ratio = width / height;
},
WindowEvent::MouseInput { state, button, .. } => {
if button == MouseButton::Middle {
is_rotating = state == ElementState::Pressed;
}
},
WindowEvent::MouseWheel { delta, .. } => {
let zoom_amount = match delta {
MouseScrollDelta::LineDelta(_, y) => y * 0.5,
MouseScrollDelta::PixelDelta(pos) => pos.y as f32 * 0.05,
};
camera.zoom(zoom_amount);
},
WindowEvent::CursorMoved { position, .. } => {
let (x, y) = (position.x as f32, position.y as f32);
if is_rotating {
let delta_x = x - last_mouse_position.0;
let delta_y = y - last_mouse_position.1;
camera.rotate(delta_x * 0.1, delta_y * 0.1);
}
last_mouse_position = (x, y);
},
_ => (),
},
_ => (),
}
});
}
default is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.