diff options
Diffstat (limited to 'crates/renderer/src/overlay')
| -rw-r--r-- | crates/renderer/src/overlay/automata.rs | 109 |
1 files changed, 84 insertions, 25 deletions
diff --git a/crates/renderer/src/overlay/automata.rs b/crates/renderer/src/overlay/automata.rs index b5d749e..22f7884 100644 --- a/crates/renderer/src/overlay/automata.rs +++ b/crates/renderer/src/overlay/automata.rs @@ -1,6 +1,9 @@ use teleia::*; use glow::HasContext; +use lexpr::sexp; +use base64::prelude::*; +use rand::Rng; use crate::overlay; @@ -34,6 +37,7 @@ impl Pattern { data.push_str(&line); } } + if w == 0 || h == 0 || w > WIDTH || h > HEIGHT { return None } let mut ret = Self { w, h, cells: vec![false; w * h], @@ -91,7 +95,7 @@ impl Pattern { type Cell = u8; struct CellRule { - color: glam::Vec4, + color: [u8; 4], } struct CellBuffer { @@ -111,6 +115,17 @@ impl CellBuffer { pub fn get(&self, x: i32, y: i32) -> Cell { self.buf[Self::idx(x, y)] } + pub fn neighbors(&self, x: i32, y: i32) -> [Cell; 8] { + [ self.get(x-1, y-1), + self.get(x, y-1), + self.get(x+1, y-1), + self.get(x-1, y), + self.get(x+1, y), + self.get(x-1, y+1), + self.get(x, y+1), + self.get(x+1, y+1), + ] + } pub fn is_nonzero(&self, x: i32, y: i32) -> bool { self.get(x, y) > 0 } @@ -118,9 +133,22 @@ impl CellBuffer { self.get(x, y).min(1) as i32 } pub fn count_neighbors(&self, x: i32, y: i32) -> i32 { - self.count_cell(x-1, y-1) + self.count_cell(x, y-1) + self.count_cell(x+1, y-1) - + self.count_cell(x-1, y) + self.count_cell(x+1, y) - + self.count_cell(x-1, y+1) + self.count_cell(x, y+1) + self.count_cell(x+1, y+1) + self.neighbors(x, y).into_iter().map(|c| c.min(1) as i32).sum() + } + pub fn most_common_neighbor(&self, x: i32, y: i32) -> u8 { + let mut ns = self.neighbors(x, y); + ns.sort_unstable(); + let mut winner = 0; + let mut score = 0; + let mut cur = 0; + let mut curscore = 0; + for c in ns { + if c == 0 { continue; } + if c != cur { cur = c; curscore = 1; } + else { curscore += 1; } + if curscore >= score { winner = c; score = curscore; } + } + winner } pub fn set(&mut self, x: i32, y: i32, v: Cell) { self.buf[Self::idx(x, y)] = v; @@ -133,15 +161,16 @@ pub struct Overlay { active: bool, buf0: CellBuffer, buf1: CellBuffer, + next_rule: usize, rules: [CellRule; 256], } impl Overlay { pub fn new(ctx: &context::Context) -> Self { let rules = std::array::from_fn(|idx| match idx { - 0 => CellRule { color: glam::Vec4::new(0.0, 0.0, 0.0, 0.0) }, - _ => CellRule { color: glam::Vec4::new(1.0, 1.0, 1.0, 1.0) }, + 0 => CellRule { color: [0, 0, 0, 0] }, + _ => CellRule { color: [0xff, 0xff, 0xff, 0xff] }, }); - let mut ret = Self { + Self { shader: shader::Shader::new( ctx, include_str!("../assets/shaders/automata/vert.glsl"), @@ -151,21 +180,9 @@ impl Overlay { active: false, buf0: CellBuffer::new(), buf1: CellBuffer::new(), + next_rule: 1, rules, - }; - if let Some(pat) = Pattern::from_rle(" -#N Tanner's p46 -#O Tanner Jacobi -#C A period 46 oscillator discovered by Tanner Jacobi in October 2017. -#C https://conwaylife.com/wiki/Tanner%27s_p46 -x = 13, y = 26, rule = B3/S23 -2b2o9b$2bo10b$3bo9b$2b2o9b$13b$9b2o2b$9bo3b$10bo2b$9b2o2b$b2o10b$b2o6b -2o2b$o7bobo2b$b2o6bo3b$b2o7b3o$12bo$13b$13b$13b$13b$13b$13b$b2o10b$b2o -2b2o6b$5bobo5b$7bo5b$7b2o4b! -") { - ret.spawn(30, 10, 1, &pat); } - ret } pub fn spawn(&mut self, x: i32, y: i32, c: Cell, pat: &Pattern) { let cur = if self.active { &mut self.buf0 } else { &mut self.buf1 }; @@ -189,7 +206,7 @@ x = 13, y = 26, rule = B3/S23 if cur.is_nonzero(x, y) && n != 2 && n != 3{ next.set(x, y, 0) } else if n == 3 { - next.set(x, y, 1) + next.set(x, y, cur.most_common_neighbor(x, y)) } else { next.set(x, y, cur.get(x, y)) } @@ -199,25 +216,67 @@ x = 13, y = 26, rule = B3/S23 } pub fn upload(&self, ctx: &context::Context) { let cur = if self.active { &self.buf0 } else { &self.buf1 }; + let mut buf = vec![0; WIDTH * HEIGHT * 4]; + for (idx, c) in cur.buf.iter().enumerate() { + for off in 0..4 { buf[idx * 4 + off] = self.rules[*c as usize].color[off] } + } unsafe { - let err = ctx.gl.get_error(); self.tex.bind(ctx); ctx.gl.tex_image_2d( glow::TEXTURE_2D, 0, - glow::R8 as i32, + glow::RGBA as i32, WIDTH as i32, HEIGHT as i32, 0, - glow::RED, + glow::RGBA, glow::UNSIGNED_BYTE, - Some(&cur.buf), + Some(&buf), ); ctx.gl.generate_mipmap(glow::TEXTURE_2D); } } + pub fn handle_spawn(&mut self, msg: fig::SexpMessage) -> Option<()> { + let bs = BASE64_STANDARD.decode(msg.data.get(0)?.as_str()?).ok()?; + let s = std::str::from_utf8(&bs).ok()?; + let bcol = BASE64_STANDARD.decode(msg.data.get(2)?.as_str()?).ok()?; + let scol = std::str::from_utf8(&bcol).ok()?.trim().strip_prefix("#")?; + let col = u32::from_str_radix(scol, 16).ok()?; + let r = (col >> 16 & 0xff) as u8; + let g = (col >> 8 & 0xff) as u8; + let b = (col & 0xff) as u8; + if let Some(pat) = Pattern::from_rle(s) { + let mut rng = rand::thread_rng(); + let x = rng.gen_range(0..WIDTH); + let y = rng.gen_range(0..HEIGHT); + self.rules[self.next_rule] = CellRule { color: [r, g, b, 0xff] }; + self.spawn(x as i32, y as i32, self.next_rule as u8, &pat); + self.next_rule = (self.next_rule + 1) % 256; + if self.next_rule == 0 { self.next_rule = 1; } + } + Some(()) + } } impl overlay::Overlay for Overlay { + fn reset(&mut self, ctx: &context::Context, st: &mut state::State, _ost: &mut overlay::State) -> Erm<()> { + let cur = if self.active { &mut self.buf0 } else { &mut self.buf1 }; + for ux in 0..WIDTH { + for uy in 0..HEIGHT { + cur.set(ux as i32, uy as i32, 0) + } + } + Ok(()) + } + fn handle( + &mut self, ctx: &context::Context, st: &mut state::State, _ost: &mut overlay::State, + msg: fig::SexpMessage, + ) -> Erm<()> { + let malformed = format!("malformed {} data: {}", msg.event, msg.data); + if msg.event == sexp!((avatar automata spawn)) { + if self.handle_spawn(msg).is_none() { log::warn!("{}", malformed) } + } + Ok(()) + } fn update(&mut self, ctx: &context::Context, st: &mut state::State, _ost: &mut overlay::State) -> Erm<()> { if st.tick % 10 == 0 { self.step(); |
