summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLLLL Colonq <llll@colonq>2025-11-13 22:04:56 -0500
committerLLLL Colonq <llll@colonq>2025-11-13 22:04:56 -0500
commita76f583c2d11043a94c3251ec6a5381d7836bdb4 (patch)
treebcb6a085fa5114be1354efa79cc6e191095f8253
parente8bf4d7dde7d4f450458deb0612eb3bc14994901 (diff)
Add TCG
-rw-r--r--Cargo.lock41
-rw-r--r--Cargo.toml4
-rw-r--r--crates/client/Cargo.toml2
-rw-r--r--crates/renderer/Cargo.toml5
-rw-r--r--crates/renderer/src/assets.rs24
-rw-r--r--crates/renderer/src/assets/shaders/flat/frag.glsl3
-rw-r--r--crates/renderer/src/assets/shaders/tcg_base/frag.glsl60
-rw-r--r--crates/renderer/src/assets/shaders/tcg_base/vert.glsl4
-rw-r--r--crates/renderer/src/assets/shaders/tcg_screen/frag.glsl7
-rw-r--r--crates/renderer/src/assets/textures/tcg/factions/lever.pngbin0 -> 366 bytes
-rw-r--r--crates/renderer/src/assets/textures/tcg/factions/nate.pngbin0 -> 323 bytes
-rw-r--r--crates/renderer/src/assets/textures/tcg/factions/tony.pngbin0 -> 272 bytes
-rw-r--r--crates/renderer/src/overlay.rs16
-rw-r--r--crates/renderer/src/overlay/tcg.rs325
-rw-r--r--crates/shader/Cargo.toml2
15 files changed, 408 insertions, 85 deletions
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"
@@ -1719,6 +1728,15 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1940,6 +1958,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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",
@@ -4061,6 +4085,21 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
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
--- /dev/null
+++ b/crates/renderer/src/assets/textures/tcg/factions/lever.png
Binary files 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
--- /dev/null
+++ b/crates/renderer/src/assets/textures/tcg/factions/nate.png
Binary files 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
--- /dev/null
+++ b/crates/renderer/src/assets/textures/tcg/factions/tony.png
Binary files 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::<LE>()?;
- let cursor_y = reader.read_f32::<LE>()?;
+ 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::<LE>()?;
- let biblicality = reader.read_f32::<LE>()?;
+ 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<Card>,
+ 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<Card>,
+ 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<image::RgbaImage> {
+ 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::<Vec<char>>().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