From 2a0a832049459783967c889bf4fca7636bb0f76d Mon Sep 17 00:00:00 2001 From: LLLL Colonq Date: Fri, 14 Nov 2025 13:54:26 -0500 Subject: Update TCG --- .../src/assets/shaders/tcg_screen/frag.glsl | 7 +- .../renderer/src/assets/textures/tcg/cardback.png | Bin 0 -> 34257 bytes crates/renderer/src/overlay/tcg.rs | 162 ++++++++++++++++----- 3 files changed, 132 insertions(+), 37 deletions(-) create mode 100644 crates/renderer/src/assets/textures/tcg/cardback.png diff --git a/crates/renderer/src/assets/shaders/tcg_screen/frag.glsl b/crates/renderer/src/assets/shaders/tcg_screen/frag.glsl index cb4a58f..43acea3 100644 --- a/crates/renderer/src/assets/shaders/tcg_screen/frag.glsl +++ b/crates/renderer/src/assets/shaders/tcg_screen/frag.glsl @@ -1,9 +1,10 @@ -uniform sampler2D texture_data; +uniform sampler2D texture_front; +uniform sampler2D texture_back; void main() { - vec2 tcfull = vec2(vertex_texcoord.x, vertex_texcoord.y); - vec4 texel = texture(texture_data, tcfull); + vec2 tcfull = vec2(vertex_texcoord.x, 1.0 - vertex_texcoord.y); + vec4 texel = gl_FrontFacing ? texture(texture_back, tcfull) : texture(texture_front, tcfull); if (texel.a == 0.0) { discard; } diff --git a/crates/renderer/src/assets/textures/tcg/cardback.png b/crates/renderer/src/assets/textures/tcg/cardback.png new file mode 100644 index 0000000..15cb7c7 Binary files /dev/null and b/crates/renderer/src/assets/textures/tcg/cardback.png differ diff --git a/crates/renderer/src/overlay/tcg.rs b/crates/renderer/src/overlay/tcg.rs index 4191e6c..cd19556 100644 --- a/crates/renderer/src/overlay/tcg.rs +++ b/crates/renderer/src/overlay/tcg.rs @@ -4,6 +4,8 @@ use glow::HasContext; use crate::overlay; +pub const CARD_SLOTS: usize = 11; +pub const CARD_SPACING: u64 = 300; pub const IWIDTH: usize = 160; pub const IHEIGHT: usize = 225; pub const WIDTH: f32 = IWIDTH as f32; @@ -81,25 +83,103 @@ impl RenderedCardSlot { ost.assets.shader_tcg_screen.set_position_2d(ctx, st, &pos, &dim); st.mesh_square.render(ctx); } + pub fn render_3d(&self, + ctx: &context::Context, st: &mut state::State, ost: &mut overlay::State, + back: &texture::Texture, + pos: glam::Mat4, + ) { + st.bind_3d(ctx, &ost.assets.shader_tcg_screen); + ost.assets.shader_tcg_screen.set_i32(ctx, "texture_front", 0); + ost.assets.shader_tcg_screen.set_i32(ctx, "texture_back", 1); + self.texture.bind(ctx); + back.bind_index(ctx, 1); + ost.assets.shader_tcg_screen.set_position_3d(ctx, st, &pos); + st.mesh_square.render(ctx); + } } -fn load_texture(tex: &texture::Texture, ctx: &context::Context, st: &mut state::State, path: &str) -> Erm<()> { - unsafe { - let img = image::ImageReader::open(path)?.decode()?.into_rgba8(); - tex.bind(ctx); - ctx.gl.tex_image_2d( - glow::TEXTURE_2D, - 0, - glow::RGBA as i32, - img.width() as i32, - img.height() as i32, - 0, - glow::RGBA, - glow::UNSIGNED_BYTE, - Some(&img.as_bytes()), - ); - ctx.gl.generate_mipmap(glow::TEXTURE_2D); - Ok(()) +struct MarqueeSlot { + card: RenderedCardSlot, + active: Option, // ticks active +} +struct Marquee { + texture_back: texture::Texture, + slots: [MarqueeSlot; CARD_SLOTS], + next_slot: usize, + queue: std::collections::VecDeque<(Card, image::RgbaImage)>, + most_recent: u64, +} +impl Marquee { + pub fn new(ctx: &context::Context) -> Self { + Self { + texture_back: texture::Texture::new(ctx, include_bytes!("../assets/textures/tcg/cardback.png")), + slots: std::array::from_fn(|_| MarqueeSlot { + card: RenderedCardSlot::new(ctx), + active: None, + }), + next_slot: 0, + queue: std::collections::VecDeque::new(), + most_recent: 0, + } + } + fn fill_slot(&mut self, + ctx: &context::Context, st: &state::State, + card: &Card, img: &image::RgbaImage + ) -> bool { + if st.tick - self.most_recent > CARD_SPACING { + for s in self.slots.iter_mut() { + if s.active.is_none() { + s.card.set(ctx, card.clone(), img); + s.active = Some(st.tick); + self.most_recent = st.tick; + return true; + } + } + } + false + } + pub fn add(&mut self, + ctx: &context::Context, st: &state::State, + card: Card, img: &image::RgbaImage + ) { + if !self.fill_slot(ctx, st, &card, img) { + self.queue.push_back((card, img.clone())) + } + } + pub fn render(&mut self, + ctx: &context::Context, st: &mut state::State, ost: &mut overlay::State, + ) { + for s in self.slots.iter_mut() { + if s.active.is_none() && st.tick - self.most_recent > CARD_SPACING{ + if let Some((c, img)) = self.queue.pop_front() { + s.card.set(ctx, c.clone(), &img); + s.active = Some(st.tick); + self.most_recent = st.tick; + } else { + break; + } + } + } + for s in self.slots.iter_mut() { + if let Some(spawn) = s.active { + let p = st.tick - spawn; + let pos = (p as f32) / 200.0 - 7.0; + if pos > 8.0 { + s.active = None; + } else { + s.card.render_3d(ctx, st, ost, + &self.texture_back, + glam::Mat4::from_scale_rotation_translation( + glam::Vec3::new(0.7111, 1.0, 1.0), + glam::Quat::from_rotation_y(p as f32 / 75.0), + glam::Vec3::new(pos, -2.0, + (p as f32 / 100.0).sin() / 2.0 - 8.0 + ), + ) + ); + } + } + } } } @@ -110,12 +190,30 @@ pub struct Overlay { texture_faction_nate: texture::Texture, texture_faction_lever: texture::Texture, texture_faction_tony: texture::Texture, - // font: font::TrueType, font: font::Bitmap, - card: RenderedCardSlot, + marquee: Marquee, } - impl Overlay { + fn load_texture(tex: &texture::Texture, ctx: &context::Context, st: &mut state::State, path: &str) -> Erm<()> { + unsafe { + let img = image::ImageReader::open(path)?.decode()?.into_rgba8(); + tex.bind(ctx); + ctx.gl.tex_image_2d( + glow::TEXTURE_2D, + 0, + glow::RGBA as i32, + img.width() as i32, + img.height() as i32, + 0, + glow::RGBA, + glow::UNSIGNED_BYTE, + Some(&img.as_bytes()), + ); + ctx.gl.generate_mipmap(glow::TEXTURE_2D); + Ok(()) + } + } + pub fn new(ctx: &context::Context) -> Self { Self { fb: framebuffer::Framebuffer::new(ctx, &glam::Vec2::new(WIDTH, HEIGHT), &glam::Vec2::new(0.0, 0.0)), @@ -124,9 +222,8 @@ impl Overlay { texture_faction_nate: texture::Texture::new(ctx, include_bytes!("../assets/textures/tcg/factions/nate.png")), texture_faction_lever: texture::Texture::new(ctx, include_bytes!("../assets/textures/tcg/factions/lever.png")), texture_faction_tony: texture::Texture::new(ctx, include_bytes!("../assets/textures/tcg/factions/tony.png")), - // font: font::TrueType::new(ctx, 32.0, include_bytes!("../assets/fonts/iosevka-comfy-regular.ttf")), font: font::Bitmap::from_image(ctx, 6, 12, 96, 72, include_bytes!("../assets/fonts/terminus.png")), - card: RenderedCardSlot::new(ctx), + marquee: Marquee::new(ctx), } } fn draw_rectangle(&self, @@ -261,7 +358,8 @@ impl Overlay { st.bind_render_framebuffer(ctx); let mut pixels = vec![0; IWIDTH * IHEIGHT * 4]; self.fb.get_pixels_raw(ctx, &mut pixels); - image::RgbaImage::from_vec(IWIDTH as u32, IHEIGHT as u32, pixels) + let pixels_rev = pixels.chunks_exact(IWIDTH * 4).rev().flatten().copied().collect(); + image::RgbaImage::from_vec(IWIDTH as u32, IHEIGHT as u32, pixels_rev) } } @@ -276,7 +374,6 @@ impl overlay::Overlay for Overlay { let s = std::str::from_utf8(&msg.data)?.to_owned(); log::info!("msg: {}", s); let mut sp = s.split("\t"); - let id = sp.next().ok_or(Error::NotEnoughFields)?.to_owned(); let name = sp.next().ok_or(Error::NotEnoughFields)?.to_owned(); let ty = sp.next().ok_or(Error::NotEnoughFields)?.to_owned(); let depicted_subject = sp.next().ok_or(Error::NotEnoughFields)?.to_owned(); @@ -299,9 +396,9 @@ impl overlay::Overlay for Overlay { let set = sp.next().ok_or(Error::NotEnoughFields)?.to_owned(); let minted_date = sp.next().ok_or(Error::NotEnoughFields)?.to_owned(); let flags = sp.next().ok_or(Error::NotEnoughFields)?.to_owned(); - load_texture(&self.texture_base, ctx, st, &format!("crates/renderer/src/assets/textures/tcg/bases/{}.png", base_image_name))?; - if load_texture(&self.texture_art, ctx, st, &format!("/home/llll/src/wasp/assets/avatars/{}.png", depicted_subject.to_ascii_lowercase())).is_err() { - load_texture(&self.texture_art, ctx, st, "/home/llll/src/wasp/assets/avatars/jontest.png")?; + Self::load_texture(&self.texture_base, ctx, st, &format!("crates/renderer/src/assets/textures/tcg/bases/{}.png", base_image_name))?; + if Self::load_texture(&self.texture_art, ctx, st, &format!("/home/llll/src/wasp/assets/avatars/{}.png", depicted_subject.to_ascii_lowercase())).is_err() { + Self::load_texture(&self.texture_art, ctx, st, "/home/llll/src/wasp/assets/avatars/jontest.png")?; } let card = Card { name, @@ -321,7 +418,7 @@ impl overlay::Overlay for Overlay { flags, }; if let Some(img) = self.generate_card(ctx, st, ost, card.clone()) { - self.card.set(ctx, card, &img); + self.marquee.add(ctx, st, card, &img); let err: Erm<()> = (||{ let mut buf = Vec::new(); let mut cursor = std::io::Cursor::new(&mut buf); @@ -346,12 +443,9 @@ impl overlay::Overlay for Overlay { Ok(()) } fn render(&mut self, ctx: &context::Context, st: &mut state::State, ost: &mut overlay::State) -> Erm<()> { - if self.card.card.is_some() { - self.card.render(ctx, st, ost, - glam::Vec2::new(1000.0, 400.0), - glam::Vec2::new(WIDTH * 2.0, HEIGHT * 2.0) - ); - } + st.render_framebuffer.bind(ctx); + ctx.clear_depth(); + self.marquee.render(ctx, st, ost); Ok(()) } } -- cgit v1.2.3