shadiertoy 0.1.0

A shadertoy rip-off.
Documentation
#![deny(missing_docs)]
#![feature(use_extern_macros)]

//! A Shadertoy rip-off.
//!
//! There are examples in the repository.

// Crates

pub extern crate gfx;
pub extern crate glutin;
pub extern crate image;
extern crate gfx_device_gl;
extern crate gfx_window_glutin;

// Modules

mod error;
mod game;
mod pipeline;

// Imports

use gfx::{Device, Encoder, Factory, Slice};
use gfx::format::{Rgba8, DepthStencil};
use gfx::handle::Sampler;
use gfx::pso::{PipelineData, PipelineInit, PipelineState};
use gfx::texture::{AaMode, Kind, Mipmap};
use gfx::traits::FactoryExt;
use gfx_device_gl::{CommandBuffer, Device as GlDevice, Factory as GlFactory, Resources};
use glutin::{ContextBuilder, EventsLoop, GlContext, GlWindow, WindowBuilder};
use image::RgbaImage;

// Type Aliases

/// The `gfx-rs` resource used in this crate.
pub type R = Resources;
/// A color buffer handle.
pub type RenderTargetView = gfx::handle::RenderTargetView<R, Rgba8>;
/// A depth-stencil buffer handle.
pub type DepthStencilView = gfx::handle::DepthStencilView<R, DepthStencil>;
/// A texture handle.
pub type ShaderResourceView  = gfx::handle::ShaderResourceView<R, [f32; 4]>;

/// A compiled shader.
pub type Shader<T> = PipelineState<R, T>;
/// A loaded texture.
pub type Texture = (ShaderResourceView, Sampler<R>);

// Exports

pub use error::{Error, Result};
pub use game::Game;

// Data Types

pub use gfx::{Global, TextureSampler};
/// A link type for the color buffer.
pub type RenderTarget = gfx::RenderTarget<Rgba8>;
/// A link type for the depth buffer.
pub type DepthTarget = gfx::DepthTarget<DepthStencil>;

// Default Shaders

const VERTEX_SHADER: &'static [u8] = b"
#version 130

void main() {
    float x = -1.0 + float((gl_VertexID & 1) << 2);
    float y = -1.0 + float((gl_VertexID & 2) << 1);
    gl_Position = vec4(x, y, 0, 1);
}
";

// Window

/// A window that you can draw on.
pub struct Window {
    window: GlWindow,
    device: GlDevice,
    factory: GlFactory,
    rtv: RenderTargetView,
    stv: DepthStencilView,
    encoder: Encoder<R, CommandBuffer>,
}

impl Window {
    /// Make a new window that runs on the given event loop.
    pub fn new(events: &EventsLoop) -> Self {
        let (window, device, mut factory, rtv, stv) = gfx_window_glutin::init(
            WindowBuilder::new(),
            ContextBuilder::new().with_vsync(true),
            &events,
        );

        Window {
            encoder: factory.create_command_buffer().into(),
            window, device, factory, rtv, stv,
        }
    }

    /// Compile a shader.
    pub fn shader<I>(&mut self, init: I, shader: &[u8])
        -> Result<Shader<I::Meta>>
    where
        I: PipelineInit,
    {
        Ok(self.factory.create_pipeline_simple(
            VERTEX_SHADER,
            shader,
            init,
        )?)
    }

    /// Pass the given data to the shader and draw!
    pub fn draw<'a, D, F>(&'a mut self, shader: &Shader<D::Meta>, data: F)
    where
        D: PipelineData<R>,
        F: FnOnce(&'a RenderTargetView, &'a DepthStencilView) -> D
    {
        let slice = Slice {
            start: 0,
            end: 3,
            base_vertex: 0,
            instances: None,
            buffer: Default::default(),
        };

        self.encoder.draw(&slice, shader, &data(&self.rtv, &self.stv));
    }

    /// Finish the frame.
    pub fn flush(&mut self) {
        self.encoder.flush(&mut self.device);
        self.window.swap_buffers().unwrap();
        self.device.cleanup();
    }

    /// Handle a resize event, resizing the buffers appropriately.
    pub fn resize(&mut self) {
        gfx_window_glutin::update_views(&self.window, &mut self.rtv, &mut self.stv);
    }

    /// Load a texture, which can then be passed to the shader.
    ///
    /// The corresponding link type is `TextureSampler`.
    pub fn texture(&mut self, image: RgbaImage) -> Texture {
        let (width, height) = image.dimensions();

        let (_, view) = self.factory
            .create_texture_immutable_u8::<Rgba8>(
                Kind::D2(width as u16, height as u16, AaMode::Single),
                Mipmap::Provided,
                &[&image],
            )
            .unwrap();

        (view, self.factory.create_sampler_linear())
    }

    /// Get the size of this window, in pixels.
    ///
    /// This is the resolution you should pass to your shaders. The function
    /// returns `None` if the window has already been closed.
    pub fn size(&self) -> Option<(u32, u32)> {
        self.window().get_inner_size()
    }

    /// Get the raw window handle for this window.
    ///
    /// This might be useful for locking the mouse or resizing the window.
    pub fn window(&self) -> &glutin::Window { self.window.window() }

    /// Get the encoder for this window.
    ///
    /// You probably don't need this.
    pub fn encoder(&mut self) -> &mut Encoder<R, CommandBuffer> { &mut self.encoder }

    /// Get the factory for this window.
    ///
    /// You probably don't need this.
    pub fn factory(&mut self) -> &mut GlFactory { &mut self.factory }
}