summaryrefslogtreecommitdiff
path: root/crates/renderer/src/overlay.rs
diff options
context:
space:
mode:
authorLLLL Colonq <llll@colonq>2025-11-06 23:50:33 -0500
committerLLLL Colonq <llll@colonq>2025-11-06 23:50:33 -0500
commite8bf4d7dde7d4f450458deb0612eb3bc14994901 (patch)
tree5ce52d534aff87bcbdb017fd62c6695597c01515 /crates/renderer/src/overlay.rs
parent0e8b1dded85602aa2dc15f27c3c89800e4c3402b (diff)
Update
Diffstat (limited to 'crates/renderer/src/overlay.rs')
-rw-r--r--crates/renderer/src/overlay.rs235
1 files changed, 112 insertions, 123 deletions
diff --git a/crates/renderer/src/overlay.rs b/crates/renderer/src/overlay.rs
index bd0a24d..af02eda 100644
--- a/crates/renderer/src/overlay.rs
+++ b/crates/renderer/src/overlay.rs
@@ -2,22 +2,20 @@ pub mod model;
pub mod shader;
pub mod drawing;
pub mod automata;
-pub mod irish;
pub mod tcg;
pub mod loopback;
use teleia::*;
use std::f32::consts::PI;
-use lexpr::sexp;
-use base64::prelude::*;
+use byteorder::{LE, ReadBytesExt};
use crate::{assets, fig, toggle, input, background};
pub struct Chat {
author: String,
msg: String,
- time: f64,
+ time: f32,
biblicality: f32,
}
impl Chat {
@@ -30,19 +28,27 @@ impl Chat {
}
}
}
+
+pub struct Tracking {
+ eyes: (f32, f32),
+ mouth: f32,
+ neck: glam::Quat,
+}
+
+pub struct Info {
+ mouse_cursor: (f32, f32),
+ emacs_cursor: (f32, f32),
+ emacs_heartrate: i32,
+ muzak_author: Option<String>,
+}
+
pub struct State {
assets: assets::Assets,
model: scene::Scene,
model_neck_base: glam::Mat4,
- fig: fig::SexpClient,
fig_binary: fig::BinaryClient,
- tracking_eyes: (f32, f32),
- tracking_mouth: f32,
- tracking_neck: glam::Quat,
- emacs_cursor: (f32, f32),
- mouse_cursor: (f32, f32),
- emacs_heartrate: i32,
- muzak_author: Option<String>,
+ tracking: Tracking,
+ info: Info,
chat: Chat,
toggles: toggle::Toggles,
input: input::Input,
@@ -59,36 +65,35 @@ impl State {
assets: assets::Assets::new(ctx),
model,
model_neck_base,
- fig: fig::SexpClient::new("shiro:32050", &[
- sexp!((avatar toggle)),
- sexp!((avatar toggle set)),
- sexp!((avatar toggle unset)),
- sexp!((avatar text)),
- sexp!((avatar frame)),
- sexp!((avatar reset)),
- sexp!((avatar tracking)),
- sexp!((avatar overlay shader)),
- sexp!((avatar overlay muzak)),
- sexp!((avatar overlay muzak clear)),
- sexp!((avatar overlay chat)),
- sexp!((avatar overlay cursor)),
- sexp!((avatar overlay emacs)),
- sexp!((avatar automata spawn)),
- sexp!((overlay irish start)),
- sexp!((overlay irish update)),
- sexp!((overlay irish save)),
- ]),
- fig_binary: fig::BinaryClient::new("shiro:32051", false, &[
- b"background frame",
+ fig_binary: fig::BinaryClient::new("shiro:32051", &[
+ b"overlay reset",
+ b"overlay tracking",
+ b"overlay background frame",
+ b"overlay toggle",
+ b"overlay toggle set",
+ b"overlay toggle unset",
+ b"overlay info emacs",
+ b"overlay info emacs cursor",
+ b"overlay info credits music",
+ b"overlay info credits music clear",
+ b"overlay chat",
+ b"overlay avatar text",
+ b"overlay shader",
+ b"overlay shader chat",
+ b"overlay automata spawn",
b"overlay tcg generate",
- ]),
- tracking_eyes: (1.0, 1.0),
- tracking_mouth: 0.0,
- tracking_neck: glam::Quat::IDENTITY,
- emacs_cursor: (0.0, 0.0),
- mouse_cursor: (0.0, 0.0),
- emacs_heartrate: 0,
- muzak_author: None,
+ ]).expect("failed to connect to bus"),
+ tracking: Tracking {
+ eyes: (1.0, 1.0),
+ mouth: 0.0,
+ neck: glam::Quat::IDENTITY,
+ },
+ info: Info {
+ emacs_cursor: (0.0, 0.0),
+ mouse_cursor: (0.0, 0.0),
+ emacs_heartrate: 0,
+ muzak_author: None,
+ },
chat: Chat::new(),
toggles: toggle::Toggles::new(),
backgrounds: background::Backgrounds::new(ctx),
@@ -98,53 +103,6 @@ impl State {
fn reset(&mut self, ctx: &context::Context, st: &mut state::State) {
self.toggles.reset();
}
- pub fn handle_tracking(&mut self, msg: fig::SexpMessage) -> Option<()> {
- let eyes = msg.data.get(0)?;
- let eye_left = eyes.get(0)?.as_str()?.parse::<f32>().ok()?;
- let eye_right = eyes.get(1)?.as_str()?.parse::<f32>().ok()?;
- let mouth = msg.data.get(1)?.as_str()?.parse::<f32>().ok()?;
- let euler = msg.data.get(2)?;
- let euler_x = euler.get(0)?.as_str()?.parse::<f32>().ok()?.to_radians();
- let euler_y = PI - euler.get(1)?.as_str()?.parse::<f32>().ok()?.to_radians();
- let euler_z = euler.get(2)?.as_str()?.parse::<f32>().ok()?.to_radians() + PI/2.0;
- self.tracking_eyes = (eye_left, eye_right);
- self.tracking_mouth = mouth;
- self.tracking_neck = glam::Quat::from_euler(glam::EulerRot::XYZ, euler_x, euler_y, euler_z);
- Some(())
- }
- fn handle_overlay_muzak(&mut self, msg: fig::SexpMessage) -> Option<()> {
- let ba = BASE64_STANDARD.decode(msg.data.get(0)?.as_str()?).ok()?;
- let author = String::from_utf8_lossy(&ba);
- self.muzak_author = Some(author.to_string());
- Some(())
- }
- fn handle_overlay_muzak_clear(&mut self) -> Option<()> {
- self.muzak_author = None;
- Some(())
- }
- fn handle_overlay_chat(&mut self, msg: fig::SexpMessage) -> Option<()> {
- 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(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;
- Some(())
- }
- fn handle_overlay_cursor(&mut self, msg: fig::SexpMessage) -> Option<()> {
- let cursor_x = msg.data.get(0)?.as_i64()? as f32;
- let cursor_y = msg.data.get(1)?.as_i64()? as f32;
- self.emacs_cursor = (cursor_x, cursor_y);
- Some(())
- }
- fn handle_overlay_emacs(&mut self, msg: fig::SexpMessage) -> Option<()> {
- self.emacs_heartrate = msg.data.get(0)?.as_i64()? as i32;
- Some(())
- }
fn update(&mut self, ctx: &context::Context, st: &mut state::State) -> Erm<()> {
st.move_camera(
ctx,
@@ -153,9 +111,10 @@ impl State {
&glam::Vec3::new(0.0, 1.0, 0.0),
);
let (x, y) = self.input.get_mouse();
- self.mouse_cursor = (x as f32, y as f32);
+ self.info.mouse_cursor = (x as f32, y as f32);
+ // update model head transform based on tracking state
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);
+ n.transform = self.model_neck_base * glam::Mat4::from_quat(self.tracking.neck);
}
Ok(())
}
@@ -168,10 +127,7 @@ pub trait Overlay {
fn update(&mut self, ctx: &context::Context, st: &mut state::State, ost: &mut State) -> Erm<()> {
Ok(())
}
- fn handle(&mut self, ctx: &context::Context, st: &mut state::State, ost: &mut State, msg: fig::SexpMessage) -> Erm<()> {
- Ok(())
- }
- fn handle_binary(&mut self, ctx: &context::Context, st: &mut state::State, ost: &mut State, msg: fig::BinaryMessage) -> Erm<()> {
+ fn handle_binary(&mut self, ctx: &context::Context, st: &mut state::State, ost: &mut State, msg: &fig::BinaryMessage) -> Erm<()> {
Ok(())
}
fn render(&mut self, ctx: &context::Context, st: &mut state::State, ost: &mut State) -> Erm<()> {
@@ -206,45 +162,78 @@ impl teleia::state::Game for Overlays {
&glam::Vec3::new(0.0, 0.0, -1.0),
&glam::Vec3::new(0.0, 1.0, 0.0),
);
- while let Some(msg) = self.state.fig.pump() {
- let malformed = format!("malformed {} data: {}", msg.event, msg.data);
+ while let Some(msg) = self.state.fig_binary.pump()? {
for ov in self.overlays.iter_mut() {
- ov.handle(ctx, st, &mut self.state, msg.clone())?;
+ ov.handle_binary(ctx, st, &mut self.state, &msg)?;
}
- if msg.event == sexp!((avatar tracking)) {
- if self.state.handle_tracking(msg).is_none() { log::warn!("{}", malformed) }
- } else if msg.event == sexp!((avatar reset)) {
- self.reset(ctx, st)?;
- } else if msg.event == sexp!((avatar toggle)) {
- if self.state.toggles.handle(ctx, st, msg).is_none() { log::warn!("{}", malformed) }
- } else if msg.event == sexp!((avatar toggle set)) {
- if self.state.toggles.handle_set(ctx, st, msg, true).is_none() { log::warn!("{}", malformed) }
- } else if msg.event == sexp!((avatar toggle unset)) {
- if self.state.toggles.handle_set(ctx, st, msg, false).is_none() { log::warn!("{}", malformed) }
- } else if msg.event == sexp!((avatar overlay muzak)) {
- if self.state.handle_overlay_muzak(msg).is_none() { log::warn!("{}", malformed) }
- } else if msg.event == sexp!((avatar overlay muzak clear)) {
- if self.state.handle_overlay_muzak_clear().is_none() { log::warn!("{}", malformed) }
- } else if msg.event == sexp!((avatar overlay chat)) {
- if self.state.handle_overlay_chat(msg).is_none() { log::warn!("{}", malformed) }
- } else if msg.event == sexp!((avatar overlay cursor)) {
- if self.state.handle_overlay_cursor(msg).is_none() { log::warn!("{}", malformed) }
- }
- }
- while let Some(msg) = self.state.fig_binary.pump() {
match &*msg.event {
- b"background frame" => {
+ b"overlay reset" => self.reset(ctx, st)?,
+ b"overlay tracking" => {
+ let res: Erm<()> = (|| {
+ let mut reader = std::io::Cursor::new(&msg.data);
+ let eye_left = reader.read_f32::<LE>()?;
+ let eye_right = reader.read_f32::<LE>()?;
+ let mouth = reader.read_f32::<LE>()?;
+ let euler_x = reader.read_f32::<LE>()?.to_radians();
+ let euler_y = PI - reader.read_f32::<LE>()?.to_radians();
+ let euler_z = reader.read_f32::<LE>()?.to_radians() + PI/2.0;
+ self.state.tracking.eyes = (eye_left, eye_right);
+ self.state.tracking.mouth = mouth;
+ self.state.tracking.neck = glam::Quat::from_euler(
+ glam::EulerRot::XYZ, euler_x, euler_y, euler_z
+ );
+ Ok(())
+ })();
+ if let Err(e) = res { log::warn!("malformed tracking update: {}", e); }
+ },
+ b"overlay background frame" => {
if let Some(f) = background::Frame::parse(&mut &*msg.data) {
self.state.backgrounds.update(ctx, f);
} else {
- log::warn!("failed to parse frame");
+ log::warn!("malformed background frame");
}
},
- _ => {
- for ov in self.overlays.iter_mut() {
- ov.handle_binary(ctx, st, &mut self.state, msg.clone())?;
+ b"overlay toggle" => self.state.toggles.handle(ctx, st, msg),
+ b"overlay toggle set" => self.state.toggles.handle_set(ctx, st, msg, true),
+ b"overlay toggle unset" => self.state.toggles.handle_set(ctx, st, msg, false),
+ b"overlay info emacs" => {
+ let mut reader = std::io::Cursor::new(&msg.data);
+ match reader.read_i32::<LE>() {
+ Ok(heartrate) => self.state.info.emacs_heartrate = heartrate,
+ Err(e) => log::warn!("malformed Emacs update: {}", e),
}
},
+ 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>()?;
+ self.state.info.emacs_cursor = (cursor_x, cursor_y);
+ Ok(())
+ })();
+ if let Err(e) = res { log::warn!("malformed Emacs cursor update: {}", e); }
+ },
+ b"overlay info credits music" => match str::from_utf8(&msg.data) {
+ Ok(nm) => self.state.info.muzak_author = Some(nm.to_owned()),
+ Err(e) => log::warn!("malformed music credits update: {}", e),
+ },
+ b"overlay info credits music clear" => { self.state.info.muzak_author = None; },
+ b"overlay chat" => {
+ let res: Erm<()> = (|| {
+ 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>()?;
+ self.state.chat.author = author;
+ self.state.chat.msg = msg;
+ self.state.chat.time = time;
+ self.state.chat.biblicality = biblicality;
+ Ok(())
+ })();
+ if let Err(e) = res { log::warn!("malformed chat update: {}", e); }
+ },
+ _ => {},
}
}
self.state.update(ctx, st)?;