use std::marker::PhantomData;
use cgmath;
use collision;
use gfx;
use gfx_phase;
use hprof;
pub trait Culler<S: cgmath::BaseFloat, B: collision::Bound<S>> {
fn init(&mut self);
fn cull(&mut self, &B, &cgmath::Matrix4<S>) -> collision::Relation;
}
impl<S, B: collision::Bound<S>> Culler<S, B> for ()
where S: cgmath::BaseFloat
{
fn init(&mut self) {}
fn cull(&mut self, _: &B, _: &cgmath::Matrix4<S>) -> collision::Relation {
collision::Relation::Cross
}
}
pub struct Frustum<S, B>(PhantomData<(S, B)>);
impl<S, B> Frustum<S, B> {
pub fn new() -> Frustum<S, B> {
Frustum(PhantomData)
}
}
impl<S: cgmath::BaseFloat, B: collision::Bound<S>> Culler<S, B> for Frustum<S, B> {
fn init(&mut self) {}
fn cull(&mut self, bound: &B, mvp: &cgmath::Matrix4<S>) -> collision::Relation {
bound.relate_clip_space(mvp)
}
}
pub struct Context<'u, S, B, T, U> where
S: cgmath::BaseFloat,
B: collision::Bound<S>,
U: Culler<S, B> + 'u,
{
culler: &'u mut U,
cam_inverse: T,
view_projection: cgmath::Matrix4<S>,
dummy: PhantomData<B>,
}
impl<'u,
S: cgmath::BaseFloat,
B: collision::Bound<S>,
T: cgmath::Transform3<S> + Clone,
U: Culler<S, B>,
> Context<'u, S, B, T, U> {
pub fn new<C>(culler: &'u mut U, camera: &C) -> Context<'u, S, B, T, U> where
C: ::Camera<S, Transform = T>,
{
use cgmath::{Matrix, Transform};
let cam_inverse = camera.get_transform().invert().unwrap();
let mx_proj: cgmath::Matrix4<S> = camera.get_projection().into();
let mx_view_proj = mx_proj.mul_m(&cam_inverse.clone().into());
culler.init();
Context {
culler: culler,
cam_inverse: cam_inverse,
view_projection: mx_view_proj,
dummy: PhantomData
}
}
pub fn is_visible<N, V>(&mut self, node: &N, bound: &B)
-> Option<V> where
N: ::Node<Transform = T>,
V: ::ViewInfo<S, T>
{
use cgmath::{Matrix, Transform};
let model = node.get_transform();
let view = self.cam_inverse.concat(&model);
let mvp = self.view_projection.mul_m(&model.clone().into());
if self.culler.cull(bound, &mvp) != collision::Relation::Out {
Some(::ViewInfo::new(mvp, view, model))
}else {
None
}
}
pub fn draw<'b, R, M, E, I, V, H, X>(&mut self,
entities: I, phase: &mut H, stream: &mut X)
-> Result<::Report, ::Error> where
R: gfx::Resources + 'b,
M: 'b,
E: ::Entity<R, M, Bound = B, Transform = T> + 'b,
I: Iterator<Item = &'b E>,
V: ::ViewInfo<S, T>,
H: gfx_phase::AbstractPhase<R, M, V>,
X: gfx::Stream<R>,
{
let mut report = ::Report::new();
let g = hprof::enter("enqueue");
for ent in entities {
let frag_count = ent.get_fragments().len() as ::Count;
if !ent.is_visible() {
report.calls_invisible += frag_count;
continue
}
if let Some(view_info) = self.is_visible(ent, &ent.get_bound()) {
for frag in ent.get_fragments().iter() {
match phase.enqueue(ent.get_mesh(), &frag.slice, &frag.material, &view_info) {
Ok(true) => {
report.primitives_rendered += frag.slice.get_prim_count();
report.calls_passed += 1;
},
Ok(false) => report.calls_rejected += 1,
Err(e) => return Err(::Error::Batch(e)),
}
}
} else {
report.calls_culled += frag_count;
}
}
drop(g);
let _g = hprof::enter("flush");
match phase.flush(stream) {
Ok(()) => Ok(report),
Err(e) => Err(::Error::Flush(e)),
}
}
}