diff options
| -rw-r--r-- | Cargo.lock | 50 | ||||
| -rw-r--r-- | Cargo.toml | 4 | ||||
| -rw-r--r-- | flake.lock | 23 | ||||
| -rw-r--r-- | flake.nix | 2 | ||||
| -rw-r--r-- | src/common/overlay.rs | 26 | ||||
| -rw-r--r-- | src/common/overlay/terminal.rs | 31 | ||||
| -rw-r--r-- | src/main.rs | 11 |
7 files changed, 139 insertions, 8 deletions
@@ -422,6 +422,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + +[[package]] name = "combine" version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1497,6 +1507,17 @@ dependencies = [ ] [[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall 0.5.8", +] + +[[package]] name = "linux-raw-sys" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1642,6 +1663,7 @@ dependencies = [ "base64 0.22.1", "bitflags 2.4.2", "clap", + "colored", "env_logger", "glam", "glow", @@ -1652,6 +1674,7 @@ dependencies = [ "rand", "strum", "teleia", + "termion", "tokio", "wasm-bindgen", "wasm-bindgen-futures", @@ -1709,6 +1732,12 @@ dependencies = [ ] [[package]] +name = "numtoa" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f" + +[[package]] name = "objc-sys" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1834,7 +1863,7 @@ version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" dependencies = [ - "libredox", + "libredox 0.0.2", ] [[package]] @@ -2081,6 +2110,12 @@ dependencies = [ ] [[package]] +name = "redox_termios" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" + +[[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2699,6 +2734,7 @@ name = "teleia" version = "0.1.0" dependencies = [ "bimap", + "bitflags 2.4.2", "bytes", "console_error_panic_hook", "console_log", @@ -2740,6 +2776,18 @@ dependencies = [ ] [[package]] +name = "termion" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eaa98560e51a2cf4f0bb884d8b2098a9ea11ecf3b7078e9c68242c74cc923a7" +dependencies = [ + "libc", + "libredox 0.1.3", + "numtoa", + "redox_termios", +] + +[[package]] name = "thiserror" version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -38,4 +38,6 @@ wasm-bindgen-futures = "*" # interface with async javascript env_logger = "*" tokio = { version = "*", features = ["full"] } clap = { version = "*", features = ["cargo"] } -base64 = "0.22.1"
\ No newline at end of file +base64 = "0.22.1" +colored = "2.2.0" +termion = "*"
\ No newline at end of file @@ -76,7 +76,8 @@ "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", "nixpkgs-for-wasm-bindgen": "nixpkgs-for-wasm-bindgen", - "rust-overlay": "rust-overlay" + "rust-overlay": "rust-overlay", + "st": "st" } }, "rust-overlay": { @@ -99,6 +100,26 @@ "type": "github" } }, + "st": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1738300089, + "narHash": "sha256-kcLup2C1MlD/yiN7quujBSGfEH6356QggnqI+sVQHR4=", + "owner": "lcolonq", + "repo": "st", + "rev": "e982b1d75eda54ab6b3051675fb21d32b3731bb5", + "type": "github" + }, + "original": { + "owner": "lcolonq", + "repo": "st", + "type": "github" + } + }, "systems": { "locked": { "lastModified": 1681028828, @@ -73,7 +73,7 @@ { packages = { default = game; - inherit st; + st = st.packages.x86_64-linux.st; }; devShells.default = craneLib.devShell { diff --git a/src/common/overlay.rs b/src/common/overlay.rs index 6e68974..492f1bd 100644 --- a/src/common/overlay.rs +++ b/src/common/overlay.rs @@ -4,12 +4,19 @@ mod terminal; mod fig; use teleia::*; +use termion::raw::IntoRawMode; use std::{collections::HashMap, f32::consts::PI}; use lexpr::sexp; use base64::prelude::*; +pub enum RenderMode { + Overlay, + Terminal(termion::raw::RawTerminal<std::io::Stdout>), +} + pub struct Overlay { + mode: RenderMode, assets: assets::Assets, model: scene::Scene, model_neck_base: glam::Mat4, @@ -22,13 +29,14 @@ pub struct Overlay { } impl Overlay { - pub async fn new(ctx: &context::Context) -> Self { + pub async fn new(ctx: &context::Context, mode: RenderMode) -> Self { let model = scene::Scene::from_gltf(ctx, include_bytes!("overlay/assets/scenes/lcolonq.vrm")); let model_neck_base = model.nodes_by_name.get("J_Bip_C_Neck") .and_then(|i| model.nodes.get(*i)) .expect("failed to find neck joint") .transform; Self { + mode, assets: assets::Assets::new(ctx), model, model_neck_base, @@ -50,6 +58,13 @@ impl Overlay { tracking_neck: glam::Quat::IDENTITY, } } + pub async fn overlay(ctx: &context::Context) -> Self { + Self::new(ctx, RenderMode::Overlay).await + } + pub async fn terminal(ctx: &context::Context) -> Self { + let raw_stdout = std::io::stdout().into_raw_mode().expect("failed to set raw mode"); + Self::new(ctx, RenderMode::Terminal(raw_stdout)).await + } pub fn handle_tracking(&mut self, msg: fig::Message) -> Option<()> { let eyes = msg.data.get(0)?; let eye_left = eyes.get(0)?.as_str()?.parse::<f32>().ok()?; @@ -135,7 +150,14 @@ impl teleia::state::Game for Overlay { self.model.render(ctx, &self.assets.shader_scene); st.render_framebuffer.bind(ctx); self.terminal.update(ctx, &self.model_fb); - self.terminal.render(ctx, &glam::Vec2::new(12.0, 250.0)); + match &mut self.mode { + RenderMode::Overlay => { + self.terminal.render(ctx, &glam::Vec2::new(12.0, 250.0)); + }, + RenderMode::Terminal(stdout) => if st.tick % 10 == 0 { + self.terminal.write_tty(stdout); + }, + } // self.model_fb.blit( // ctx, &st.render_framebuffer, // &glam::Vec2::new(ctx.render_width / 2.0 - 512.0, ctx.render_height / 2.0 - 512.0), diff --git a/src/common/overlay/terminal.rs b/src/common/overlay/terminal.rs index 1b8df4c..e631899 100644 --- a/src/common/overlay/terminal.rs +++ b/src/common/overlay/terminal.rs @@ -1,3 +1,6 @@ +use std::io::Write; +use colored::Colorize; + use teleia::*; pub const WIDTH: usize = 64; @@ -164,4 +167,32 @@ impl Terminal { } self.font.render_text_helper(ctx, pos, &s, &colors); } + pub fn write_tty<W>(&self, out: &mut W) + where W: Write { + let mut output: Vec<u8> = Vec::new(); + write!(output, "\x1b[2J\x1b[1;1H").expect("failed to write output"); + for row in 0..64 { + for col in 0..64 { + let pos = Pos::new(col, row); + let c = self.get_color(pos); + let new = if let Some(p) = self.set_char.get(pos) { + if c == glam::Vec3::new(0.0, 0.0, 0.0) { + String::from(" ") + } else if let Some(pat) = self.outline_pattern(pos) { + pat + } else { + format!("{}{}", p.first, if let Some(snd) = p.second { snd } else { ' ' }) + } + } else { + String::from(" ") + }; + write!( + output, "{}", + new.truecolor((c.x * 255.0) as u8, (c.y * 255.0) as u8, (c.z * 255.0) as u8) + ).unwrap(); + } + write!(output, "\r\n").unwrap(); + } + out.write(&output).expect("failed to write to terminal"); + } } diff --git a/src/main.rs b/src/main.rs index 8306cae..af8e753 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,11 @@ pub async fn main() { .arg_required_else_help(true) .subcommand( Command::new("overlay") - .about("Run the LCOLONQ model renderer / overlay") + .about("Run the LCOLONQ model renderer in a full-screen transparent overlay") + ) + .subcommand( + Command::new("terminal") + .about("Run the LCOLONQ model renderer in a terminal") ) .subcommand( Command::new("server") @@ -24,7 +28,10 @@ pub async fn main() { .get_matches(); match matches.subcommand() { Some(("overlay", _cm)) => { - teleia::run("LCOLONQ", 1920, 1080, true, common::overlay::Overlay::new).await; + teleia::run("LCOLONQ", 1920, 1080, teleia::Options::OVERLAY, common::overlay::Overlay::overlay).await; + }, + Some(("terminal", _cm)) => { + teleia::run("LCOLONQ", 1920, 1080, teleia::Options::HIDDEN, common::overlay::Overlay::terminal).await; }, Some(("server", _cm)) => { env_logger::Builder::new().filter(None, log::LevelFilter::Info).init(); |
