starstruck 0.1.0-alpha.3

A game building utility that is made to be simple to use while still providing great performance
Documentation
use crate::camera::Camera;
use crate::graphics::Bundle;
use crate::graphics::BundleEncoderExt;
use crate::graphics::Pipeline;
use crate::graphics::PipelineEncoderExt;
use crate::input::UserInput;
use crate::internal::Mat4Ext;
use crate::primitive::Index;
use crate::primitive::Vertex;
use crate::setup_context::SetupContext;
use gfx_hal::command::RenderPassInlineEncoder;
use gfx_hal::pso::ShaderStageFlags;
use gfx_hal::window::Extent2D;
use gfx_hal::Backend;
use gfx_hal::Device;
use gfx_hal::Instance;
use vek::geom::FrustumPlanes;
use vek::Mat4;
use crate::allocator::GpuAllocator;
use crate::allocator::DefaultGpuAllocator;
use crate::allocator::DefaultChunk;
use std::sync::Arc;
use futures::lazy;
use failure::Error;
use futures::IntoFuture;
use futures::Future;

pub struct Context<
    'a,
    A: GpuAllocator<B, D> = DefaultGpuAllocator<DefaultChunk<backend::Backend, backend::Device>, backend::Backend, backend::Device>,
    B: Backend = backend::Backend,
    D: Device<B> = backend::Device,
    I: Instance<Backend = B> = backend::Instance,
> {
    setup_context: Arc<SetupContext<A, B, D, I>>,
    input: UserInput,
    encoder: RenderPassInlineEncoder<'a, B>,
    base_projection: Mat4<f32>,
    render_area: Extent2D,
    stop: bool,
}

impl<'a, A: GpuAllocator<B, D>, B: Backend, D: Device<B>, I: Instance<Backend = B>> Context<'a, A, B, D, I> {
    pub(crate) fn new(
        input: UserInput,
        setup_context: Arc<SetupContext<A, B, D, I>>,
        encoder: RenderPassInlineEncoder<'a, B>,
        render_area: Extent2D,
    ) -> Self {
        let ratio = (((render_area.width as f32 / render_area.height as f32) - 1.0) / 2.0) + 1.0;
        Context {
            input,
            encoder,
            setup_context,
            render_area,
            base_projection: Mat4::<f32>::orthographic_lh_zo(FrustumPlanes {
                left: ratio * -1.0,
                right: ratio,
                bottom: -1.,
                top: 1.,
                near: 0.,
                far: 100.,
            }),
            stop: false,
        }
    }

    pub fn setup<
        FU: Future<Item=(), Error=Error> + Send + 'static + Sized,
        IN: IntoFuture<Future=FU, Item=(), Error=Error> + Send + 'static,
        C: FnOnce(&SetupContext<A, B, D, I>) -> IN + Send + 'static
    >(&self, callback: C) {
        let cloned_setup = Arc::clone(&self.setup_context);
        tokio::run(lazy(move || {
            callback(&*cloned_setup).into_future().map_err(|error| {
                error!("Error occurred in setup callback: {:?}", error);
            })
        }))
    }

    pub fn stop_starstruck(&mut self) {
        self.stop = true;
    }

    pub(crate) fn should_stop_starstruck(&self) -> bool {
        self.stop
    }

    pub fn render_area(&self) -> Extent2D {
        self.render_area
    }

    pub fn input(&self) -> &UserInput {
        &self.input
    }

    pub fn setup_context(&self) -> &SetupContext<A, B, D, I> {
        &*self.setup_context
    }

    pub fn draw<In: Index, V: Vertex>(
        &mut self,
        pipeline: &Pipeline<V, A, B, D, I>,
        bundle: &Bundle<In, V, A, B, D, I>,
    ) where
        RenderPassInlineEncoder<'a, B>: BundleEncoderExt<In, V, A, B, D, I>,
    {
        self.encoder.bind_pipeline(pipeline);
        self.encoder.bind_bundle(bundle);

        unsafe {
            let mat_data = self.base_projection.as_push_constant_data();
            self.encoder
                .bind_push_constant(pipeline, ShaderStageFlags::VERTEX, 0, mat_data);
            self.encoder.draw_indexed(0..bundle.index_count(), 0, 0..1)
        }
    }

    pub fn draw_with_camera<In: Index, V: Vertex>(
        &mut self,
        pipeline: &Pipeline<V, A, B, D, I>,
        bundle: &Bundle<In, V, A, B, D, I>,
        camera: &Camera,
    ) where
        RenderPassInlineEncoder<'a, B>: BundleEncoderExt<In, V, A, B, D, I>,
    {
        self.encoder.bind_pipeline(pipeline);
        self.encoder.bind_bundle(bundle);

        unsafe {
            let mut mat = camera.projection_view();
            let mat_data = mat.as_push_constant_data();
            self.encoder
                .bind_push_constant(pipeline, ShaderStageFlags::VERTEX, 0, mat_data);
            self.encoder.draw_indexed(0..bundle.index_count(), 0, 0..1)
        }
    }
}