use cgmath;
use mint;
use hub::{Hub, Operation, SubNode};
use object::{Base, DowncastObject, Object, ObjectType};
use scene::SyncGuard;
use std::ops;
#[derive(Clone, Debug, PartialEq)]
pub enum ZRange {
Finite(ops::Range<f32>),
Infinite(ops::RangeFrom<f32>),
}
impl From<ops::Range<f32>> for ZRange {
fn from(range: ops::Range<f32>) -> ZRange {
ZRange::Finite(range)
}
}
impl From<ops::RangeFrom<f32>> for ZRange {
fn from(range: ops::RangeFrom<f32>) -> ZRange {
ZRange::Infinite(range)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Projection {
Orthographic(Orthographic),
Perspective(Perspective),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Camera {
pub(crate) object: Base,
}
impl AsRef<Base> for Camera {
fn as_ref(&self) -> &Base { &self.object }
}
impl Object for Camera {
type Data = Projection;
fn resolve_data(&self, sync_guard: &SyncGuard) -> Self::Data {
match &sync_guard.hub[self].sub_node {
SubNode::Camera(ref projection) => projection.clone(),
sub_node @ _ => panic!("`Group` had a bad sub node type: {:?}", sub_node),
}
}
}
impl Camera {
pub(crate) fn new(hub: &mut Hub, projection: Projection) -> Self {
Camera {
object: hub.spawn(SubNode::Camera(projection)),
}
}
pub fn set_projection<P: Into<Projection>>(&self, projection: P) {
self.as_ref().send(Operation::SetProjection(projection.into()));
}
}
impl DowncastObject for Camera {
fn downcast(object_type: ObjectType) -> Option<Self> {
match object_type {
ObjectType::Camera(camera) => Some(camera),
_ => None,
}
}
}
impl Projection {
pub fn orthographic<P>(
center: P,
extent_y: f32,
range: ops::Range<f32>,
) -> Self
where
P: Into<mint::Point2<f32>>,
{
let center = center.into();
Projection::Orthographic(Orthographic {
center,
extent_y,
range,
})
}
pub fn perspective<R>(
fov_y: f32,
range: R,
) -> Self
where
R: Into<ZRange>,
{
Projection::Perspective(Perspective {
fov_y,
zrange: range.into(),
})
}
pub fn matrix(
&self,
aspect_ratio: f32,
) -> mint::ColumnMatrix4<f32> {
match *self {
Projection::Orthographic(ref x) => x.matrix(aspect_ratio),
Projection::Perspective(ref x) => x.matrix(aspect_ratio),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Orthographic {
pub center: mint::Point2<f32>,
pub extent_y: f32,
pub range: ops::Range<f32>,
}
impl Orthographic {
pub fn matrix(
&self,
aspect_ratio: f32,
) -> mint::ColumnMatrix4<f32> {
let extent_x = aspect_ratio * self.extent_y;
cgmath::ortho(
self.center.x - extent_x,
self.center.x + extent_x,
self.center.y - self.extent_y,
self.center.y + self.extent_y,
self.range.start,
self.range.end,
).into()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Perspective {
pub fov_y: f32,
pub zrange: ZRange,
}
impl Perspective {
pub fn matrix(
&self,
aspect_ratio: f32,
) -> mint::ColumnMatrix4<f32> {
match self.zrange {
ZRange::Finite(ref range) => cgmath::perspective(
cgmath::Deg(self.fov_y),
aspect_ratio,
range.start,
range.end,
).into(),
ZRange::Infinite(ref range) => {
let f = 1.0 / (0.5 * self.fov_y.to_radians()).tan();
let m00 = f / aspect_ratio;
let m11 = f;
let m22 = -1.0;
let m23 = -1.0;
let m32 = -2.0 * range.start;
let m = [
[m00, 0.0, 0.0, 0.0],
[0.0, m11, 0.0, 0.0],
[0.0, 0.0, m22, m23],
[0.0, 0.0, m32, 0.0],
];
m.into()
}
}
}
}