summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.rs4
-rw-r--r--crates/teleia/src/lib.rs9
-rw-r--r--crates/teleia/src/postprocessing.rs75
-rw-r--r--crates/teleia/src/renderer.rs10
-rw-r--r--crates/teleia/src/scene.rs1
-rw-r--r--crates/teleia/src/shader.rs2
-rw-r--r--crates/teleia/src/state.rs18
-rw-r--r--crates/teleia_macros/src/lib.rs12
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
}