diff options
| -rw-r--r-- | crates/teleia/src/assets/shaders/common/postprocessing_vert.glsl (renamed from crates/teleia/src/assets/shaders/scale/vert.glsl) | 0 | ||||
| -rw-r--r-- | crates/teleia/src/framebuffer.rs | 4 | ||||
| -rw-r--r-- | crates/teleia/src/lib.rs | 9 | ||||
| -rw-r--r-- | crates/teleia/src/postprocessing.rs | 75 | ||||
| -rw-r--r-- | crates/teleia/src/renderer.rs | 10 | ||||
| -rw-r--r-- | crates/teleia/src/scene.rs | 1 | ||||
| -rw-r--r-- | crates/teleia/src/shader.rs | 2 | ||||
| -rw-r--r-- | crates/teleia/src/state.rs | 18 | ||||
| -rw-r--r-- | crates/teleia_macros/src/lib.rs | 12 |
9 files changed, 105 insertions, 26 deletions
diff --git a/crates/teleia/src/assets/shaders/scale/vert.glsl b/crates/teleia/src/assets/shaders/common/postprocessing_vert.glsl index e05bbb6..e05bbb6 100644 --- a/crates/teleia/src/assets/shaders/scale/vert.glsl +++ b/crates/teleia/src/assets/shaders/common/postprocessing_vert.glsl diff --git a/crates/teleia/src/framebuffer.rs b/crates/teleia/src/framebuffer.rs index cfc8ac1..0d6a9ec 100644 --- a/crates/teleia/src/framebuffer.rs +++ b/crates/teleia/src/framebuffer.rs @@ -71,8 +71,8 @@ impl Framebuffer { glow::UNSIGNED_BYTE, None, ); - ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::CLAMP_TO_EDGE as _); - ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::CLAMP_TO_EDGE as _); + ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::REPEAT as _); + ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::REPEAT as _); ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::NEAREST as _); ctx.gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::NEAREST as _); ctx.gl.framebuffer_texture_2d(glow::FRAMEBUFFER, glow::COLOR_ATTACHMENT0, glow::TEXTURE_2D, Some(tex), 0); diff --git a/crates/teleia/src/lib.rs b/crates/teleia/src/lib.rs index 85b28c4..0f77e82 100644 --- a/crates/teleia/src/lib.rs +++ b/crates/teleia/src/lib.rs @@ -6,6 +6,7 @@ pub mod context; pub mod state; pub mod framebuffer; pub mod shader; +pub mod postprocessing; pub mod mesh; pub mod texture; pub mod scene; @@ -67,7 +68,7 @@ where pub fn run<'a, F, G>(title: &str, w: u32, h: u32, options: Options, gnew: F) -> Erm<()> where G: state::Game + 'static, - F: (FnOnce(&'a context::Context) -> G), + F: (FnOnce(&'a context::Context, &mut state::State) -> G), { env_logger::Builder::new() .filter(None, log::LevelFilter::Info) @@ -132,8 +133,8 @@ where glfw, window, gl, w as f32, h as f32, options, ))); - let game = Box::leak(Box::new(gnew(ctx))); let st = Box::leak(Box::new(state::State::new(ctx))); + let game = Box::leak(Box::new(gnew(ctx, st))); unsafe { CTX = Some(ctx as _); @@ -200,7 +201,7 @@ where pub fn run<'a, F, G>(w: u32, h: u32, options: Options, gnew: F) where G: state::Game + 'static, - F: (Fn(&'a context::Context) -> G), + F: (Fn(&'a context::Context, &mut state::State) -> G), { console_log::init_with_level(log::Level::Debug).unwrap(); console_error_panic_hook::set_once(); @@ -240,8 +241,8 @@ where let ctx = Box::leak(Box::new(context::Context::new(window, gl, w as f32, h as f32, options))); ctx.maximize_canvas(); - let game = Box::leak(Box::new(gnew(ctx))); let st = Box::leak(Box::new(state::State::new(&ctx))); + let game = Box::leak(Box::new(gnew(ctx, st))); unsafe { CTX = Some(ctx as _); diff --git a/crates/teleia/src/postprocessing.rs b/crates/teleia/src/postprocessing.rs new file mode 100644 index 0000000..b5e8165 --- /dev/null +++ b/crates/teleia/src/postprocessing.rs @@ -0,0 +1,75 @@ +use crate::{context, framebuffer, shader, state, utils}; + +#[derive(Clone, Copy)] +pub struct Effect(usize); +pub struct Pipeline { + pub effects: Vec<shader::Shader>, + pub upscale: shader::Shader, + pub stages: Vec<Effect>, + pub framebuffer: framebuffer::Framebuffer, +} +impl Pipeline { + pub fn new(ctx: &context::Context) -> Self { + Self { + effects: Vec::new(), + upscale: shader::Shader::new_nolib( + ctx, + include_str!("assets/shaders/common/postprocessing_vert.glsl"), + include_str!("assets/shaders/scale/frag.glsl"), + ), + stages: Vec::new(), + framebuffer: framebuffer::Framebuffer::new( + ctx, + &glam::Vec2::new(ctx.render_width, ctx.render_height), + &glam::Vec2::new(0.0, 0.0), + ), + } + } + /// Create a new postprocessing effect from a fragment shader. + pub fn effect(&mut self, ctx: &context::Context, fsrc: &str) -> utils::Erm<Effect> { + let shader = shader::Shader::new_nolib(ctx, + include_str!("assets/shaders/common/postprocessing_vert.glsl"), + fsrc + ); + let idx = self.effects.len(); + self.effects.push(shader); + Ok(Effect(idx)) + } + /// Apply a postprocessing effect to the current frame. + pub fn apply(&mut self, effect: Effect) { + self.stages.push(effect); + } + /// Upscale the image in the state's rendering framebuffer to the screen, + /// applying postprocessing effects in between. + pub fn render(&self, ctx: &context::Context, st: &state::State) -> utils::Erm<()> { + let mut src = &st.render_framebuffer; + let mut dst = &self.framebuffer; + for Effect(e) in self.stages.iter() { + let s = &self.effects[*e]; + dst.bind(ctx); + ctx.clear(); + s.bind(ctx); + s.set_f32(ctx, "time", st.tick as f32 + (st.tick % 60) as f32 / 60.0); + src.bind_texture(ctx); + ctx.render_no_geometry(); + std::mem::swap(&mut src, &mut dst); + } + st.screen.bind(ctx); + ctx.clear_color( + if ctx.options.contains(crate::Options::OVERLAY) { + glam::Vec4::new(0.0, 0.0, 0.0, 0.0) + } else { + glam::Vec4::new(0.0, 0.0, 0.0, 1.0) + } + ); + ctx.clear(); + self.upscale.bind(ctx); + src.bind_texture(ctx); + ctx.render_no_geometry(); + Ok(()) + } + /// Conclude the current frame, and clear all staged effects. + pub fn finish(&mut self) { + self.stages.clear(); + } +} diff --git a/crates/teleia/src/renderer.rs b/crates/teleia/src/renderer.rs index 2b5e747..32ce016 100644 --- a/crates/teleia/src/renderer.rs +++ b/crates/teleia/src/renderer.rs @@ -1,4 +1,4 @@ -use crate::{context, state, shader, texture, mesh}; +use crate::{context, mesh, postprocessing, shader, state, texture}; use bitflags::bitflags; @@ -40,6 +40,8 @@ pub trait Assets { fn mesh(&self, i: Self::Mesh) -> &mesh::Mesh; type Material: PartialEq + Eq + Clone + Copy; fn material(&self, i: Self::Material) -> &texture::Material; + type Effect: PartialEq + Eq + Clone + Copy; + fn effect(&self, i: Self::Effect) -> &postprocessing::Effect; } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -75,8 +77,8 @@ pub struct Renderer<A: Assets> { texture: BoundTexture<A>, } impl<A: Assets> Renderer<A> { - pub fn new<F>(ctx: &context::Context, f: F) -> Self - where F: FnOnce(&context::Context) -> A { + pub fn new<F>(ctx: &context::Context, st: &mut state::State, f: F) -> Self + where F: FnOnce(&context::Context, &mut state::State) -> A { let shader_uber = shader::Shader::new_nolib(ctx, &format!("{}{}", UberFlags::prelude(), include_str!("assets/shaders/uber/vert.glsl")), &format!("{}{}", UberFlags::prelude(), include_str!("assets/shaders/uber/frag.glsl")), @@ -84,7 +86,7 @@ impl<A: Assets> Renderer<A> { shader_uber.bind(ctx); shader_uber.set_i32(ctx, "texture_normal", 1); Self { - assets: f(ctx), + assets: f(ctx, st), shader_uber, shader: BoundShader::None, texture: BoundTexture::None, diff --git a/crates/teleia/src/scene.rs b/crates/teleia/src/scene.rs index d00f098..26615fb 100644 --- a/crates/teleia/src/scene.rs +++ b/crates/teleia/src/scene.rs @@ -195,7 +195,6 @@ impl Scene { ctx.gl.enable_vertex_attrib_array(mesh::ATTRIB_WEIGHT); ctx.gl.vertex_attrib_pointer_f32(mesh::ATTRIB_WEIGHT, 4, glow::FLOAT, false, vertex_size, offset_of!(Vertex, weights) as _); - Primitive { mesh: mesh::Mesh { vao, diff --git a/crates/teleia/src/shader.rs b/crates/teleia/src/shader.rs index 6248658..7c60790 100644 --- a/crates/teleia/src/shader.rs +++ b/crates/teleia/src/shader.rs @@ -218,7 +218,7 @@ impl Shader { self.set_mat4(ctx, "normal_matrix", &position.inverse().transpose()); } - pub fn set_position_2d_mat(&self, ctx: &context::Context, st: &state::State, position: &glam::Mat4) { + pub fn set_position_2d_mat(&self, ctx: &context::Context, _st: &state::State, position: &glam::Mat4) { self.set_mat4(ctx, "position", position); } diff --git a/crates/teleia/src/state.rs b/crates/teleia/src/state.rs index 8f29d15..d4c9742 100644 --- a/crates/teleia/src/state.rs +++ b/crates/teleia/src/state.rs @@ -8,11 +8,11 @@ use strum::EnumIter; #[cfg(not(target_arch = "wasm32"))] use glow::HasContext; -use crate::{audio, context, font, framebuffer, mesh, shader, utils}; +use crate::{audio, context, font, framebuffer, mesh, postprocessing, shader, utils}; pub type Tick = u64; -const DELTA_TIME: f64 = 0.016; // todo +pub const DELTA_TIME: f64 = 0.016; // todo pub const ORTH_WIDTH: f32 = 7.55869; pub const ORTH_HEIGHT: f32 = 5.03913; @@ -162,7 +162,7 @@ pub struct State { pub screen: framebuffer::Framebuffer, pub render_framebuffer: framebuffer::Framebuffer, pub render_dims: glam::Vec2, - pub shader_upscale: shader::Shader, + pub postprocessing: postprocessing::Pipeline, pub shader_text_bitmap: shader::Shader, pub mesh_square: mesh::Mesh, pub font_default: font::Bitmap, @@ -234,11 +234,6 @@ impl State { &glam::Vec2::new(ctx.render_width, ctx.render_height), &glam::Vec2::new(0.0, 0.0), ); - let shader_upscale = shader::Shader::new_nolib( - ctx, - include_str!("assets/shaders/scale/vert.glsl"), - include_str!("assets/shaders/scale/frag.glsl"), - ); let shader_text_bitmap = shader::Shader::new_nolib( ctx, include_str!("assets/shaders/bitmap/vert.glsl"), @@ -265,7 +260,7 @@ impl State { screen, render_framebuffer, render_dims: glam::Vec2::new(ctx.render_width, ctx.render_height), - shader_upscale, + postprocessing: postprocessing::Pipeline::new(ctx), shader_text_bitmap, mesh_square, font_default: font::Bitmap::default(ctx), @@ -568,9 +563,8 @@ impl State { } ); ctx.clear(); - self.shader_upscale.bind(ctx); - self.render_framebuffer.bind_texture(ctx); - ctx.render_no_geometry(); + self.postprocessing.render(ctx, self)?; + self.postprocessing.finish(); #[cfg(all(debug_assertions, not(target_arch = "wasm32")))] { let err = unsafe { ctx.gl.get_error() }; diff --git a/crates/teleia_macros/src/lib.rs b/crates/teleia_macros/src/lib.rs index c780154..af67541 100644 --- a/crates/teleia_macros/src/lib.rs +++ b/crates/teleia_macros/src/lib.rs @@ -32,6 +32,7 @@ impl Designator { "textures" => "texture", "materials" => "material", "shaders" => "shader", + "effects" => "effect", _ => fnm, }; let mut ret = format!("\n#[doc=\"newton-{}: {}\"]\n", singular, self.path); @@ -55,6 +56,8 @@ impl Designator { } else { Some(format!("teleia::shader::Shader::new(ctx, include_str!(\"{}/vert.glsl\"), include_str!(\"{}/frag.glsl\"))", i, i)) }, + "effects" => + Some(format!("st.effect(ctx, include_bytes!(\"{}.glsl\"))", i)), _ => None, } } @@ -94,6 +97,7 @@ impl Field { "textures" => ("Texture", "teleia::texture::Texture"), "materials" => ("Material", "teleia::texture::Material"), "shaders" => ("Shader", "teleia::shader::Shader"), + "effects" => ("Effect", "teleia::postprocessing::Effect"), _ => return None, }; let enums: Vec<_> = ents.iter().map(|(e, _, _)| e.clone()).collect(); @@ -114,7 +118,7 @@ impl AssetData { fn new(base: &str) -> Self { let mut fields = Vec::new(); let dirs = std::fs::read_dir(base).unwrap_or_else(|_| panic!("failed to read assets directory: {}", base)); - let (mut has_meshes, mut has_textures, mut has_materials, mut has_shaders) = (false, false, false, false); + let (mut has_meshes, mut has_textures, mut has_materials, mut has_shaders, mut has_effects) = (false, false, false, false, false); for d in dirs.flatten() { let nm = d.file_name().into_string().unwrap(); fields.push(Field::new(base, &nm)); @@ -123,6 +127,7 @@ impl AssetData { "textures" => has_textures = true, "materials" => has_materials = true, "shaders" => has_shaders = true, + "effects" => has_effects = true, _ => {}, }; } @@ -130,6 +135,7 @@ impl AssetData { if !has_textures { fields.push(Field { nm: "textures".to_owned(), entries: HashSet::new() }); } if !has_materials { fields.push(Field { nm: "materials".to_owned(), entries: HashSet::new() }); } if !has_shaders { fields.push(Field { nm: "shaders".to_owned(), entries: HashSet::new() }); } + if !has_effects { fields.push(Field { nm: "effects".to_owned(), entries: HashSet::new() }); } Self { fields, } @@ -144,7 +150,7 @@ impl AssetData { for (_, decl, _) in fdata.iter() { res += decl; res += ",\n"; } - res += "}\nimpl Assets {\npub fn new(ctx: &teleia::context::Context) -> Self {\nSelf {\n"; + res += "}\nimpl Assets {\npub fn new(ctx: &teleia::context::Context, st: &mut teleia::state::State) -> Self {\nSelf {\n"; for (_, _, init) in fdata.iter() { res += init; res += ",\n"; } @@ -158,6 +164,8 @@ impl AssetData { res += "fn material(&self, i: Self::Material) -> &teleia::texture::Material { &self.materials[i] }\n"; res += "type Mesh = Mesh;\n"; res += "fn mesh(&self, i: Self::Mesh) -> &teleia::mesh::Mesh { &self.meshes[i] }\n"; + res += "type Effect = Effect;\n"; + res += "fn effect(&self, i: Self::Effect) -> &teleia::postprocessing::Effect { &self.effects[i] }\n"; res += "}\n"; res } |
