use crate::font::BakedFont; use crate::generated::fonts::{FONT_CHERRY_BOMB_ONE, FONT_GALMURI}; use rand::Rng; use sdl2::pixels::{Color, PixelFormatEnum}; use sdl2::rect::Rect; use sdl2::render::{Canvas, Texture, TextureCreator}; use sdl2::surface::Surface; use sdl2::video::{Window, WindowContext}; use std::ops::DerefMut; fn dvd_logo_offset(t: f64, screen_size_x: f64, screen_size_y: f64) -> (f64, f64) { let offset_x = t % (screen_size_x * 2.0); let offset_x = if offset_x > screen_size_x { screen_size_x * 2.0 - offset_x } else { offset_x }; let offset_y = t % (screen_size_y * 2.0); let offset_y = if offset_y > screen_size_y { screen_size_y * 2.0 - offset_y } else { offset_y }; (offset_x, offset_y) } pub const START_UWUSPACE: f64 = 0.0; pub const START_BOUNCE: f64 = 0.5; pub const START_SIN: f64 = 12.0; pub const START_COMETOCOSIN: f64 = 18.0; #[cfg(not(feature = "32k"))] pub const SCENE_MOAR: f64 = 30.0; #[cfg(not(feature = "32k"))] pub const SCENE_GREETINGS: f64 = SCENE_MOAR + 33.0; #[cfg(not(feature = "32k"))] pub const JUST_DVD: f64 = SCENE_GREETINGS + 27.0; pub const PRINT_TIME: bool = true; pub fn render( canvas: &mut Canvas, texture_creator: &TextureCreator, time_seconds: f64, ) { if PRINT_TIME { println!("Time: {time_seconds}\x1b[F"); } let colourthing = ((time_seconds * 60.0) % 510.0).round(); let colourthing = if colourthing > 255.0 { (255.0 - (colourthing - 255.0)) as u8 } else { colourthing as u8 }; #[cfg(not(feature = "logical"))] let win_size = canvas.window().drawable_size(); #[cfg(feature = "logical")] let win_size = canvas.logical_size(); canvas.set_draw_color(Color::RGB(12, 12, 12)); canvas.clear(); #[cfg(not(feature = "32k"))] let is_dvd = (time_seconds < SCENE_GREETINGS && time_seconds < SCENE_MOAR) || time_seconds >= JUST_DVD; #[cfg(feature = "32k")] let is_dvd = true; if is_dvd { #[cfg(not(feature = "32k"))] let time_seconds = if time_seconds >= JUST_DVD { time_seconds - JUST_DVD + 15.0 } else { time_seconds }; if time_seconds >= START_UWUSPACE { let bounce_speed = 90.0; let padding_x = 16.0; let padding_y = 16.0; let (offset_x, offset_y) = if time_seconds > START_BOUNCE { let mut uwu_width = padding_x; let mut uwu_height: f32 = padding_y; for c in "UwU-Space".chars() { let char = FONT_CHERRY_BOMB_ONE.get_char(c); uwu_width += char.advance_width; let nh = f32::from(char.height) + padding_y; if nh > uwu_height { uwu_height = nh; } } let virtual_screen_size = ( f64::from(win_size.0) - (uwu_width + padding_x), f64::from(win_size.1) - f64::from(uwu_height + padding_y), ); let t = (time_seconds - START_BOUNCE) * bounce_speed; let (offset_x, offset_y) = dvd_logo_offset(t, virtual_screen_size.0, virtual_screen_size.1); ( (padding_x + offset_x).round() as i32, (f64::from(padding_y) + offset_y).round() as i32, ) } else { (padding_x.round() as i32, padding_y.round() as i32) }; let mut offset: f64 = 0.0; for c in "UwU-Space".chars() { let char = FONT_CHERRY_BOMB_ONE.get_char(c); canvas .copy( &char .to_texture( texture_creator, Color::RGB(colourthing, 64, 255 - colourthing), ) .unwrap(), None, char.to_rect(offset as i32 + offset_x, offset_y), ) .unwrap(); offset += char.advance_width; } } } #[cfg(not(feature = "32k"))] let no_other_rendering = time_seconds >= JUST_DVD; #[cfg(feature = "32k")] let no_other_rendering = false; #[cfg(not(feature = "32k"))] let is_moar_scene = time_seconds >= SCENE_MOAR && time_seconds < SCENE_GREETINGS; #[cfg(feature = "32k")] let is_moar_scene = false; #[cfg(not(feature = "32k"))] let is_greetings_scene = time_seconds >= SCENE_GREETINGS; #[cfg(feature = "32k")] let is_greetings_scene = false; if no_other_rendering { // } else if is_moar_scene { #[cfg(not(feature = "32k"))] { let time_seconds = time_seconds - SCENE_MOAR; // Greetings let greeting_header_duration = 2.0; { let mut rng = rand::thread_rng(); let mut i = 0.0; let strlen = 19; let mut uwuoffset_x = 0.0; for c in { if time_seconds + 0.75 >= (SCENE_GREETINGS - SCENE_MOAR) { "oh look a 64k rust demo".chars() } else { "Have some scrollers: x3".chars() } } { let char: crate::font::RenderableCharacter = FONT_GALMURI.get_char(c); canvas .copy( &char .to_texture( texture_creator, Color::RGB(colourthing, 64, 255 - colourthing), ) .unwrap(), None, char.to_rect( uwuoffset_x as i32 + 18 + rng.gen_range(-2..2), (24) + rng.gen_range(-3..3), ), ) .unwrap(); uwuoffset_x += char.advance_width; i += 1.0; if i > (time_seconds * (f64::from(strlen) / greeting_header_duration)).floor() { break; } } let mut offset_y = 36; for line in [ "Pls come to CoSin 2025 Credits", " May 29th - June 1st 2025 UwU :3 ceemos", " Villa Ritter 30CHF, paid on-site memdmp", " Biel/Bienne https://cosin.ch fence", " Switzerland /dev/urandom", "", "", "", " Matrix: #cosin:fairydust.space Mastodon: @CoSin@mastodon.social" ] { offset_y += 24; let mut offset_x: f64 = f64::from(win_size.0) - ((time_seconds - greeting_header_duration) * (f64::from(win_size.0) * 0.078125)); for c in line.chars() { let char = FONT_GALMURI.get_char(c); canvas .copy( &char .to_texture( texture_creator, Color::RGB(colourthing, 64, 255 - colourthing), ) .unwrap(), None, char.to_rect( offset_x as i32 + rng.gen_range(-2..2), offset_y + rng.gen_range(-2..2), ), ) .unwrap(); offset_x += char.advance_width; } } } } } else if is_greetings_scene { #[cfg(not(feature = "32k"))] { let time_seconds = time_seconds - SCENE_GREETINGS; // Greetings let greeting_header_duration = 4.0; { let mut rng = rand::thread_rng(); let mut i = 0.0; let strlen = 43; let mut uwuoffset_x = 0.0; for c in "Greetings by UwU-Space to:".chars() { let is_not_uwu = i <= 12.0 || i >= 22.0; let char = if is_not_uwu { FONT_GALMURI.get_char(c) } else { FONT_CHERRY_BOMB_ONE.get_char(c) }; canvas .copy( &char .to_texture( texture_creator, Color::RGB(colourthing, 64, 255 - colourthing), ) .unwrap(), None, char.to_rect( uwuoffset_x as i32 + 18 + rng.gen_range(-2..2), (if is_not_uwu { 24 } else { 16 }) + rng.gen_range(-3..3), ), ) .unwrap(); uwuoffset_x += char.advance_width; i += 1.0; if i > (time_seconds * (f64::from(strlen) / greeting_header_duration)).floor() { break; } } let mut offset_y = 36; let mut line_index = 0; for line in [ vec!["Venty", "Erdit", "Kaede", "dui"], vec!["Deja", "vimja", "cy", "alu"], vec!["sashu", "expired bread", "gaben"], vec!["Shana", "psykon", "Unlock"], ] { offset_y += 24; let mut offset_x = (f64::from(line_index) * 90.0) + f64::from(win_size.0) - ((time_seconds - greeting_header_duration) * (f64::from(win_size.0) * 0.078125)); line_index += 1; for c in line.join(" ").chars() { let char = FONT_GALMURI.get_char(c); canvas .copy( &char .to_texture( texture_creator, Color::RGB(colourthing, 64, 255 - colourthing), ) .unwrap(), None, char.to_rect( offset_x as i32 + 18 + rng.gen_range(-2..2), offset_y + rng.gen_range(-2..2), ), ) .unwrap(); offset_x += char.advance_width; } } } // We did most of this at mountainbytes { let mut offset = 0.0; let mut rng = rand::thread_rng(); for c in "we did 90% onsite at mountainbytes 2025,".chars() { let char = FONT_GALMURI.get_char(c); canvas .copy( &char .to_texture( texture_creator, Color::RGB(colourthing, 64, 255 - colourthing), ) .unwrap(), None, char.to_rect( offset as i32 + 18 + rng.gen_range(-2..2), win_size.1 as i32 - 64 + rng.gen_range(-2..2), ), ) .unwrap(); offset += char.advance_width; } } { let mut offset = 0.0; let mut rng = rand::thread_rng(); for c in "the rest 2 months ago".chars() { let char = FONT_GALMURI.get_char(c); canvas .copy( &char .to_texture( texture_creator, Color::RGB(colourthing, 64, 255 - colourthing), ) .unwrap(), None, char.to_rect( offset as i32 + 24 + rng.gen_range(-2..2), win_size.1 as i32 - 32 + rng.gen_range(-2..2), ), ) .unwrap(); offset += char.advance_width; } } // Sorry for shit invite { let mut offset = 0.0; let mut rng = rand::thread_rng(); for c in "sorry for shit invite we have adhd".chars() { let char = FONT_GALMURI.get_char(c); canvas .copy( &char .to_texture( texture_creator, Color::RGB(colourthing, 64, 255 - colourthing), ) .unwrap(), None, char.to_rect( offset as i32 + 18 + rng.gen_range(-2..2), win_size.1 as i32 - 96 + rng.gen_range(-2..2), ), ) .unwrap(); offset += char.advance_width; } } } } else { if time_seconds >= START_SIN { let time_seconds = time_seconds - START_SIN; let base_sin_offset = time_seconds * 0.1; let sin_offset = base_sin_offset - 0.75; let mut sin_surface = Surface::new(win_size.0, win_size.1, PixelFormatEnum::RGBA32).unwrap(); let w = win_size.0; let h = win_size.1; let f64_w = f64::from(w); let f64_h = f64::from(h); let f = &mut sin_surface.deref_mut().without_lock_mut().unwrap(); let min_x_pos = (if base_sin_offset > 1.0 { 0.0 } else { 1.0 - base_sin_offset }) * f64_w; for x in 0..w { let f64_x = f64::from(x); let out_of_frame = f64_x < min_x_pos; let sin_x = { let mut sin_x = f64_x + (sin_offset * f64_w); if sin_x > f64_w { sin_x -= f64_w; } sin_x }; let sin_y = ((f64::sin(sin_x * (3.141 * 2.0) / f64_w) + 1.0) * (f64_h / 2.0)).floor() as usize; // let sin_idx = (sin_y * w as usize + x as usize) * 4; for y in 0..h { let idx = (y * w + x) as usize * 4; let cx = x * 512 / w; f[idx] = (122 - (cx / 8)) as u8; f[idx + 1] = (255 - (cx / 2)) as u8; f[idx + 2] = (cx / 2) as u8; f[idx + 3] = if out_of_frame { 0 } else if sin_y < y as usize { let v: u16 = if idx % 7 == 0 { 255 } else { 122 }; let v = if (sin_y + 3) < (y as usize) { v } else { v * 2 / 3 }; v as u8 } else { 0 }; } } let sin_texture = Texture::from_surface(&sin_surface, texture_creator).unwrap(); canvas .copy(&sin_texture, None, Rect::new(0, 0, win_size.0, win_size.1)) .unwrap(); } if time_seconds >= START_COMETOCOSIN { let time_seconds = time_seconds - START_COMETOCOSIN; let wrap_width = f64::from(win_size.0); let mut offset = (18.0 + (time_seconds * 32.0)) % wrap_width; // WARNING: we wrap this! if the text is wider than the window, this whole thing falls apart for c in "Come to Cosin25 :3".chars() { let char = FONT_GALMURI.get_char(c); canvas .copy( &char .to_texture( texture_creator, Color::RGB(colourthing, 64, 255 - colourthing), ) .unwrap(), None, char.to_rect(offset as i32, win_size.1 as i32 - 32), ) .unwrap(); offset += char.advance_width; if offset > wrap_width { offset -= wrap_width; } } } } }