ombre 0.6.7

Shadowy game and graphics library for Rust
Documentation
#[path = "common/runtime.rs"]
mod runtime;

use ombre::prelude::*;

struct Renderer<T> {
    display_pipeline: gfx::PipelineId,
    display_bind: gfx::Bindings,
    offscreen_pipeline: gfx::PipelineId,
    offscreen_bind: gfx::Bindings,
    offscreen_pass: gfx::PassId,
    rx: f32,
    ry: f32,
    width: f64,
    height: f64,
    backend: T,
}

impl<T: gfx::RenderBackend> RuntimeRenderer for Renderer<T> {
    type Error = T::Error;

    fn window_resized(&mut self, size: platform::LogicalSize) {
        self.width = size.width;
        self.height = size.height;
        self.backend.window_resized(size);
    }

    fn frame(&mut self) -> Result<(), Self::Error> {
        let (width, height) = (self.width as f32, self.height as f32);
        let proj = Transform3D::perspective(60.0f32, width / height, 0.01, 100.);
        let view = Transform3D::<f32>::look_at(
            Vector3D::new(0.0, 1.5, 9.0),
            Vector3D::new(0.0, 0.0, 0.0),
            Vector3D::new(0.0, 1.0, 0.0),
        );
        let view_proj = proj * view;

        self.rx += 0.01;
        self.ry += 0.03;

        let model = Transform3D::rotation(self.ry, Vector3D::new(0., 1.0, 0.))
            * Transform3D::<f32>::rotation(self.rx, Vector3D::new(1.0, 0., 0.));
        let vs_params = display_shader::Uniforms {
            mvp: view_proj * model,
        };
        let mut frame = self.backend.frame();

        // Offscreen pass.
        frame.begin_pass(
            self.offscreen_pass,
            gfx::PassAction::clear(gfx::color::Rgba::WHITE),
        );
        frame.apply_pipeline(&self.offscreen_pipeline)?;
        frame.apply_bindings(&self.offscreen_bind)?;
        frame.apply_uniforms(&vs_params)?;
        frame.draw(0, 36, 1)?;
        frame.end_pass();

        // Display pass.
        frame.begin_default_pass(gfx::PassAction::clear(gfx::color::Rgba::new(
            0.0, 0., 0.45, 1.,
        )));
        frame.apply_pipeline(&self.display_pipeline)?;
        frame.apply_bindings(&self.display_bind)?;
        frame.apply_uniforms(&vs_params)?;
        frame.draw(0, 36, 1)?;
        frame.end_pass();
        frame.commit();

        Ok(())
    }
}

impl<T: gfx::RenderBackend> Renderer<T> {
    pub fn init(mut backend: T) -> Result<Self, Box<dyn std::error::Error>> {
        let color_img = backend.texture(gfx::Texture {
            data: gfx::Empty,
            access: gfx::TextureAccess::RenderTarget,
            size: Size::new(1024, 1024),
            format: gfx::TextureFormat::Rgba8,
            ..gfx::Texture::default()
        })?;
        let depth_img = backend.texture(gfx::Texture {
            data: gfx::Empty,
            size: Size::new(1024, 1024),
            access: gfx::TextureAccess::RenderTarget,
            format: gfx::TextureFormat::Depth,
            ..gfx::Texture::default()
        })?;
        let offscreen_pass = backend.pass(gfx::PassDescriptor {
            color_attachment: Some(color_img),
            depth_attachment: Some(depth_img),
        })?;

        #[rustfmt::skip]
        let vertices: &[f32] = &[
            // position          color                   uvs
            -1.0, -1.0, -1.0,    1.0, 0.5, 0.5, 1.0,     0.0, 0.0,
             1.0, -1.0, -1.0,    1.0, 0.5, 0.5, 1.0,     1.0, 0.0,
             1.0,  1.0, -1.0,    1.0, 0.5, 0.5, 1.0,     1.0, 1.0,
            -1.0,  1.0, -1.0,    1.0, 0.5, 0.5, 1.0,     0.0, 1.0,

            -1.0, -1.0,  1.0,    0.5, 1.0, 0.5, 1.0,     0.0, 0.0,
             1.0, -1.0,  1.0,    0.5, 1.0, 0.5, 1.0,     1.0, 0.0,
             1.0,  1.0,  1.0,    0.5, 1.0, 0.5, 1.0,     1.0, 1.0,
            -1.0,  1.0,  1.0,    0.5, 1.0, 0.5, 1.0,     0.0, 1.0,

            -1.0, -1.0, -1.0,    0.5, 0.5, 1.0, 1.0,     0.0, 0.0,
            -1.0,  1.0, -1.0,    0.5, 0.5, 1.0, 1.0,     1.0, 0.0,
            -1.0,  1.0,  1.0,    0.5, 0.5, 1.0, 1.0,     1.0, 1.0,
            -1.0, -1.0,  1.0,    0.5, 0.5, 1.0, 1.0,     0.0, 1.0,

             1.0, -1.0, -1.0,    1.0, 0.5, 0.0, 1.0,     0.0, 0.0,
             1.0,  1.0, -1.0,    1.0, 0.5, 0.0, 1.0,     1.0, 0.0,
             1.0,  1.0,  1.0,    1.0, 0.5, 0.0, 1.0,     1.0, 1.0,
             1.0, -1.0,  1.0,    1.0, 0.5, 0.0, 1.0,     0.0, 1.0,

            -1.0, -1.0, -1.0,    0.0, 0.5, 1.0, 1.0,     0.0, 0.0,
            -1.0, -1.0,  1.0,    0.0, 0.5, 1.0, 1.0,     1.0, 0.0,
             1.0, -1.0,  1.0,    0.0, 0.5, 1.0, 1.0,     1.0, 1.0,
             1.0, -1.0, -1.0,    0.0, 0.5, 1.0, 1.0,     0.0, 1.0,

            -1.0,  1.0, -1.0,    1.0, 0.0, 0.5, 1.0,     0.0, 0.0,
            -1.0,  1.0,  1.0,    1.0, 0.0, 0.5, 1.0,     1.0, 0.0,
             1.0,  1.0,  1.0,    1.0, 0.0, 0.5, 1.0,     1.0, 1.0,
             1.0,  1.0, -1.0,    1.0, 0.0, 0.5, 1.0,     0.0, 1.0
        ];

        let vertex_buffer = backend.buffer(
            gfx::BufferKind::Vertex,
            gfx::BufferUsage::Immutable,
            gfx::BufferSource::slice(vertices),
        )?;

        #[rustfmt::skip]
        let indices: &[u8] = &[
            0, 1, 2,  0, 2, 3,
            6, 5, 4,  7, 6, 4,
            8, 9, 10,  8, 10, 11,
            14, 13, 12,  15, 14, 12,
            16, 17, 18,  16, 18, 19,
            22, 21, 20,  23, 22, 20
        ];

        let index_buffer = backend.buffer(
            gfx::BufferKind::Index,
            gfx::BufferUsage::Immutable,
            gfx::BufferSource::slice(indices),
        )?;

        let offscreen_bind = gfx::Bindings {
            vertex: vec![vertex_buffer],
            index: Some(index_buffer),
            textures: vec![],
        };

        let display_bind = gfx::Bindings {
            vertex: vec![vertex_buffer],
            index: Some(index_buffer),
            textures: vec![color_img],
        };

        let source = gfx::ShaderSource {
            vertex: display_shader::VERTEX,
            fragment: display_shader::FRAGMENT,
        };
        let default_shader = backend.shader(source, display_shader::meta()).unwrap();

        let display_pipeline = backend.pipeline(
            default_shader,
            gfx::PipelineDescriptor {
                layout: &[gfx::BufferLayout {
                    attrs: &[
                        gfx::VertexAttribute::new("in_pos", gfx::VertexFormat::Float3),
                        gfx::VertexAttribute::new("in_color", gfx::VertexFormat::Float4),
                        gfx::VertexAttribute::new("in_uv", gfx::VertexFormat::Float2),
                    ],
                    ..gfx::BufferLayout::default()
                }],
                depth: Some(gfx::DepthState {
                    compare: gfx::Comparison::LessOrEqual,
                    offset: None,
                }),
                ..Default::default()
            },
        );

        let source = gfx::ShaderSource {
            vertex: offscreen_shader::VERTEX,
            fragment: offscreen_shader::FRAGMENT,
        };
        let offscreen_shader = backend.shader(source, offscreen_shader::meta()).unwrap();
        let offscreen_pipeline = backend.pipeline(
            offscreen_shader,
            gfx::PipelineDescriptor {
                layout: &[gfx::BufferLayout {
                    stride: Some(36),
                    attrs: &[
                        gfx::VertexAttribute::new("in_pos", gfx::VertexFormat::Float3),
                        gfx::VertexAttribute::new("in_color", gfx::VertexFormat::Float4),
                    ],
                    ..Default::default()
                }],
                depth: Some(gfx::DepthState {
                    compare: gfx::Comparison::LessOrEqual,
                    offset: None,
                }),
                ..Default::default()
            },
        );

        Ok(Self {
            display_pipeline,
            display_bind,
            offscreen_pipeline,
            offscreen_bind,
            offscreen_pass,
            height: 1.,
            width: 1.,
            rx: 0.,
            ry: 0.,
            backend,
        })
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    logger::init(log::Level::Debug)?;

    let (mut win, events) =
        platform::init("offscreen", 640, 480, &[], platform::GraphicsContext::Gl)?;
    let ctx = unsafe { glow::Context::from_loader_function(|p| win.get_proc_address(p)) };
    let backend = gfx::gl::Context::new(ctx, win.size())?;
    let renderer = Renderer::init(backend)?;

    runtime::run(win, events, renderer)?;

    Ok(())
}

mod display_shader {
    use super::*;

    pub const VERTEX: &str = r#"
    #version 100

    attribute vec3 in_pos;
    attribute vec4 in_color;
    attribute vec2 in_uv;

    varying lowp vec4 color;
    varying lowp vec2 uv;

    uniform mat4 mvp;

    void main() {
        gl_Position = mvp * vec4(in_pos, 1);
        color = in_color;
        uv = in_uv;
    }
    "#;

    pub const FRAGMENT: &str = r#"
    #version 100

    varying lowp vec4 color;
    varying lowp vec2 uv;

    uniform sampler2D tex;

    void main() {
        gl_FragColor = color * texture2D(tex, uv);
    }
    "#;

    pub fn meta() -> gfx::ShaderDescriptor {
        gfx::ShaderDescriptor {
            textures: vec!["tex".to_string()],
            uniforms: gfx::UniformBlockLayout {
                uniforms: vec![gfx::UniformDescriptor::new("mvp", gfx::UniformKind::Mat4)],
            },
        }
    }

    #[repr(C)]
    pub struct Uniforms {
        pub mvp: Transform3D,
    }
    unsafe impl bytes::Packed for Uniforms {}
}

mod offscreen_shader {
    use super::*;

    pub const VERTEX: &str = r#"
    #version 100

    attribute vec3 in_pos;
    attribute vec4 in_color;

    varying lowp vec4 color;

    uniform mat4 mvp;

    void main() {
        gl_Position = mvp * vec4(in_pos, 1);
        color = in_color;
    }
    "#;

    pub const FRAGMENT: &str = r#"
    #version 100

    varying lowp vec4 color;

    void main() {
        gl_FragColor = color;
    }
    "#;

    pub fn meta() -> gfx::ShaderDescriptor {
        gfx::ShaderDescriptor {
            textures: vec![],
            uniforms: gfx::UniformBlockLayout {
                uniforms: vec![gfx::UniformDescriptor::new("mvp", gfx::UniformKind::Mat4)],
            },
        }
    }
}