aboutsummaryrefslogblamecommitdiffstats
path: root/src/render.rs
blob: 08b17beb7a29d2987694ca2ad272691526a4a97f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11

                                                                  
                                              
              






                                                    

















                                                                                  




















































                                                                   

                                    

                                        
                            


                                                   
                            
                                                 

                                  
 




                                                  





                                                            
          
                     
    
 
                                  
                                                 

                                       
 


                                                
                              

                                                                                              

                         
 
             
                                


















                                                                 
         









                                                                                                    
              
                                                            
        
                                








                                                       

     
 




                                                    



                                                                                   



                                                           
      










                                                   

                                   
                                                                    
                                     
                  
                                     
           
















                                                                                                  















                                                                                                                                  
                                                                                                                                                                                            


         
                                
                                
     


                                                        

                                                                  
       
                        
                        
                                  








                                                                                
                                                  
                   
                                    
                                     
                  
                                             


                  
                                                                 







                                                                          






                                                  
                                            

                         


                                                                                                  

                          











                                     

         
                                             



































                                                           
     





                                                                                                   
 

                         

                               














                                                                       
           
               
          












                                                                                             
                                                              









                                                   
       
 
                                                                                      
 







                                                                          


                                                 


                 



                                                               










                                                                

   
use crate::font::BakedFont;
use crate::generated::fonts::{FONT_CHERRY_BOMB_ONE, FONT_GALMURI};
use rand::distributions::uniform::SampleRange;
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)
}

fn render_chars_with_rng<R>(
  canvas: &mut Canvas<Window>,
  texture_creator: &TextureCreator<WindowContext>,
  str: impl Iterator<Item = char>,
  font: impl BakedFont,
  range_x: &R,
  range_y: &R,
  offset_x: &mut f64,
  offset_y: &i32,
  colour: Color,
  rng: &mut impl Rng,
) where
  R: SampleRange<i32> + Clone,
{
  let mut rng = rng;
  for c in str {
    let char = font.get_char(c);
    canvas
      .copy(
        &char.to_texture(texture_creator, colour).unwrap(),
        None,
        char.to_rect(
          offset_x.clone() as i32 + rng.gen_range(range_x.clone()),
          offset_y.clone() + rng.gen_range(range_y.clone()),
        ),
      )
      .unwrap();
    *offset_x += char.advance_width;
  }
}

fn render_chars_without_rng(
  canvas: &mut Canvas<Window>,
  texture_creator: &TextureCreator<WindowContext>,
  str: impl Iterator<Item = char>,
  font: impl BakedFont,
  offset_x: &mut f64,
  offset_y: &i32,
  colour: Color,
) {
  for c in str {
    let char = font.get_char(c);
    canvas
      .copy(
        &char.to_texture(texture_creator, colour).unwrap(),
        None,
        char.to_rect(offset_x.clone() as i32, offset_y.clone()),
      )
      .unwrap();
    *offset_x += char.advance_width;
  }
}

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<Window>,
  texture_creator: &TextureCreator<WindowContext>,
  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;
      render_chars_without_rng(
        canvas,
        texture_creator,
        "UwU-Space".chars(),
        FONT_CHERRY_BOMB_ONE,
        &mut f64::from(offset_x),
        &offset_y,
        Color::RGB(colourthing, 64, 255 - colourthing),
      );
    }
  }

  #[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 = 18.0;
        let str = {
          if time_seconds + 0.75 >= (SCENE_GREETINGS - SCENE_MOAR) {
            "oh look a 64k rust demo"
          } else {
            "Have some scrollers: x3"
          }
        };
        let str = str.chars().collect::<Vec<char>>();
        let str = &str[0..((time_seconds * (f64::from(strlen) / greeting_header_duration)).floor()
          as usize)
          .min(str.len())];
        render_chars_with_rng(
          canvas,
          texture_creator,
          str.iter().map(|v| *v),
          FONT_GALMURI,
          &(-2..2),
          &(-3..3),
          &mut uwuoffset_x,
          &24,
          Color::RGB(colourthing, 64, 255 - colourthing),
          &mut rng,
        );
        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));

          render_chars_with_rng(canvas, texture_creator, line.chars(), FONT_GALMURI, &(-2..2), &(-2..2), &mut offset_x,&offset_y, Color::RGB(colourthing, 64, 255 - colourthing), &mut rng);
        }
      }
    }
  } else if is_greetings_scene {
    #[cfg(not(feature = "32k"))]
    {
      let time_seconds = time_seconds - SCENE_GREETINGS;
      // Greetings
      let greeting_header_duration = 4.0;
      let colour = Color::RGB(colourthing, 64, 255 - colourthing);
      let mut rng = rand::thread_rng();
      {
        let mut i = 0.0;
        let strlen = 43;
        let mut uwuoffset_x = 0.0;
        let str = "Greetings by UwU-Space to:";
        let str = str.chars().collect::<Vec<char>>();
        let str = &str[0..{
          let f = time_seconds * (f64::from(strlen) / greeting_header_duration);
          let f = f.floor() as usize;
          f.min(str.len())
        }];
        for c in str {
          // we dont use the abstraction funcs due to the inline uwu-space
          let is_not_uwu = i <= 12.0 || i >= 22.0;
          i += 1.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, colour).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;
        }
        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 = 18.0
            + ((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;

          render_chars_with_rng(
            canvas,
            texture_creator,
            line.join("   ").chars(),
            FONT_GALMURI,
            &(-2..2),
            &(-2..2),
            &mut offset_x,
            &offset_y,
            colour,
            &mut rng,
          );
        }
      }
      // We did most of this at mountainbytes
      render_chars_with_rng(
        canvas,
        texture_creator,
        "sorry for shit invite we have adhd".chars(),
        FONT_GALMURI,
        &(-2..2),
        &(-2..2),
        &mut 18.0,
        &(win_size.1 as i32 - 96),
        colour,
        &mut rng,
      );
      render_chars_with_rng(
        canvas,
        texture_creator,
        "we did 90% onsite at mountainbytes 2025,".chars(),
        FONT_GALMURI,
        &(-2..2),
        &(-2..2),
        &mut 18.0,
        &(win_size.1 as i32 - 64),
        colour,
        &mut rng,
      );
      render_chars_with_rng(
        canvas,
        texture_creator,
        "the rest 2 months ago".chars(),
        FONT_GALMURI,
        &(-2..2),
        &(-2..2),
        &mut 18.0,
        &(win_size.1 as i32 - 32),
        colour,
        &mut rng,
      );
    }
  } 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;
      // World's shittiest marquee implementation
      for char in "Come to Cosin25 :3".chars() {
        let char = FONT_GALMURI.get_char(char);
        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;
        }
      }
    }
  }
}