summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLLLL Colonq <llll@colonq>2026-05-06 14:54:58 -0400
committerLLLL Colonq <llll@colonq>2026-05-06 14:54:58 -0400
commitcdf8fbe731c41071025dd17080474c935cee75ef (patch)
tree068270936c354a0451ab84d7a05c5b3f6a3333be
parent27844e551d8b91c0b953e9f534440ab937310f37 (diff)
-rw-r--r--crates/teleia/src/assets/fonts/small.pngbin0 -> 904 bytes
-rw-r--r--crates/teleia/src/assets/shaders/uber/frag.glsl4
-rw-r--r--crates/teleia/src/assets/shaders/uber/vert.glsl3
-rw-r--r--crates/teleia/src/context.rs14
-rw-r--r--crates/teleia/src/font.rs61
-rw-r--r--crates/teleia/src/lib.rs12
-rw-r--r--crates/teleia/src/renderer.rs53
-rw-r--r--crates/teleia/src/state.rs60
-rw-r--r--crates/teleia/src/ui.rs12
9 files changed, 121 insertions, 98 deletions
diff --git a/crates/teleia/src/assets/fonts/small.png b/crates/teleia/src/assets/fonts/small.png
new file mode 100644
index 0000000..1d020f7
--- /dev/null
+++ b/crates/teleia/src/assets/fonts/small.png
Binary files differ
diff --git a/crates/teleia/src/assets/shaders/uber/frag.glsl b/crates/teleia/src/assets/shaders/uber/frag.glsl
index ebe6438..6778156 100644
--- a/crates/teleia/src/assets/shaders/uber/frag.glsl
+++ b/crates/teleia/src/assets/shaders/uber/frag.glsl
@@ -23,6 +23,7 @@ uniform float effect_hueshift;
uniform float effect_huescale;
in vec2 vertex_texcoord;
+in vec3 vertex_color;
in vec3 vertex_normal;
in vec3 vertex_fragpos;
in vec4 vertex_fragpos_shadow_dir;
@@ -186,4 +187,7 @@ void main() {
if (frag_color.a == 0.0) {
discard;
}
+ if (flag(VERTEX_COLOR)) {
+ frag_color.rgb = vertex_color;
+ }
}
diff --git a/crates/teleia/src/assets/shaders/uber/vert.glsl b/crates/teleia/src/assets/shaders/uber/vert.glsl
index 6de3e7e..8b4ef21 100644
--- a/crates/teleia/src/assets/shaders/uber/vert.glsl
+++ b/crates/teleia/src/assets/shaders/uber/vert.glsl
@@ -3,6 +3,7 @@ uniform highp int flags;
in vec3 vertex;
in vec3 normal;
in vec2 texcoord;
+in vec3 color;
uniform mat4 view;
uniform mat4 position;
@@ -14,6 +15,7 @@ uniform vec3 offset;
uniform float yskew;
out vec2 vertex_texcoord;
+out vec3 vertex_color;
out vec3 vertex_normal;
out vec3 vertex_fragpos;
out vec4 vertex_fragpos_shadow_dir;
@@ -25,6 +27,7 @@ bool flag(int mask) {
void main() {
vertex_texcoord = texcoord;
+ vertex_color = color;
vertex_normal = (normal_matrix * vec4(normal, 1.0)).xyz;
vec3 pos = (position * vec4(vertex, 1.0)).xyz;
vertex_fragpos = pos;
diff --git a/crates/teleia/src/context.rs b/crates/teleia/src/context.rs
index 211e23b..d8c6aea 100644
--- a/crates/teleia/src/context.rs
+++ b/crates/teleia/src/context.rs
@@ -201,6 +201,13 @@ impl Context {
}
}
+ pub fn write_and_use_stencil(&self) {
+ unsafe {
+ self.gl.stencil_func(glow::EQUAL, 1, 0xff);
+ self.gl.stencil_op(glow::KEEP, glow::KEEP, glow::REPLACE);
+ }
+ }
+
pub fn use_inverse_stencil(&self) {
unsafe {
self.gl.stencil_func(glow::NOTEQUAL, 1, 0xff);
@@ -208,6 +215,13 @@ impl Context {
}
}
+ pub fn write_and_use_inverse_stencil(&self) {
+ unsafe {
+ self.gl.stencil_func(glow::NOTEQUAL, 1, 0xff);
+ self.gl.stencil_op(glow::KEEP, glow::KEEP, glow::REPLACE);
+ }
+ }
+
pub fn end_stencil(&self) {
unsafe {
self.gl.stencil_func(glow::ALWAYS, 1, 0xff);
diff --git a/crates/teleia/src/font.rs b/crates/teleia/src/font.rs
index 0587556..2fe7423 100644
--- a/crates/teleia/src/font.rs
+++ b/crates/teleia/src/font.rs
@@ -1,16 +1,16 @@
-use crate::{context, mesh, shader, state, texture};
+use crate::{context, mesh, state, texture};
use glow::HasContext;
pub struct BitmapParams<'color> {
pub color: &'color [glam::Vec3],
pub scale: glam::Vec2,
+ pub offset: glam::Vec2,
}
pub struct Bitmap {
pub char_width: i32,
pub char_height: i32,
pub font_width: i32,
pub font_height: i32,
- pub shader: shader::Shader,
pub font: texture::Texture,
pub vao: glow::VertexArray,
pub vertex_buf: glow::Buffer,
@@ -26,11 +26,6 @@ impl Bitmap {
font_width: i32, font_height: i32,
data: &[u8],
) -> Self {
- let shader = shader::Shader::new_nolib(
- &ctx,
- include_str!("assets/shaders/bitmap/vert.glsl"),
- include_str!("assets/shaders/bitmap/frag.glsl"),
- );
let font = texture::Texture::new(ctx, data);
unsafe {
let vao = ctx.gl.create_vertex_array().expect("failed to initialize vao");
@@ -52,7 +47,6 @@ impl Bitmap {
Self {
char_width, char_height,
font_width, font_height,
- shader,
font,
vao,
vertex_buf,
@@ -67,20 +61,23 @@ impl Bitmap {
Self::from_image(ctx, 7, 9, 112, 54, include_bytes!("assets/fonts/default.png"))
}
+ pub fn small(ctx: &context::Context) -> Self {
+ Self::from_image(ctx, 6, 7, 96, 42, include_bytes!("assets/fonts/small.png"))
+ }
+
pub fn render_text_parameterized(&self,
ctx: &context::Context, st: &state::State,
- pos: &glam::Vec2, text: &str,
+ text: &str,
params: BitmapParams,
) {
- let fpos = pos.floor();
- let mut cur = glam::Vec2::new(0.0, 0.0);
+ let mut cur = params.offset;
let mut vertices = Vec::new();
let mut texcoords = Vec::new();
let mut colors = Vec::new();
let mut indices = Vec::new();
let cwidth = self.char_width as f32 / self.font_width as f32;
let cheight = self.char_height as f32 / self.font_height as f32;
- let sdims = glam::Vec2::new(self.char_width as f32, self.char_height as f32) * params.scale;
+ let sdims = glam::Vec2::new(2.0, 2.0) * params.scale;
let row_len = self.font_width as u32 / self.char_width as u32;
for (i, c) in text.chars().enumerate() {
if c == '\n' {
@@ -112,22 +109,7 @@ impl Bitmap {
}
}
let index_bytes: Vec<u8> = indices.iter().flat_map(|x| x.to_ne_bytes()).collect();
- self.shader.bind(ctx);
self.font.bind(ctx);
- let scale = glam::Vec2::new(2.0 / st.render_dims.x, 2.0 / st.render_dims.y);
- let offset = glam::Vec2::new(
- -st.render_dims.x / 2.0,
- st.render_dims.y / 2.0 - sdims.y,
- );
- let npos = (glam::Vec2::new(fpos.x, -fpos.y) + offset) * scale;
- self.shader.set_mat4(
- ctx, "transform",
- &glam::Mat4::from_scale_rotation_translation(
- glam::Vec3::new(scale.x, scale.y, 1.0),
- glam::Quat::IDENTITY,
- glam::Vec3::new(npos.x, npos.y, 0.0),
- ),
- );
unsafe {
ctx.gl.bind_vertex_array(Some(self.vao));
ctx.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buf));
@@ -167,14 +149,25 @@ impl Bitmap {
}
}
- pub fn render_text_helper(&self, ctx: &context::Context, st: &state::State, pos: &glam::Vec2, text: &str, color: &[glam::Vec3]) {
- self.render_text_parameterized(ctx, st, pos, text, BitmapParams {
- color,
- scale: glam::Vec2::ONE,
- })
+ pub fn render_text_helper(&self,
+ ctx: &context::Context, st: &state::State,
+ text: &str, color: &[glam::Vec3]
+ ) {
+ self.render_text_parameterized(
+ ctx, st,
+ text,
+ BitmapParams {
+ color,
+ scale: glam::Vec2::ONE,
+ offset: glam::Vec2::ZERO,
+ }
+ )
}
- pub fn render_text(&self, ctx: &context::Context, st: &state::State, pos: &glam::Vec2, text: &str) {
- self.render_text_helper(ctx, st, pos, text, &[]);
+ pub fn render_text(&self,
+ ctx: &context::Context, st: &state::State,
+ text: &str
+ ) {
+ self.render_text_helper(ctx, st, text, &[]);
}
}
diff --git a/crates/teleia/src/lib.rs b/crates/teleia/src/lib.rs
index 21f3b24..9a55d10 100644
--- a/crates/teleia/src/lib.rs
+++ b/crates/teleia/src/lib.rs
@@ -168,13 +168,13 @@ where
st.mouse_pressed(&ctx, game)?;
},
glfw::WindowEvent::MouseButton(_, glfw::Action::Release, _) => {
- st.mouse_released(&ctx)
+ st.mouse_released(&ctx, game)?;
},
glfw::WindowEvent::Key(key, _, glfw::Action::Press, _) => {
- st.key_pressed(&ctx, state::Keycode::new(key))
+ st.key_pressed(&ctx, game, state::Keycode::new(key))?;
},
glfw::WindowEvent::Key(key, _, glfw::Action::Release, _) => {
- st.key_released(&ctx, state::Keycode::new(key))
+ st.key_released(&ctx, game, state::Keycode::new(key))?;
},
_ => {},
}
@@ -283,7 +283,7 @@ where
st.mouse_pressed(&ctx, game)?;
},
winit::event::ElementState::Released => {
- st.mouse_released(&ctx)
+ st.mouse_released(&ctx)?;
},
}
winit::event::WindowEvent::KeyboardInput {
@@ -296,10 +296,10 @@ where
..
} => match state {
winit::event::ElementState::Pressed => {
- st.key_pressed(&ctx, state::Keycode { kc: *key })
+ st.key_pressed(&ctx, state::Keycode { kc: *key })?;
},
winit::event::ElementState::Released => {
- st.key_released(&ctx, state::Keycode { kc: *key })
+ st.key_released(&ctx, state::Keycode { kc: *key })?;
},
}
_ => {},
diff --git a/crates/teleia/src/renderer.rs b/crates/teleia/src/renderer.rs
index 94c78d8..19dd8a0 100644
--- a/crates/teleia/src/renderer.rs
+++ b/crates/teleia/src/renderer.rs
@@ -5,15 +5,16 @@ use bitflags::bitflags;
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UberFlags: u32 {
- const TEXTURE_COLOR = 0b000000001;
- const TEXTURE_NORMAL = 0b000000010;
- const FLIP_TEXTURE = 0b000000100;
- const LIGHT_AMBIENT = 0b000001000;
- const LIGHT_DIR = 0b000010000;
- const LIGHT_POINT = 0b000100000;
- const SPRITE = 0b001000000;
- const EFFECTS = 0b010000000;
- const YSKEW = 0b100000000;
+ const TEXTURE_COLOR = 0b0000000001;
+ const TEXTURE_NORMAL = 0b0000000010;
+ const FLIP_TEXTURE = 0b0000000100;
+ const LIGHT_AMBIENT = 0b0000001000;
+ const LIGHT_DIR = 0b0000010000;
+ const LIGHT_POINT = 0b0000100000;
+ const SPRITE = 0b0001000000;
+ const EFFECTS = 0b0010000000;
+ const YSKEW = 0b0100000000;
+ const VERTEX_COLOR = 0b1000000000;
}
}
impl UberFlags {
@@ -91,6 +92,9 @@ impl<A: Assets> Renderer<A> {
}
pub fn font_char_width(&self, st: &state::State) -> f32 { st.font_default.char_width as f32 }
pub fn font_char_height(&self, st: &state::State) -> f32 { st.font_default.char_height as f32 }
+ pub fn unbind_texture(&mut self, _ctx: &context::Context, _st: &mut state::State) {
+ self.texture = BoundTexture::None;
+ }
pub fn bind_texture(&mut self, ctx: &context::Context, _st: &mut state::State, texture: A::Texture) {
if self.texture != BoundTexture::Texture(texture) {
self.assets.texture(texture).bind(ctx);
@@ -272,9 +276,13 @@ impl<A: Assets> Renderer<A> {
pos: glam::Vec2,
s: &str,
) {
- // drawing text might bind the shader and texture
- self.shader = BoundShader::None; self.texture = BoundTexture::None;
- st.font_default.render_text(ctx, st, &pos, s);
+ // drawing text might bind the texture
+ self.texture = BoundTexture::None;
+ self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::VERTEX_COLOR | UberFlags::FLIP_TEXTURE);
+ let dims = glam::Vec2::new(st.font_default.char_width as f32, st.font_default.char_height as f32);
+ let fpos = (pos + glam::Vec2::new(-dims.x / 2.0, st.font_default.char_height as f32 / 2.0));
+ self.set_position_2d(ctx, st, fpos, dims);
+ st.font_default.render_text(ctx, st, s);
}
/// Common case: text in the default font, with a color (units are pixels, pos is top left)
@@ -284,9 +292,12 @@ impl<A: Assets> Renderer<A> {
col: glam::Vec3,
s: &str,
) {
- // drawing text might bind the shader and texture
- self.shader = BoundShader::None; self.texture = BoundTexture::None;
- st.font_default.render_text_helper(ctx, st, &pos, s, &[col]);
+ self.texture = BoundTexture::None;
+ self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::VERTEX_COLOR | UberFlags::FLIP_TEXTURE);
+ let dims = glam::Vec2::new(st.font_default.char_width as f32, st.font_default.char_height as f32);
+ let fpos = (pos + glam::Vec2::new(-dims.x / 2.0, st.font_default.char_height as f32 / 2.0));
+ self.set_position_2d(ctx, st, fpos, dims);
+ st.font_default.render_text_helper(ctx, st, s, &[col]);
}
/// Common case: text in the default font (units are pixels, pos is center)
@@ -295,13 +306,13 @@ impl<A: Assets> Renderer<A> {
pos: glam::Vec2,
s: &str,
) {
- // drawing text might bind the shader and texture
- self.shader = BoundShader::None; self.texture = BoundTexture::None;
+ self.texture = BoundTexture::None;
+ self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::VERTEX_COLOR | UberFlags::FLIP_TEXTURE);
let width = s.len() as f32 * st.font_default.char_width as f32;
let height = st.font_default.char_height as f32;
- st.font_default.render_text(ctx, st,
- &(pos - glam::Vec2::new((width / 2.0).round(), (height / 2.0).round())),
- s
- );
+ let dims = glam::Vec2::new(st.font_default.char_width as f32, st.font_default.char_height as f32);
+ let fpos = (pos + glam::Vec2::new(-dims.x / 2.0 - (width / 2.0).round(), st.font_default.char_height as f32 / 2.0));
+ self.set_position_2d(ctx, st, fpos, dims);
+ st.font_default.render_text(ctx, st, s);
}
}
diff --git a/crates/teleia/src/state.rs b/crates/teleia/src/state.rs
index b7b8899..f5b6313 100644
--- a/crates/teleia/src/state.rs
+++ b/crates/teleia/src/state.rs
@@ -23,6 +23,7 @@ pub trait Game {
HashMap::new()
}
fn finish_title(&mut self, ctx: &context::Context, st: &mut State) -> utils::Erm<()> { Ok(()) }
+ fn keybindings_were_reset(&mut self, ctx: &context::Context, st: &mut State) -> utils::Erm<()> { Ok(()) }
fn mouse_move(&mut self, ctx: &context::Context, st: &mut State, x: i32, y: i32) -> utils::Erm<()> { Ok(()) }
fn mouse_press(&mut self, ctx: &context::Context, st: &mut State) -> utils::Erm<()> { Ok(()) }
fn update(&mut self, ctx: &context::Context, st: &mut State) -> utils::Erm<()> { Ok(()) }
@@ -72,19 +73,19 @@ impl Keys {
pub fn start(&self) -> bool { self.pressed[Key::Start] }
pub fn select(&self) -> bool { self.pressed[Key::Select] }
pub fn debug(&self) -> bool { self.pressed[Key::Debug] }
- pub fn new_up(&self) -> bool { self.new[Key::Up] }
- pub fn new_down(&self) -> bool { self.new[Key::Down] }
- pub fn new_left(&self) -> bool { self.new[Key::Left] }
- pub fn new_right(&self) -> bool { self.new[Key::Right] }
- pub fn new_a(&self) -> bool { self.new[Key::A] }
- pub fn new_b(&self) -> bool { self.new[Key::B] }
- pub fn new_x(&self) -> bool { self.new[Key::X] }
- pub fn new_y(&self) -> bool { self.new[Key::Y] }
- pub fn new_l(&self) -> bool { self.new[Key::L] }
- pub fn new_r(&self) -> bool { self.new[Key::R] }
- pub fn new_start(&self) -> bool { self.new[Key::Start] }
- pub fn new_select(&self) -> bool { self.new[Key::Select] }
- pub fn new_debug(&self) -> bool { self.new[Key::Debug] }
+ pub fn new_up(&mut self) -> bool { let ret = self.new[Key::Up]; self.new[Key::Up] = false; ret }
+ pub fn new_down(&mut self) -> bool { let ret = self.new[Key::Down]; self.new[Key::Down] = false; ret }
+ pub fn new_left(&mut self) -> bool { let ret = self.new[Key::Left]; self.new[Key::Left] = false; ret }
+ pub fn new_right(&mut self) -> bool { let ret = self.new[Key::Right]; self.new[Key::Right] = false; ret }
+ pub fn new_a(&mut self) -> bool { let ret = self.new[Key::A]; self.new[Key::A] = false; ret }
+ pub fn new_b(&mut self) -> bool { let ret = self.new[Key::B]; self.new[Key::B] = false; ret }
+ pub fn new_x(&mut self) -> bool { let ret = self.new[Key::X]; self.new[Key::X] = false; ret }
+ pub fn new_y(&mut self) -> bool { let ret = self.new[Key::Y]; self.new[Key::Y] = false; ret }
+ pub fn new_l(&mut self) -> bool { let ret = self.new[Key::L]; self.new[Key::L] = false; ret }
+ pub fn new_r(&mut self) -> bool { let ret = self.new[Key::R]; self.new[Key::R] = false; ret }
+ pub fn new_start(&mut self) -> bool { let ret = self.new[Key::Start]; self.new[Key::Start] = false; ret }
+ pub fn new_select(&mut self) -> bool { let ret = self.new[Key::Select]; self.new[Key::Select] = false; ret }
+ pub fn new_debug(&mut self) -> bool { let ret = self.new[Key::Debug]; self.new[Key::Debug] = false; ret }
}
pub struct PointLight {
@@ -176,6 +177,7 @@ pub struct State {
pub shader_upscale: shader::Shader,
pub mesh_square: mesh::Mesh,
pub font_default: font::Bitmap,
+ pub font_small: font::Bitmap,
pub audio: Option<audio::Assets>,
pub projection: glam::Mat4,
@@ -183,8 +185,6 @@ pub struct State {
pub camera: (glam::Vec3, glam::Vec3, glam::Vec3),
pub lighting: (glam::Vec3, glam::Vec3, glam::Vec3),
pub point_lights: Vec<PointLight>,
-
- pub log: Vec<(u64, String)>,
}
#[cfg(target_arch = "wasm32")]
@@ -274,6 +274,7 @@ impl State {
shader_upscale,
mesh_square,
font_default: font::Bitmap::default(ctx),
+ font_small: font::Bitmap::small(ctx),
audio: None,
projection: glam::Mat4::perspective_lh(
@@ -300,16 +301,9 @@ impl State {
// waker_ctx,
// http_client: reqwest::Client::new(),
// request: None,
-
- log: Vec::new(),
}
}
- pub fn write_log(&mut self, e: &str) {
- log::info!("log: {}", e.to_owned());
- self.log.push((self.tick, e.to_owned()));
- }
-
pub fn handle_resize(&mut self, ctx: &context::Context) {
self.screen = framebuffer::Framebuffer::screen(ctx);
}
@@ -484,17 +478,20 @@ impl State {
Ok(())
}
- pub fn mouse_released(
+ pub fn mouse_released<G>(
&mut self,
_ctx: &context::Context,
- ) {
+ _game: &mut G
+ ) -> utils::Erm<()> where G: Game {
+ Ok(())
}
- pub fn key_pressed(
+ pub fn key_pressed<G>(
&mut self,
- _ctx: &context::Context,
+ ctx: &context::Context,
+ game: &mut G,
key: Keycode,
- ) {
+ ) -> utils::Erm<()> where G: Game {
#[cfg(target_arch = "wasm32")]
let rebind = key.kc == winit::keyboard::KeyCode::F12;
#[cfg(not(target_arch = "wasm32"))]
@@ -502,7 +499,7 @@ impl State {
if rebind {
self.keybindings = default_keybindings();
self.rebinding = None;
- self.write_log("Reset keybindings!");
+ game.keybindings_were_reset(ctx, self)?;
} else if let Some(k) = self.rebinding {
self.keybindings.insert(key, k);
self.rebinding = None;
@@ -510,16 +507,19 @@ impl State {
self.keys.pressed[*k] = true;
self.keys.new[*k] = true;
}
+ Ok(())
}
- pub fn key_released(
+ pub fn key_released<G>(
&mut self,
_ctx: &context::Context,
+ _game: &mut G,
key: Keycode,
- ) {
+ ) -> utils::Erm<()> where G: Game {
if let Some(k) = self.keybindings.get_by_left(&key) {
self.keys.pressed[*k] = false;
}
+ Ok(())
}
/// Return the first keybinding for the given virtual key
diff --git a/crates/teleia/src/ui.rs b/crates/teleia/src/ui.rs
index 056f78a..0f0e52a 100644
--- a/crates/teleia/src/ui.rs
+++ b/crates/teleia/src/ui.rs
@@ -130,13 +130,11 @@ impl Cursor {
/// Read keypresses to update this cursor (assuming that the left/right keys decrement/increment)
/// Returns true if an update was performed (e.g. to determine whether to play a sound).
- pub fn update_horizontal(&mut self, st: &state::State) -> bool {
+ pub fn update_horizontal(&mut self, st: &mut state::State) -> bool {
if st.keys.new_left() {
- self.decrement_unlocked(st.tick);
- true
+ self.decrement_unlocked(st.tick)
} else if st.keys.new_right() {
- self.increment_unlocked(st.tick);
- true
+ self.increment_unlocked(st.tick)
} else if st.keys.left() {
self.decrement(st.tick)
} else if st.keys.right() {
@@ -144,7 +142,7 @@ impl Cursor {
} else { false }
}
- pub fn update_vertical(&mut self, st: &state::State) -> bool {
+ pub fn update_vertical(&mut self, st: &mut state::State) -> bool {
if st.keys.new_up() {
self.decrement_unlocked(st.tick)
} else if st.keys.new_down() {
@@ -156,7 +154,7 @@ impl Cursor {
} else { false }
}
- pub fn update_lr(&mut self, st: &state::State) -> bool {
+ pub fn update_lr(&mut self, st: &mut state::State) -> bool {
if st.keys.new_l() {
self.decrement_unlocked(st.tick)
} else if st.keys.new_r() {