use super::Node;
use rin_math::{
Pnt3, pnt3, Mat4, one, Rect, Deg, Vec3, Angle
};
#[cfg(feature="serialize")]
use serde_derive::{Serialize, Deserialize};
#[derive(Clone,Copy,Debug,Eq,PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum CoordinateOrigin{
TopLeft,
BottomLeft,
CenterUp,
CenterDown,
}
impl CoordinateOrigin{
pub fn upwards(&self) -> bool{
match *self{
CoordinateOrigin::BottomLeft | CoordinateOrigin::CenterUp => true,
_ => false
}
}
pub fn downwards(&self) -> bool{
!self.upwards()
}
}
#[derive(Clone,Copy,Debug)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum Projection{
Perspective{
viewport: Rect<i32>,
fov: Deg<f32>,
znear: f32,
zfar: f32,
coordinate_origin: CoordinateOrigin,
camera_position: Pnt3,
},
Ortho{viewport: Rect<i32>, znear: f32, zfar: f32, coordinate_origin: CoordinateOrigin}
}
impl Projection{
pub fn new_perspective(viewport: Rect<i32>, fov: Deg<f32>, coordinate_origin: CoordinateOrigin) -> Projection{
let view_h = viewport.height as f32;
let eye_y = view_h / 2.0;
let half_fov = fov * 0.5;
let the_tan = half_fov.tan();
let dist = eye_y / the_tan;
let znear = dist / 10.0;
let zfar = dist * 10.0;
Projection::Perspective{
viewport: viewport,
fov: fov.to_deg(),
znear: znear,
zfar: zfar,
coordinate_origin: coordinate_origin,
camera_position: pnt3(viewport.width as f32 / 2. , eye_y, dist),
}
}
pub fn new_ortho(viewport: Rect<i32>, coordinate_origin: CoordinateOrigin) -> Projection{
Projection::Ortho{
viewport: viewport,
znear: -1.0,
zfar: 1.0,
coordinate_origin: coordinate_origin
}
}
pub fn new_ortho_clipped(viewport: Rect<i32>, coordinate_origin: CoordinateOrigin, near: f32, far: f32) -> Projection{
Projection::Ortho{
viewport: viewport,
znear: near,
zfar: far,
coordinate_origin: coordinate_origin
}
}
pub fn into_parts(self) -> (Rect<i32>, Mat4, Mat4, Pnt3, (f32, f32), CoordinateOrigin) {
match self{
Projection::Perspective{viewport, fov, znear, zfar, camera_position, coordinate_origin} => {
let eye_x = (viewport.width - viewport.pos.x) as f32 / 2.0;
let eye_y = (viewport.height - viewport.pos.y) as f32 / 2.0;
let aspect_ratio = viewport.width as f32/viewport.height as f32;
let half_fov = fov * 0.5;
let the_tan = half_fov.tan();
let fd = 1.0 / the_tan;
let dist = eye_y * fd;
let sx = fd / aspect_ratio as f32;
let sy = match coordinate_origin{
CoordinateOrigin::TopLeft => -fd,
CoordinateOrigin::BottomLeft => fd,
CoordinateOrigin::CenterDown => -fd,
CoordinateOrigin::CenterUp => fd
};
let sz = (zfar + znear) / (zfar - znear);
let tz = (2.0 * zfar * znear) / (zfar - znear);
let projection =
Mat4::new( sx, 0.0, 0.0, 0.0,
0.0, sy, 0.0, 0.0,
0.0, 0.0, sz, tz,
0.0, 0.0, -1.0, 0.0);
let view = Node::new_look_at(pnt3(eye_x,eye_y,dist), pnt3(eye_x,eye_y,0.0), Vec3::y())
.unwrap()
.inv_global_transformation();
(viewport, projection, view, camera_position, (znear, zfar), coordinate_origin)
}
Projection::Ortho{viewport, znear, zfar, coordinate_origin} => {
let (left,right) = match coordinate_origin{
CoordinateOrigin::BottomLeft | CoordinateOrigin::TopLeft => (viewport.pos.x, viewport.pos.x + viewport.width),
CoordinateOrigin::CenterUp | CoordinateOrigin::CenterDown => (viewport.pos.x - viewport.width/2, viewport.pos.x + viewport.width/2)
};
let (top,bottom) = match coordinate_origin{
CoordinateOrigin::BottomLeft => (viewport.pos.y + viewport.height, viewport.pos.y),
CoordinateOrigin::TopLeft => (viewport.pos.y, viewport.pos.y + viewport.height),
CoordinateOrigin::CenterUp => (viewport.pos.y + viewport.height / 2, viewport.pos.y - viewport.height / 2),
CoordinateOrigin::CenterDown => (viewport.pos.y - viewport.height / 2, viewport.pos.y + viewport.height / 2),
};
let tx = -(right+left) as f32/(right-left) as f32;
let ty = -(top+bottom) as f32/(top-bottom) as f32;
let tz = -(zfar+znear) as f32/(zfar-znear) as f32;
let projection =
Mat4::new(2.0/(right-left) as f32, 0.0, 0.0, tx,
0.0, 2.0/(top-bottom) as f32, 0.0, ty,
0.0, 0.0, -2.0/(zfar-znear), tz,
0.0, 0.0, 0.0, one());
(viewport, projection, one(), Pnt3::origin(), (znear, zfar), coordinate_origin)
}
}
}
pub fn viewport(&self) -> Rect<i32>{
match self{
&Projection::Perspective{viewport, ..} => {
viewport
}
&Projection::Ortho{viewport, ..} => {
viewport
}
}
}
pub fn origin(&self) -> CoordinateOrigin{
match self{
&Projection::Perspective{coordinate_origin, ..} => {
coordinate_origin
}
&Projection::Ortho{coordinate_origin, ..} => {
coordinate_origin
}
}
}
}