#[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();
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();
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] = &[
-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)],
},
}
}
}