use std::f32;
use glamx::{Mat4, Pose3, Vec2, Vec3};
use crate::camera::Camera3d;
use crate::event::{Action, Key, MouseButton, WindowEvent};
use crate::window::Canvas;
#[derive(Copy, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FirstPersonCamera3dStereo {
eye: Vec3,
eye_left: Vec3,
eye_right: Vec3,
ipd: f32,
yaw: f32,
pitch: f32,
yaw_step: f32,
pitch_step: f32,
move_step: f32,
fov: f32,
znear: f32,
zfar: f32,
view_left: Mat4,
view_right: Mat4,
proj: Mat4,
proj_view: Mat4,
inverse_proj_view: Mat4,
last_cursor_pos: Vec2,
last_framebuffer_size: Vec2,
}
impl FirstPersonCamera3dStereo {
pub fn new(eye: Vec3, at: Vec3, ipd: f32) -> FirstPersonCamera3dStereo {
FirstPersonCamera3dStereo::new_with_frustum(
f32::consts::PI / 4.0,
0.1,
1024.0,
eye,
at,
ipd,
)
}
pub fn new_with_frustum(
fov: f32,
znear: f32,
zfar: f32,
eye: Vec3,
at: Vec3,
ipd: f32,
) -> FirstPersonCamera3dStereo {
let mut res = FirstPersonCamera3dStereo {
eye: Vec3::ZERO,
eye_left: Vec3::ZERO,
eye_right: Vec3::ZERO,
ipd,
yaw: 0.0,
pitch: 0.0,
yaw_step: 0.005,
pitch_step: 0.005,
move_step: 0.5,
fov,
znear,
zfar,
proj_view: Mat4::IDENTITY,
inverse_proj_view: Mat4::IDENTITY,
last_cursor_pos: Vec2::ZERO,
last_framebuffer_size: Vec2::new(800.0, 600.0),
proj: Mat4::IDENTITY,
view_left: Mat4::IDENTITY,
view_right: Mat4::IDENTITY,
};
res.look_at(eye, at);
res
}
pub fn look_at(&mut self, eye: Vec3, at: Vec3) {
let dist = (eye - at).length();
let pitch = ((at.y - eye.y) / dist).acos();
let yaw = (at.z - eye.z).atan2(at.x - eye.x);
self.eye = eye;
self.yaw = yaw;
self.pitch = pitch;
self.update_eyes_location();
self.update_projviews();
}
pub fn at(&self) -> Vec3 {
let ax = self.eye.x + self.yaw.cos() * self.pitch.sin();
let ay = self.eye.y + self.pitch.cos();
let az = self.eye.z + self.yaw.sin() * self.pitch.sin();
Vec3::new(ax, ay, az)
}
fn update_restrictions(&mut self) {
if self.pitch <= 0.0001 {
self.pitch = 0.0001
}
let _pi: f32 = f32::consts::PI;
if self.pitch > _pi - 0.0001 {
self.pitch = _pi - 0.0001
}
}
#[doc(hidden)]
pub fn handle_left_button_displacement(&mut self, dpos: Vec2) {
self.yaw += dpos.x * self.yaw_step;
self.pitch += dpos.y * self.pitch_step;
self.update_restrictions();
self.update_projviews();
}
fn update_eyes_location(&mut self) {
let dir = (self.at() - self.eye).normalize();
let tangent = Vec3::Y.cross(dir).normalize();
self.eye_left = self.eye - tangent * (self.ipd / 2.0);
self.eye_right = self.eye + tangent * (self.ipd / 2.0);
}
#[doc(hidden)]
pub fn handle_right_button_displacement(&mut self, dpos: Vec2) {
let at = self.at();
let dir = (at - self.eye).normalize();
let tangent = Vec3::Y.cross(dir).normalize();
let bitangent = dir.cross(tangent);
self.eye = self.eye + tangent * (0.01 * dpos.x / 10.0) + bitangent * (0.01 * dpos.y / 10.0);
self.update_eyes_location();
self.update_restrictions();
self.update_projviews();
}
#[doc(hidden)]
pub fn handle_scroll(&mut self, yoff: f32) {
let front = self.view_transform().rotation * Vec3::Z;
self.eye += front * (self.move_step * yoff);
self.update_eyes_location();
self.update_restrictions();
self.update_projviews();
}
fn update_projviews(&mut self) {
let aspect = self.last_framebuffer_size.x / self.last_framebuffer_size.y;
self.proj = Mat4::perspective_rh_gl(self.fov, aspect, self.znear, self.zfar);
self.proj_view = self.proj * self.view_transform().to_mat4();
self.inverse_proj_view = self.proj_view.inverse();
self.view_left = self.view_transform_left().to_mat4();
self.view_right = self.view_transform_right().to_mat4();
}
#[allow(dead_code)]
fn view_eye(&self, eye: usize) -> Mat4 {
match eye {
0usize => self.view_left,
1usize => self.view_right,
_ => panic!("bad eye index"),
}
}
fn view_transform_left(&self) -> Pose3 {
Pose3::look_at_rh(self.eye_left, self.at(), Vec3::Y)
}
fn view_transform_right(&self) -> Pose3 {
Pose3::look_at_rh(self.eye_right, self.at(), Vec3::Y)
}
pub fn ipd(&self) -> f32 {
self.ipd
}
pub fn set_ipd(&mut self, ipd: f32) {
self.ipd = ipd;
self.update_eyes_location();
self.update_restrictions();
self.update_projviews();
}
}
impl Camera3d for FirstPersonCamera3dStereo {
fn clip_planes(&self) -> (f32, f32) {
(self.znear, self.zfar)
}
fn view_transform(&self) -> Pose3 {
Pose3::look_at_rh(self.eye, self.at(), Vec3::Y)
}
fn handle_event(&mut self, canvas: &Canvas, event: &WindowEvent) {
match *event {
WindowEvent::CursorPos(x, y, _) => {
let curr_pos = Vec2::new(x as f32, y as f32);
if canvas.get_mouse_button(MouseButton::Button1) == Action::Press {
let dpos = curr_pos - self.last_cursor_pos;
self.handle_left_button_displacement(dpos)
}
if canvas.get_mouse_button(MouseButton::Button2) == Action::Press {
let dpos = curr_pos - self.last_cursor_pos;
self.handle_right_button_displacement(dpos)
}
self.last_cursor_pos = curr_pos;
}
WindowEvent::Scroll(_, off, _) => self.handle_scroll(off as f32),
WindowEvent::FramebufferSize(w, h) => {
self.last_framebuffer_size = Vec2::new(w as f32, h as f32);
self.update_projviews();
}
_ => {}
}
}
fn eye(&self) -> Vec3 {
self.eye
}
fn transformation(&self) -> Mat4 {
self.proj_view
}
fn inverse_transformation(&self) -> Mat4 {
self.inverse_proj_view
}
fn update(&mut self, canvas: &Canvas) {
let t = self.view_transform();
let front = t.rotation * Vec3::Z;
let right = t.rotation * Vec3::X;
if canvas.get_key(Key::Up) == Action::Press {
self.eye += front * self.move_step
}
if canvas.get_key(Key::Down) == Action::Press {
self.eye += front * (-self.move_step)
}
if canvas.get_key(Key::Right) == Action::Press {
self.eye += right * (-self.move_step)
}
if canvas.get_key(Key::Left) == Action::Press {
self.eye += right * self.move_step
}
self.update_eyes_location();
self.update_restrictions();
self.update_projviews();
}
fn view_transform_pair(&self, pass: usize) -> (Pose3, Mat4) {
let view = match pass {
0 => self.view_transform_left(),
1 => self.view_transform_right(),
_ => self.view_transform(),
};
(view, self.proj)
}
fn num_passes(&self) -> usize {
2usize
}
fn start_pass(&self, _pass: usize, _canvas: &Canvas) {
}
fn render_complete(&self, _canvas: &Canvas) {
}
}