/ bin / app / src / gfx / anim.rs
anim.rs
  1  /* This file is part of DarkFi (https://dark.fi)
  2   *
  3   * Copyright (C) 2020-2025 Dyne.org foundation
  4   *
  5   * This program is free software: you can redistribute it and/or modify
  6   * it under the terms of the GNU Affero General Public License as
  7   * published by the Free Software Foundation, either version 3 of the
  8   * License, or (at your option) any later version.
  9   *
 10   * This program is distributed in the hope that it will be useful,
 11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13   * GNU Affero General Public License for more details.
 14   *
 15   * You should have received a copy of the GNU Affero General Public License
 16   * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17   */
 18  
 19  use std::collections::HashMap;
 20  
 21  use super::{BufferId, DrawCall, GfxDrawCall, TextureId};
 22  
 23  #[derive(Debug, Clone)]
 24  pub struct Frame {
 25      /// Duration of this frame in ms
 26      duration: u32,
 27      dc: DrawCall,
 28  }
 29  
 30  impl Frame {
 31      pub fn new(duration: u32, dc: DrawCall) -> Self {
 32          Self { duration, dc }
 33      }
 34  }
 35  
 36  #[derive(Debug, Clone)]
 37  pub(super) struct GfxSeqAnim {
 38      oneshot: bool,
 39      frames: Vec<Option<GfxFrame>>,
 40      /// Timer between frames
 41      timer: std::time::Instant,
 42      current_idx: usize,
 43      pub(super) is_visible: bool,
 44  }
 45  
 46  impl GfxSeqAnim {
 47      pub fn new(frames_len: usize, oneshot: bool) -> Self {
 48          let frames = vec![None; frames_len];
 49          Self {
 50              oneshot,
 51              frames,
 52              timer: std::time::Instant::now(),
 53              current_idx: 0,
 54              is_visible: false,
 55          }
 56      }
 57  
 58      pub fn set(
 59          &mut self,
 60          frame_idx: usize,
 61          frame: Frame,
 62          textures: &HashMap<TextureId, miniquad::TextureId>,
 63          buffers: &HashMap<BufferId, miniquad::BufferId>,
 64      ) {
 65          assert!(frame_idx < self.frames.len());
 66          let duration = std::time::Duration::from_millis(frame.duration as u64);
 67          let dc = frame.dc.compile(textures, buffers, 0).unwrap();
 68          self.frames[frame_idx] = Some(GfxFrame { duration, dc });
 69          //t!("got frame {frame_idx}");
 70      }
 71  
 72      fn curr_frame(&self) -> Option<&GfxFrame> {
 73          assert!(self.current_idx < self.frames.len());
 74          self.frames[self.current_idx].as_ref()
 75      }
 76  
 77      pub fn tick(&mut self) -> Option<GfxDrawCall> {
 78          //t!("tick");
 79          if self.curr_frame().is_none() {
 80              assert_eq!(self.current_idx, 0);
 81              return None
 82          };
 83  
 84          self.increment();
 85  
 86          let curr_frame = self.curr_frame().unwrap().clone();
 87          Some(curr_frame.dc)
 88      }
 89  
 90      fn increment(&mut self) {
 91          // One shot anims dont loop
 92          if self.oneshot && self.current_idx + 1 == self.frames.len() {
 93              return
 94          }
 95  
 96          let elapsed = self.timer.elapsed();
 97          let frame = self.curr_frame().unwrap();
 98          let curr_duration = frame.duration;
 99  
100          if elapsed >= curr_duration {
101              let next_idx = (self.current_idx + 1) % self.frames.len();
102              // Only advance when the next frame is Some
103              // Otherwise stay on the same frame
104              if self.frames[next_idx].is_some() {
105                  self.current_idx = next_idx;
106                  // Reset the timer now we changed frame
107                  self.timer = std::time::Instant::now();
108              }
109          }
110      }
111  }
112  
113  #[derive(Debug, Clone)]
114  struct GfxFrame {
115      duration: std::time::Duration,
116      dc: GfxDrawCall,
117  }