diff options
| author | LLLL Colonq <llll@colonq> | 2025-07-25 20:35:51 -0400 |
|---|---|---|
| committer | LLLL Colonq <llll@colonq> | 2025-07-25 20:35:51 -0400 |
| commit | ba1ff8e7e3ea915c6d673c05cdcdee10a9b5b9f0 (patch) | |
| tree | 41de2e6d7a99ad8d8059ca9f482646be92f79d39 /crates/renderer/src/overlay/shader.rs | |
| parent | 513a3dd4c5899063e80fd691600d9142910547c4 (diff) | |
WIP
Diffstat (limited to 'crates/renderer/src/overlay/shader.rs')
| -rw-r--r-- | crates/renderer/src/overlay/shader.rs | 164 |
1 files changed, 153 insertions, 11 deletions
diff --git a/crates/renderer/src/overlay/shader.rs b/crates/renderer/src/overlay/shader.rs index cfaaee4..181ba07 100644 --- a/crates/renderer/src/overlay/shader.rs +++ b/crates/renderer/src/overlay/shader.rs @@ -3,18 +3,22 @@ use teleia::*; use std::{collections::HashMap, f32::consts::PI}; use lexpr::sexp; use base64::prelude::*; +use device_query::DeviceQuery; + +use glow::HasContext; use crate::{assets, fig, toggle}; pub struct Chat { + author: String, msg: String, time: f64, biblicality: f32, } - impl Chat { pub fn new() -> Self { Self { + author: format!(""), msg: format!(""), time: 0.0, biblicality: 0.0, @@ -22,11 +26,96 @@ impl Chat { } } +const DRAWING_WIDTH: usize = 1920 / 4; +const DRAWING_HEIGHT: usize = 1080 / 4; +pub enum DrawingCommand { + None, + Drawing, + EraseAll, +} +pub struct Drawing { + tex: texture::Texture, + pixels: [u8; DRAWING_WIDTH * DRAWING_HEIGHT], + last_point: Option<(i32, i32)>, + shader_white: shader::Shader, +} +impl Drawing { + pub fn new(ctx: &context::Context) -> Self { + Self { + tex: texture::Texture::new_empty(ctx), + pixels: [0; DRAWING_WIDTH * DRAWING_HEIGHT], + last_point: None, + shader_white: shader::Shader::new( + ctx, + include_str!("../assets/shaders/white/vert.glsl"), + include_str!("../assets/shaders/white/frag.glsl"), + ), + } + } + pub fn coord(&self, x: usize, y: usize) -> Option<usize> { + if x > DRAWING_WIDTH || y > DRAWING_HEIGHT { + None + } else { + Some(x + y * DRAWING_WIDTH) + } + } + pub fn set(&mut self, val: u8, x: i32, y: i32) { + self.coord(x as usize, y as usize).map(|idx| self.pixels[idx] = val); + } + pub fn point(&mut self, val: u8, x: i32, y: i32) { + self.set(val, x, y - 1); + self.set(val, x - 1, y); + self.set(val, x, y); + self.set(val, x + 1, y); + self.set(val, x, y + 1); + } + pub fn line(&mut self, val: u8, (mut x0, mut y0): (i32, i32), (x1, y1): (i32, i32)) { + let dx = (x1 - x0).abs(); + let sx = if x0 < x1 { 1 } else { -1 }; + let dy = -((y1 - y0).abs()); + let sy = if y0 < y1 { 1 } else { -1 }; + let mut error = dx + dy; + loop { + self.point(val, x0, y0); + let e2 = 2 * error; + if e2 >= dy { + if x0 == x1 { break; } + error += dy; + x0 += sx; + } + if e2 <= dx { + if y0 == y1 { break; } + error += dx; + y0 += sy; + } + } + } + pub fn upload(&self, ctx: &context::Context) { + unsafe { + let err = ctx.gl.get_error(); + self.tex.bind(ctx); + ctx.gl.tex_image_2d( + glow::TEXTURE_2D, + 0, + glow::R8 as i32, + DRAWING_WIDTH as i32, + DRAWING_HEIGHT as i32, + 0, + glow::RED, + glow::UNSIGNED_BYTE, + Some(&self.pixels), + ); + ctx.gl.generate_mipmap(glow::TEXTURE_2D); + } + } +} + pub struct Overlay { assets: assets::Assets, model: scene::Scene, model_neck_base: glam::Mat4, fig: fig::Client, + fig_binary: fig::BinaryClient, tracking_eyes: (f32, f32), tracking_mouth: f32, tracking_neck: glam::Quat, @@ -37,6 +126,8 @@ pub struct Overlay { muzak_author: Option<String>, chat: Chat, toggles: toggle::Toggles, + drawing: Drawing, + device: device_query::DeviceState, } impl Overlay { @@ -66,6 +157,9 @@ impl Overlay { sexp!((avatar overlay cursor)), sexp!((avatar overlay emacs)), ]), + fig_binary: fig::BinaryClient::new("shiro:32051", &[ + b"test event" + ]), tracking_eyes: (1.0, 1.0), tracking_mouth: 0.0, tracking_neck: glam::Quat::IDENTITY, @@ -76,6 +170,21 @@ impl Overlay { muzak_author: None, chat: Chat::new(), toggles: toggle::Toggles::new(), + drawing: Drawing::new(ctx), + device: device_query::DeviceState::new(), + } + } + fn get_mouse(&self) -> (i32, i32) { + self.device.get_mouse().coords + } + fn get_drawing_command(&mut self) -> DrawingCommand { + let keys = self.device.get_keys(); + if keys.contains(&device_query::Keycode::LMeta) { + DrawingCommand::Drawing + } else if keys.contains(&device_query::Keycode::RMeta) { + DrawingCommand::EraseAll + } else { + DrawingCommand::None } } pub fn handle_reset(&mut self, ctx: &context::Context) { @@ -125,11 +234,13 @@ impl Overlay { Some(()) } pub fn handle_overlay_chat(&mut self, msg: fig::Message) -> Option<()> { - let bs = BASE64_STANDARD.decode(msg.data.get(0)?.as_str()?).ok()?; + let ba = BASE64_STANDARD.decode(msg.data.get(0)?.as_str()?).ok()?; + let a = String::from_utf8_lossy(&ba); + let bs = BASE64_STANDARD.decode(msg.data.get(1)?.as_str()?).ok()?; let s = String::from_utf8_lossy(&bs); - let time = msg.data.get(1)?.as_str()?.parse::<f64>().ok()?; - let biblicality = msg.data.get(2)?.as_str()?.parse::<f32>().ok()?; - // log::info!("received chat message: {} {} {}", s, time, biblicality); + let time = msg.data.get(2)?.as_str()?.parse::<f64>().ok()?; + let biblicality = msg.data.get(3)?.as_str()?.parse::<f32>().ok()?; + self.chat.author = a.to_string(); self.chat.msg = s.to_string(); self.chat.time = time; self.chat.biblicality = biblicality; @@ -145,6 +256,16 @@ impl Overlay { self.emacs_heartrate = msg.data.get(0)?.as_i64()? as i32; Some(()) } + pub fn render_drawing(&self, ctx: &context::Context, st: &mut state::State) { + st.bind_2d(ctx, &self.drawing.shader_white); + self.drawing.tex.bind(ctx); + self.drawing.shader_white.set_position_2d( + ctx, + &glam::Vec2::new(0.0, 0.0), + &glam::Vec2::new(1920.0, 1080.0) + ); + self.assets.mesh_square.render(ctx); + } } impl teleia::state::Game for Overlay { @@ -161,12 +282,8 @@ impl teleia::state::Game for Overlay { &glam::Vec3::new(0.0, 0.0, -1.0), &glam::Vec3::new(0.0, 1.0, 0.0), ); - match mouse_position::mouse_position::Mouse::get_mouse_position() { - mouse_position::mouse_position::Mouse::Position { x, y } => { - self.mouse_cursor = (x as f32, y as f32); - }, - _ => {}, - } + let (x, y) = self.get_mouse(); + self.mouse_cursor = (x as f32, y as f32); while let Some(msg) = self.fig.pump() { let malformed = format!("malformed {} data: {}", msg.event, msg.data); if msg.event == sexp!((avatar tracking)) { @@ -193,6 +310,9 @@ impl teleia::state::Game for Overlay { log::info!("received unhandled event {} with data: {}", msg.event, msg.data); } } + while let Some(msg) = self.fig_binary.pump() { + log::info!("binary message: {:?}", msg); + } if let Some(n) = self.model.nodes_by_name.get("J_Bip_C_Neck").and_then(|i| self.model.nodes.get_mut(*i)) { n.transform = self.model_neck_base * glam::Mat4::from_quat(self.tracking_neck); } @@ -247,6 +367,28 @@ impl teleia::state::Game for Overlay { } let astr: String = authors.join(", "); self.assets.font.render_text(ctx, &glam::Vec2::new(0.0, 0.0), &astr); + match self.get_drawing_command() { + DrawingCommand::Drawing => { + let (sx, sy) = self.get_mouse(); + let x = sx / 4; + let y = sy / 4; + if let Some(last) = self.drawing.last_point { + self.drawing.line(1, last, (x, y)); + } else { + self.drawing.point(1, x, y); + } + self.drawing.last_point = Some((x, y)); + }, + DrawingCommand::EraseAll => { + self.drawing.pixels.fill(0); + self.drawing.last_point = None; + }, + DrawingCommand::None => { + self.drawing.last_point = None; + }, + } + self.drawing.upload(ctx); + self.render_drawing(ctx, st); Ok(()) } } |
