#![allow(clippy::unwrap_used)] use lueur::prelude::*; struct Renderer { display_pipeline: gfx::PipelineId, display_bind: gfx::Bindings<1, 1>, offscreen_pipeline: gfx::PipelineId, offscreen_bind: gfx::Bindings<1, 0>, offscreen_pass: gfx::PassId, rx: f32, ry: f32, width: f64, height: f64, backend: T, } impl ApplicationExt for Renderer { type Error = T::Error; fn window_resized(&mut self, size: platform::LogicalSize) -> Result<(), Self::Error> { self.width = size.width; self.height = size.height; self.backend.window_resized(size); Ok(()) } fn present(&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::::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::::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.)), [self.width as u32, self.height as u32], ); 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 Renderer { pub fn init(mut backend: T) -> Result> { 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_attachments: &[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: [vertex_buffer], index: Some(index_buffer), textures: [], }; let display_bind = gfx::Bindings { vertex: [vertex_buffer], index: Some(index_buffer), textures: [color_img], }; let source = gfx::ShaderSource { vertex: display_shader::VERTEX, fragment: display_shader::FRAGMENT, geometry: None, }; 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, geometry: None, }; 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> { 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)?; lueur::runtime::default::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<'a>() -> gfx::ShaderDescriptor<'a> { gfx::ShaderDescriptor { textures: &["tex"], uniforms: gfx::UniformBlockLayout { uniforms: &[gfx::UniformDescriptor { name: "mvp", kind: gfx::UniformKind::Mat4, arity: 1, }], }, } } #[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<'static> { gfx::ShaderDescriptor { textures: &[], uniforms: gfx::UniformBlockLayout { uniforms: &[gfx::UniformDescriptor { name: "mvp", kind: gfx::UniformKind::Mat4, arity: 1, }], }, } } }