Skip to main content

doomfire/
lib.rs

1use rand::{Rng, SeedableRng};
2
3pub const FIRE_WIDTH: usize = 320;
4pub const FIRE_HEIGHT: usize = 168;
5pub const TARGET_FPS: u64 = 60;
6pub const TIME_PER_FRAME: u64 = 1000 / TARGET_FPS; // in milliseconds
7
8/// RGBA color pallet
9const PALLET: [[u8; 4]; 37] = [
10    [0x07, 0x07, 0x07, 0xFF],
11    [0x1F, 0x07, 0x07, 0xFF],
12    [0x2F, 0x0F, 0x07, 0xFF],
13    [0x47, 0x0F, 0x07, 0xFF],
14    [0x57, 0x17, 0x07, 0xFF],
15    [0x67, 0x1F, 0x07, 0xFF],
16    [0x77, 0x1F, 0x07, 0xFF],
17    [0x8F, 0x27, 0x07, 0xFF],
18    [0x9F, 0x2F, 0x07, 0xFF],
19    [0xAF, 0x3F, 0x07, 0xFF],
20    [0xBF, 0x47, 0x07, 0xFF],
21    [0xC7, 0x47, 0x07, 0xFF],
22    [0xDF, 0x4F, 0x07, 0xFF],
23    [0xDF, 0x57, 0x07, 0xFF],
24    [0xDF, 0x57, 0x07, 0xFF],
25    [0xD7, 0x5F, 0x07, 0xFF],
26    [0xD7, 0x5F, 0x07, 0xFF],
27    [0xD7, 0x67, 0x0F, 0xFF],
28    [0xCF, 0x6F, 0x0F, 0xFF],
29    [0xCF, 0x77, 0x0F, 0xFF],
30    [0xCF, 0x7F, 0x0F, 0xFF],
31    [0xCF, 0x87, 0x17, 0xFF],
32    [0xC7, 0x87, 0x17, 0xFF],
33    [0xC7, 0x8F, 0x17, 0xFF],
34    [0xC7, 0x97, 0x1F, 0xFF],
35    [0xBF, 0x9F, 0x1F, 0xFF],
36    [0xBF, 0x9F, 0x1F, 0xFF],
37    [0xBF, 0xA7, 0x27, 0xFF],
38    [0xBF, 0xA7, 0x27, 0xFF],
39    [0xBF, 0xAF, 0x2F, 0xFF],
40    [0xB7, 0xAF, 0x2F, 0xFF],
41    [0xB7, 0xB7, 0x2F, 0xFF],
42    [0xB7, 0xB7, 0x37, 0xFF],
43    [0xCF, 0xCF, 0x6F, 0xFF],
44    [0xDF, 0xDF, 0x9F, 0xFF],
45    [0xEF, 0xEF, 0xC7, 0xFF],
46    [0xFF, 0xFF, 0xFF, 0xFF],
47];
48
49pub struct DoomFire {
50    fire_pixels: Vec<usize>,
51}
52
53impl DoomFire {
54    pub fn new() -> Self {
55        // set the whole screen to color 0
56        let mut fire_pixels = vec![0; FIRE_WIDTH * FIRE_HEIGHT];
57        // set the bottom line to color 36
58        for i in 0..FIRE_WIDTH {
59            fire_pixels[(FIRE_HEIGHT - 1) * FIRE_WIDTH + i] = 36;
60        }
61
62        DoomFire { fire_pixels }
63    }
64
65    /// Update the internal state of each pixel for the Doom Fire effect.
66    /// This is main code for the Doom Fire effect.
67    pub fn update(&mut self) {
68        // No particular reason to use ChaCha8Rng. Was just part of
69        // an example on the [rust rand
70        // cookbook](https://rust-random.github.io/book/guide-start.html#fixed-seed-rngs)
71        let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(666);
72
73        for x in 0..FIRE_WIDTH {
74            for y in 1..FIRE_HEIGHT {
75                let src = y * FIRE_WIDTH + x;
76                let pixel = self.fire_pixels[src];
77
78                if pixel == 0 {
79                    self.fire_pixels[src - FIRE_WIDTH] = 0;
80                } else {
81                    let rand_idx = (rng.gen_range(0.0, 3.0) + 0.5) as usize & 3;
82                    let dst = src - rand_idx + 1;
83                    self.fire_pixels[dst - FIRE_WIDTH] = pixel - (rand_idx & 1);
84                }
85            }
86        }
87    }
88
89    /// Draw the next frame to a generic byte slice.
90    /// frame will usually be some reference to a pixel buffer provided by some rendering library.
91    /// This function will fill the frame with RGBA pixels
92    pub fn draw(&self, frame: &mut [u8]) {
93        for (i, pixel) in frame.chunks_exact_mut(4).enumerate() {
94            pixel.copy_from_slice(&PALLET[self.fire_pixels[i]])
95        }
96    }
97}
98
99impl Default for DoomFire {
100    fn default() -> Self {
101        DoomFire::new()
102    }
103}