From a76f583c2d11043a94c3251ec6a5381d7836bdb4 Mon Sep 17 00:00:00 2001 From: LLLL Colonq Date: Thu, 13 Nov 2025 22:04:56 -0500 Subject: Add TCG --- Cargo.lock | 41 ++- Cargo.toml | 4 + crates/client/Cargo.toml | 2 +- crates/renderer/Cargo.toml | 5 +- crates/renderer/src/assets.rs | 24 ++ crates/renderer/src/assets/shaders/flat/frag.glsl | 3 - .../renderer/src/assets/shaders/tcg_base/frag.glsl | 60 ++++ .../renderer/src/assets/shaders/tcg_base/vert.glsl | 4 + .../src/assets/shaders/tcg_screen/frag.glsl | 7 +- .../src/assets/textures/tcg/factions/lever.png | Bin 0 -> 366 bytes .../src/assets/textures/tcg/factions/nate.png | Bin 0 -> 323 bytes .../src/assets/textures/tcg/factions/tony.png | Bin 0 -> 272 bytes crates/renderer/src/overlay.rs | 16 +- crates/renderer/src/overlay/tcg.rs | 325 ++++++++++++++++----- crates/shader/Cargo.toml | 2 +- 15 files changed, 408 insertions(+), 85 deletions(-) create mode 100644 crates/renderer/src/assets/shaders/tcg_base/frag.glsl create mode 100644 crates/renderer/src/assets/shaders/tcg_base/vert.glsl create mode 100644 crates/renderer/src/assets/textures/tcg/factions/lever.png create mode 100644 crates/renderer/src/assets/textures/tcg/factions/nate.png create mode 100644 crates/renderer/src/assets/textures/tcg/factions/tony.png diff --git a/Cargo.lock b/Cargo.lock index 32d0e3a..de6a89e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1707,6 +1707,15 @@ name = "jpeg-decoder" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" +dependencies = [ + "rayon", +] + +[[package]] +name = "jpeg-encoder" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b454d911ac55068f53495488d8ccd0646eaa540c033a28ee15b07838afafb01f" [[package]] name = "js-sys" @@ -1718,6 +1727,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kamadak-exif" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077" +dependencies = [ + "mutate_once", +] + [[package]] name = "kira" version = "0.9.6" @@ -1939,6 +1957,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "mutate_once" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" + [[package]] name = "nalgebra" version = "0.33.2" @@ -2066,6 +2090,7 @@ dependencies = [ "strum", "teleia", "termion", + "web-image-meta", ] [[package]] @@ -3385,7 +3410,6 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "teleia" version = "0.1.0" -source = "git+https://github.com/lcolonq/teleia#9e95cb43f1dbc9e63c9e3df2472c36e38e19fc88" dependencies = [ "bimap", "bincode", @@ -4060,6 +4084,21 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "web-image-meta" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345e45cce08e14e76a42ab280f1ca3083aee6e8cb68a78bcf9368b912f9f8f2f" +dependencies = [ + "crc32fast", + "flate2", + "jpeg-decoder", + "jpeg-encoder", + "kamadak-exif", + "png", + "thiserror 1.0.69", +] + [[package]] name = "web-sys" version = "0.3.77" diff --git a/Cargo.toml b/Cargo.toml index 5f0dc95..7569b52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,7 @@ strip = true [profile.dev.package."*"] debug = 0 opt-level = 2 + +[workspace.dependencies] +# teleia = { git = "https://github.com/lcolonq/teleia" } # engine +teleia = { path = "../teleia/crates/teleia" } # engine \ No newline at end of file diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index d7da09a..fc486f3 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -8,7 +8,7 @@ authors.workspace = true crate-type = ["cdylib", "rlib"] [dependencies] -teleia = { git = "https://github.com/lcolonq/teleia" } # engine +teleia = { workspace = true } glam = "*" # linear algebra glow = "*" # gl bindings bitflags = "*" # C-style bitwise flags diff --git a/crates/renderer/Cargo.toml b/crates/renderer/Cargo.toml index 6bf77a7..aef49bb 100644 --- a/crates/renderer/Cargo.toml +++ b/crates/renderer/Cargo.toml @@ -9,7 +9,7 @@ name = "newton_renderer" path = "src/main.rs" [dependencies] -teleia = { git = "https://github.com/lcolonq/teleia" } # engine +teleia = { workspace = true } newton_shader = { path = "../shader" } glam = "*" # linear algebra glow = "*" # gl bindings @@ -28,4 +28,5 @@ device_query = "*" # get pressed keys when unfocused byteorder = "*" # read little-endian numbers image = "*" # read and write image files cpal = "*" # record microphone -redis = "*" # database \ No newline at end of file +redis = "*" # database +web-image-meta = "*" # edit image metadata \ No newline at end of file diff --git a/crates/renderer/src/assets.rs b/crates/renderer/src/assets.rs index 83b5a0d..1006c13 100644 --- a/crates/renderer/src/assets.rs +++ b/crates/renderer/src/assets.rs @@ -4,6 +4,10 @@ pub struct Assets { pub font: font::Bitmap, pub shader_flat: shader::Shader, pub shader_scene: shader::Shader, + pub shader_color: shader::Shader, + pub shader_tcg: shader::Shader, + pub shader_tcg_screen: shader::Shader, + pub shader_tcg_base: shader::Shader, pub texture_adblock: texture::Texture, pub texture_mod: texture::Texture, pub texture_operatop: texture::Texture, @@ -24,6 +28,26 @@ impl Assets { include_str!("assets/shaders/scene/vert.glsl"), include_str!("assets/shaders/scene/frag.glsl") ), + shader_color: shader::Shader::new( + ctx, + include_str!("assets/shaders/color/vert.glsl"), + include_str!("assets/shaders/color/frag.glsl") + ), + shader_tcg: shader::Shader::new( + ctx, + include_str!("assets/shaders/tcg/vert.glsl"), + include_str!("assets/shaders/tcg/frag.glsl") + ), + shader_tcg_screen: shader::Shader::new( + ctx, + include_str!("assets/shaders/tcg_screen/vert.glsl"), + include_str!("assets/shaders/tcg_screen/frag.glsl") + ), + shader_tcg_base: shader::Shader::new( + ctx, + include_str!("assets/shaders/tcg_base/vert.glsl"), + include_str!("assets/shaders/tcg_base/frag.glsl") + ), texture_adblock: texture::Texture::new(ctx, include_bytes!("assets/textures/adblock.png")), texture_mod: texture::Texture::new(ctx, include_bytes!("assets/textures/mod.png")), texture_operatop: texture::Texture::new(ctx, include_bytes!("assets/textures/operatop.png")), diff --git a/crates/renderer/src/assets/shaders/flat/frag.glsl b/crates/renderer/src/assets/shaders/flat/frag.glsl index cab840d..69fe2ec 100644 --- a/crates/renderer/src/assets/shaders/flat/frag.glsl +++ b/crates/renderer/src/assets/shaders/flat/frag.glsl @@ -7,9 +7,6 @@ void main() float opacity = 1.0 - clamp(transparency, 0.0, 1.0); vec2 tcfull = vec2(vertex_texcoord.x, 1.0 - vertex_texcoord.y); vec4 texel = texture(texture_data, tcfull); - if (texel.a != 1.0) { - discard; - } texel.a *= opacity; frag_color = texel; } diff --git a/crates/renderer/src/assets/shaders/tcg_base/frag.glsl b/crates/renderer/src/assets/shaders/tcg_base/frag.glsl new file mode 100644 index 0000000..b941d61 --- /dev/null +++ b/crates/renderer/src/assets/shaders/tcg_base/frag.glsl @@ -0,0 +1,60 @@ +uniform sampler2D texture_data; + +uniform vec4 shift_color; + +vec3 rgb_to_hsl(vec3 rgb) { + vec3 ret; + float min = min(min(rgb.r, rgb.g), rgb.b); + float max = max(max(rgb.r, rgb.g), rgb.b); + float lum = (max + min) / 2.0; + ret.z = lum; + if (max == min) { + ret.x = ret.y = 0.0; + } else { + float chroma = max - min; + ret.y = chroma / (1.0 - abs(2.0 * lum - 1.0)); + if (max == rgb.r) { + ret.x = (rgb.g - rgb.b) / chroma + (rgb.g < rgb.b ? 6.0 : 0.0); + } else if (max == rgb.g) { + ret.x = (rgb.b - rgb.r) / chroma + 2.0; + } else { + ret.x = (rgb.r - rgb.g) / chroma + 4.0; + } + ret.x /= 6.0; + } + return ret; +} + +float hue_to_rgb(float p, float q, float t) { + if (t < 0.0) t += 1.0; + if (t > 1.0) t -= 1.0; + if (t < 1.0/6.0) return p + (q - p) * 6.0 * t; + if (t < 1.0/2.0) return q; + if (t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; + return p; +} + +vec3 hsl_to_rgb(vec3 hsl) { + vec3 ret; + if (hsl.y == 0.0) { + ret.r = ret.g = ret.b = hsl.z; + } else { + float q = hsl.z < 0.5 ? hsl.z * (1.0 + hsl.y) : hsl.z + hsl.y - hsl.z * hsl.y; + float p = 2.0 * hsl.z - q; + ret.r = hue_to_rgb(p, q, hsl.x + 1.0/3.0); + ret.g = hue_to_rgb(p, q, hsl.x); + ret.b = hue_to_rgb(p, q, hsl.x - 1.0/3.0); + } + return ret; +} + +void main() +{ + vec2 tcfull = vec2(vertex_texcoord.x, 1.0 - vertex_texcoord.y); + vec4 texel = texture(texture_data, tcfull); + vec3 hsl = rgb_to_hsl(texel.xyz); + vec3 shift_hsl = rgb_to_hsl(shift_color.xyz); + hsl.x = shift_hsl.x; + texel.xyz = hsl_to_rgb(hsl); + frag_color = texel; +} diff --git a/crates/renderer/src/assets/shaders/tcg_base/vert.glsl b/crates/renderer/src/assets/shaders/tcg_base/vert.glsl new file mode 100644 index 0000000..e324f7e --- /dev/null +++ b/crates/renderer/src/assets/shaders/tcg_base/vert.glsl @@ -0,0 +1,4 @@ +void main() +{ + default_main(); +} \ No newline at end of file diff --git a/crates/renderer/src/assets/shaders/tcg_screen/frag.glsl b/crates/renderer/src/assets/shaders/tcg_screen/frag.glsl index 2a14860..cb4a58f 100644 --- a/crates/renderer/src/assets/shaders/tcg_screen/frag.glsl +++ b/crates/renderer/src/assets/shaders/tcg_screen/frag.glsl @@ -1,15 +1,12 @@ uniform sampler2D texture_data; -uniform float transparency; - void main() { - float opacity = 1.0 - clamp(transparency, 0.0, 1.0); vec2 tcfull = vec2(vertex_texcoord.x, vertex_texcoord.y); vec4 texel = texture(texture_data, tcfull); - if (texel.a != 1.0) { + if (texel.a == 0.0) { discard; } - texel.a *= opacity; + texel.a = 1.0; frag_color = texel; } diff --git a/crates/renderer/src/assets/textures/tcg/factions/lever.png b/crates/renderer/src/assets/textures/tcg/factions/lever.png new file mode 100644 index 0000000..a757baa Binary files /dev/null and b/crates/renderer/src/assets/textures/tcg/factions/lever.png differ diff --git a/crates/renderer/src/assets/textures/tcg/factions/nate.png b/crates/renderer/src/assets/textures/tcg/factions/nate.png new file mode 100644 index 0000000..38a45e5 Binary files /dev/null and b/crates/renderer/src/assets/textures/tcg/factions/nate.png differ diff --git a/crates/renderer/src/assets/textures/tcg/factions/tony.png b/crates/renderer/src/assets/textures/tcg/factions/tony.png new file mode 100644 index 0000000..c6ccaf0 Binary files /dev/null and b/crates/renderer/src/assets/textures/tcg/factions/tony.png differ diff --git a/crates/renderer/src/overlay.rs b/crates/renderer/src/overlay.rs index af02eda..049d788 100644 --- a/crates/renderer/src/overlay.rs +++ b/crates/renderer/src/overlay.rs @@ -44,6 +44,8 @@ pub struct Info { pub struct State { assets: assets::Assets, + redis: redis::Client, + redis_conn: redis::Connection, model: scene::Scene, model_neck_base: glam::Mat4, fig_binary: fig::BinaryClient, @@ -61,8 +63,11 @@ impl State { .and_then(|i| model.nodes.get(*i)) .expect("failed to find neck joint") .transform; + let redis = redis::Client::open("redis://shiro").unwrap(); + let redis_conn = redis.get_connection().unwrap(); Self { assets: assets::Assets::new(ctx), + redis, redis_conn, model, model_neck_base, fig_binary: fig::BinaryClient::new("shiro:32051", &[ @@ -166,6 +171,9 @@ impl teleia::state::Game for Overlays { for ov in self.overlays.iter_mut() { ov.handle_binary(ctx, st, &mut self.state, &msg)?; } + if let Ok(t) = str::from_utf8(&msg.event) { + log::info!("incoming: {}", t); + } match &*msg.event { b"overlay reset" => self.reset(ctx, st)?, b"overlay tracking" => { @@ -206,8 +214,8 @@ impl teleia::state::Game for Overlays { b"overlay info emacs cursor" => { let res: Erm<()> = (|| { let mut reader = std::io::Cursor::new(&msg.data); - let cursor_x = reader.read_f32::()?; - let cursor_y = reader.read_f32::()?; + let cursor_x = fig::read_length_prefixed_utf8(&mut reader)?.parse()?; + let cursor_y = fig::read_length_prefixed_utf8(&mut reader)?.parse()?; self.state.info.emacs_cursor = (cursor_x, cursor_y); Ok(()) })(); @@ -223,8 +231,8 @@ impl teleia::state::Game for Overlays { let mut reader = std::io::Cursor::new(&msg.data); let author = fig::read_length_prefixed_utf8(&mut reader)?; let msg = fig::read_length_prefixed_utf8(&mut reader)?; - let time = reader.read_f32::()?; - let biblicality = reader.read_f32::()?; + let time = fig::read_length_prefixed_utf8(&mut reader)?.parse()?; + let biblicality = fig::read_length_prefixed_utf8(&mut reader)?.parse()?; self.state.chat.author = author; self.state.chat.msg = msg; self.state.chat.time = time; diff --git a/crates/renderer/src/overlay/tcg.rs b/crates/renderer/src/overlay/tcg.rs index 5c6c16a..4191e6c 100644 --- a/crates/renderer/src/overlay/tcg.rs +++ b/crates/renderer/src/overlay/tcg.rs @@ -4,8 +4,10 @@ use glow::HasContext; use crate::overlay; -pub const WIDTH: f32 = 320.0; -pub const HEIGHT: f32 = 450.0; +pub const IWIDTH: usize = 160; +pub const IHEIGHT: usize = 225; +pub const WIDTH: f32 = IWIDTH as f32; +pub const HEIGHT: f32 = IHEIGHT as f32; #[derive(Debug, Clone)] enum Error { @@ -20,6 +22,7 @@ impl std::fmt::Display for Error { } impl std::error::Error for Error {} +#[derive(Debug, Clone)] struct Card { name: String, ty: String, @@ -27,37 +30,239 @@ struct Card { element: String, color: glam::Vec4, faction: String, + faction_color: glam::Vec4, equity: i64, - boost_level: i64, + boost_level: String, rarity: String, rarity_level: i64, body_text: String, base_image_name: String, + set: String, + minted_date: String, flags: String, } +struct RenderedCardSlot { + card: Option, + texture: texture::Texture, +} +impl RenderedCardSlot { + pub fn new(ctx: &context::Context) -> Self { + Self { + card: None, + texture: texture::Texture::new_empty(ctx), + } + } + pub fn set(&mut self, ctx: &context::Context, card: Card, img: &image::RgbaImage) { + self.card = Some(card); + unsafe { + self.texture.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); + } + } + pub fn render(&self, + ctx: &context::Context, st: &mut state::State, ost: &mut overlay::State, + pos: glam::Vec2, + dim: glam::Vec2, + ) { + st.bind_2d(ctx, &ost.assets.shader_tcg_screen); + self.texture.bind(ctx); + ost.assets.shader_tcg_screen.set_position_2d(ctx, st, &pos, &dim); + 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(()) + } +} + pub struct Overlay { fb: framebuffer::Framebuffer, - texture: texture::Texture, - shader_color: shader::Shader, - shader_screen: shader::Shader, - shader: shader::Shader, - font: font::TrueType, - card: Option, + texture_base: texture::Texture, + texture_art: texture::Texture, + texture_faction_nate: texture::Texture, + texture_faction_lever: texture::Texture, + texture_faction_tony: texture::Texture, + // font: font::TrueType, + font: font::Bitmap, + card: RenderedCardSlot, } impl Overlay { 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)), - texture: texture::Texture::new_empty(ctx), - shader_color: shader::Shader::new(ctx, include_str!("../assets/shaders/color/vert.glsl"), include_str!("../assets/shaders/color/frag.glsl")), - shader_screen: shader::Shader::new(ctx, include_str!("../assets/shaders/tcg_screen/vert.glsl"), include_str!("../assets/shaders/tcg_screen/frag.glsl")), - shader: shader::Shader::new(ctx, include_str!("../assets/shaders/tcg/vert.glsl"), include_str!("../assets/shaders/tcg/frag.glsl")), - font: font::TrueType::new(ctx, 20.0, include_bytes!("../assets/fonts/iosevka-comfy-regular.ttf")), - card: None, + texture_base: texture::Texture::new_empty(ctx), + texture_art: texture::Texture::new_empty(ctx), + 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), } } + fn draw_rectangle(&self, + ctx: &context::Context, st: &mut state::State, ost: &mut overlay::State, + color: glam::Vec4, pos: glam::Vec2, dims: glam::Vec2 + ) { + st.bind_2d(ctx, &ost.assets.shader_color); + ost.assets.shader_color.set_vec4(ctx, "color", &color); + ost.assets.shader_color.set_position_2d( + ctx, st, + &pos, &dims, + ); + st.mesh_square.render(ctx); + } + + fn generate_card(&self, ctx: &context::Context, st: &mut state::State, ost: &mut overlay::State, card: Card) -> Option { + st.bind_framebuffer(ctx, &self.fb); + ctx.clear(); + + st.bind_2d(ctx, &ost.assets.shader_tcg_base); + self.texture_base.bind(ctx); + ost.assets.shader_tcg_base.set_vec4(ctx, "shift_color", &card.color); + ost.assets.shader_tcg_base.set_mat4(ctx, "view", &glam::Mat4::IDENTITY); + ost.assets.shader_tcg_base.set_mat4(ctx, "position", &glam::Mat4::IDENTITY); + st.mesh_square.render(ctx); + + // top bar + self.draw_rectangle(ctx, st, ost, + card.color.clone(), + glam::Vec2::new(0.0, 0.0), + glam::Vec2::new(WIDTH, 16.0), + ); + self.font.render_text_helper(ctx, st, + &glam::Vec2::new(8.0, 1.0), + &card.name, + &[glam::Vec3::new(0.0, 0.0, 0.0)] + ); + self.font.render_text_helper(ctx, st, + &glam::Vec2::new(WIDTH - 8.0 * card.rarity.len() as f32, 1.0), + &card.rarity, + &[glam::Vec3::new(0.0, 0.0, 0.0)] + ); + + // art + self.draw_rectangle(ctx, st, ost, + glam::Vec4::new(0.1, 0.1, 0.1, 1.0), + glam::Vec2::new(10.0, 16.0), + glam::Vec2::new(140.0, 100.0), + ); + st.bind_2d(ctx, &ost.assets.shader_flat); + self.texture_art.bind(ctx); + ost.assets.shader_flat.set_position_2d( + ctx, st, + &glam::Vec2::new(10.0, 16.0), + &glam::Vec2::new(140.0, 100.0) + ); + st.mesh_square.render(ctx); + + // faction stamp + let stex = match card.faction.as_ref() { + "nate" => Some(&self.texture_faction_nate), + "lever" => Some(&self.texture_faction_lever), + "tony" => Some(&self.texture_faction_tony), + _ => None, + }; + if let Some(tex) = stex { + st.bind_2d(ctx, &ost.assets.shader_flat); + tex.bind(ctx); + ost.assets.shader_flat.set_position_2d( + ctx, st, + &glam::Vec2::new(WIDTH - 12.0 - 32.0, 18.0), + &glam::Vec2::new(32.0, 32.0) + ); + st.mesh_square.render(ctx); + } + + // boost text + let boost_pos = glam::Vec2::new(12.0, 105.0); + self.font.render_text_helper(ctx, st, + &boost_pos, + &card.boost_level, + &[glam::Vec3::new(0.1, 0.1, 0.1)] + ); + self.font.render_text_helper(ctx, st, + &(boost_pos - glam::Vec2::new(1.0, 1.0)), + &card.boost_level, + &[glam::Vec3::new(0.9, 0.9, 0.9)] + ); + + // equity marks + let equity_pos = glam::Vec2::new(12.0, 18.0); + for i in 0..card.equity { + self.font.render_text_helper(ctx, st, + &(equity_pos + glam::Vec2::new(0.0, 10.0) * i as f32), + "$", + &[glam::Vec3::new(0.1, 0.1, 0.1)] + ); + } + + // body text + self.draw_rectangle(ctx, st, ost, + glam::Vec4::new(1.0, 1.0, 1.0, 0.5), + glam::Vec2::new(4.0, 119.0), + glam::Vec2::new(152.0, 100.0), + ); + for (i, cs) in card.body_text.chars().collect::>().chunks(25).enumerate() { + let line: String = cs.iter().collect(); + self.font.render_text_helper(ctx, st, + &glam::Vec2::new(5.0, 120.0 + 10.0 * i as f32), + &format!("{}", line), + &[glam::Vec3::new(0.2, 0.2, 0.2)] + ); + } + + // bottom bar + self.draw_rectangle(ctx, st, ost, + glam::Vec4::new(0.0, 0.0, 0.0, 0.8), + glam::Vec2::new(0.0, HEIGHT - 16.0), + glam::Vec2::new(WIDTH, 16.0), + ); + self.font.render_text_helper(ctx, st, + &glam::Vec2::new(1.0, HEIGHT - 15.0), + &format!("{}", card.set), + &[glam::Vec3::new(1.0, 1.0, 1.0)] + ); + self.font.render_text_helper(ctx, st, + &glam::Vec2::new(WIDTH - 7.0 * (card.minted_date.len() - 1) as f32, HEIGHT - 15.0), + &format!("{}", card.minted_date), + &[glam::Vec3::new(1.0, 1.0, 1.0)] + ); + + 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) + } } impl overlay::Overlay for Overlay { @@ -69,6 +274,7 @@ impl overlay::Overlay for Overlay { b"overlay tcg generate" => { let res: Erm<()> = (|| { 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(); @@ -80,43 +286,57 @@ impl overlay::Overlay for Overlay { let g = i64::from_str_radix(&color[3..=4], 16)?; let b = i64::from_str_radix(&color[5..=6], 16)?; let faction = sp.next().ok_or(Error::NotEnoughFields)?.to_owned(); + let faction_color = sp.next().ok_or(Error::NotEnoughFields)?.to_owned(); + let f_r = i64::from_str_radix(&faction_color[1..=2], 16)?; + let f_g = i64::from_str_radix(&faction_color[3..=4], 16)?; + let f_b = i64::from_str_radix(&faction_color[5..=6], 16)?; let equity = sp.next().ok_or(Error::NotEnoughFields)?.parse()?; - let boost_level = sp.next().ok_or(Error::NotEnoughFields)?.parse()?; + let boost_level = sp.next().ok_or(Error::NotEnoughFields)?.to_owned(); let rarity = sp.next().ok_or(Error::NotEnoughFields)?.to_owned(); let rarity_level = sp.next().ok_or(Error::NotEnoughFields)?.parse()?; let body_text = sp.next().ok_or(Error::NotEnoughFields)?.to_owned(); let base_image_name = sp.next().ok_or(Error::NotEnoughFields)?.to_owned(); + 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(); - unsafe { - let img = image::ImageReader::open(format!("crates/renderer/src/assets/textures/tcg/bases/{}.png", base_image_name))?.decode()?.into_rgba8(); - self.texture.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); + 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.card = Some(Card { + let card = Card { name, ty, depicted_subject, element, color: glam::Vec4::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, 1.0), faction, + faction_color: glam::Vec4::new(f_r as f32 / 255.0, f_g as f32 / 255.0, f_b as f32 / 255.0, 1.0), equity, boost_level, rarity, rarity_level, body_text, base_image_name, + set, + minted_date, flags, - }); + }; + if let Some(img) = self.generate_card(ctx, st, ost, card.clone()) { + self.card.set(ctx, card, &img); + let err: Erm<()> = (||{ + let mut buf = Vec::new(); + let mut cursor = std::io::Cursor::new(&mut buf); + img.write_to(&mut cursor, image::ImageFormat::Png)?; + let with_meta = web_image_meta::png::add_text_chunk( + &buf, "lcolonqtcg", &s + )?; + // TODO: write to redis here + std::fs::write("/tmp/card.png", &with_meta)?; + Ok(()) + })(); + if let Err(e) = err { + log::warn!("failed to encode image: {}", e) + } + } Ok(()) })(); if let Err(e) = res { log::warn!("malformed TCG generate: {}", e); } @@ -126,42 +346,11 @@ impl overlay::Overlay for Overlay { Ok(()) } fn render(&mut self, ctx: &context::Context, st: &mut state::State, ost: &mut overlay::State) -> Erm<()> { - if let Some(card) = &self.card { - st.bind_framebuffer(ctx, &self.fb); - ctx.clear(); - - st.bind_2d(ctx, &ost.assets.shader_flat); - self.texture.bind(ctx); - ost.assets.shader_flat.set_f32(ctx, "transparency", 0.0); - ost.assets.shader_flat.set_mat4(ctx, "view", &glam::Mat4::IDENTITY); - ost.assets.shader_flat.set_mat4(ctx, "position", &glam::Mat4::IDENTITY); - st.mesh_square.render(ctx); - - st.bind_2d(ctx, &self.shader_color); - self.shader_color.set_vec4(ctx, "color", &glam::Vec4::new(1.0, 0.0, 0.0, 1.0)); - self.shader_color.set_position_2d( - ctx, st, - &glam::Vec2::new(0.0, 10.0), - &glam::Vec2::new(WIDTH, 32.0) + 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.mesh_square.render(ctx); - - self.font.render_text_helper(ctx, st, - &glam::Vec2::new(0.0, 0.0), - &glam::Vec2::new(21.0, 40.0), - &card.name, - &[] - ); - - st.bind_render_framebuffer(ctx); - st.bind_2d(ctx, &self.shader_screen); - self.fb.bind_texture(ctx); - self.shader_screen.set_position_2d( - ctx, st, - &glam::Vec2::new(1000.0, 200.0), - &glam::Vec2::new(WIDTH, HEIGHT) - ); - st.mesh_square.render(ctx); } Ok(()) } diff --git a/crates/shader/Cargo.toml b/crates/shader/Cargo.toml index 1ccc821..aff4b79 100644 --- a/crates/shader/Cargo.toml +++ b/crates/shader/Cargo.toml @@ -8,7 +8,7 @@ authors.workspace = true crate-type = ["cdylib", "rlib"] [dependencies] -teleia = { git = "https://github.com/lcolonq/teleia" } # engine +teleia = { workspace = true } glam = "*" # linear algebra bitflags = "*" # C-style bitwise flags rand = "=0.8.5" # rng -- cgit v1.2.3