use std::ops::{Add, Div, Mul, Neg, Sub};
use mat4;
use num_traits::{Float, FromPrimitive};
use specs::{Component, VecStorage};
pub struct Camera3D<T> {
fov: T,
near: T,
far: T,
ortho_size: T,
ortho_mode: bool,
projection: [T; 16],
view: [T; 16],
}
impl<T> Component for Camera3D<T>
where
T: 'static + Sync + Send,
{
type Storage = VecStorage<Self>;
}
impl<T> Default for Camera3D<T>
where
T: Float + FromPrimitive,
{
#[inline(always)]
fn default() -> Self {
Camera3D {
fov: T::from_usize(35).unwrap(),
near: T::from_f32(::std::f32::EPSILON).unwrap(),
far: T::from_usize(1024).unwrap(),
ortho_size: T::from_usize(2).unwrap(),
ortho_mode: false,
projection: mat4::new_identity(),
view: mat4::new_identity(),
}
}
}
impl<T> Camera3D<T>
where
T: Float + FromPrimitive,
for<'a, 'b> &'a T: Sub<&'b T, Output = T>
+ Add<&'b T, Output = T>
+ Div<&'b T, Output = T>
+ Mul<&'b T, Output = T>
+ Neg<Output = T>,
{
#[inline(always)]
pub fn new() -> Self {
Self::default()
}
#[inline(always)]
pub fn fov(&self) -> T {
self.ortho_size
}
#[inline(always)]
pub fn set_fov(&mut self, fov: T) {
self.fov = fov;
}
#[inline(always)]
pub fn near(&self) -> T {
self.near
}
#[inline(always)]
pub fn set_near(&mut self, near: T) {
self.near = near;
}
#[inline(always)]
pub fn far(&self) -> T {
self.far
}
#[inline(always)]
pub fn set_far(&mut self, far: T) {
self.far = far;
}
#[inline(always)]
pub fn ortho_size(&self) -> T {
self.ortho_size
}
#[inline(always)]
pub fn set_ortho_size(&mut self, ortho_size: T) {
self.ortho_size = ortho_size;
}
#[inline(always)]
pub fn ortho_mode(&self) -> bool {
self.ortho_mode
}
#[inline(always)]
pub fn set_ortho_mode(&mut self, ortho_mode: bool) {
self.ortho_mode = ortho_mode;
}
#[inline(always)]
pub fn projection(&self) -> &[T; 16] {
&self.projection
}
#[inline(always)]
pub fn view(&self) -> &[T; 16] {
&self.view
}
#[inline]
pub fn update_view(&mut self, matrix: &[T; 16]) {
mat4::inv(&mut self.view, matrix);
}
#[inline]
pub fn update_projection(&mut self, width: usize, height: usize) {
let w = T::from_usize(width).unwrap();
let h = T::from_usize(height).unwrap();
let w_gt_h = &w > &h;
let aspect = if w_gt_h { &w / &h } else { &h / &w };
if self.ortho_mode {
let ortho_size_aspect = &self.ortho_size * &aspect;
let r = if w_gt_h {
ortho_size_aspect
} else {
self.ortho_size
};
let l = -&r;
let t = if w_gt_h {
self.ortho_size
} else {
ortho_size_aspect
};
let b = -&t;
mat4::orthographic(&mut self.projection, &t, &r, &b, &l, &self.near, &self.far);
} else {
mat4::perspective(
&mut self.projection,
&self.fov,
&aspect,
&self.near,
&self.far,
);
}
}
}