summaryrefslogtreecommitdiff
path: root/src/camera.rs
blob: edb7f29a6a728d32eb6709df69b7669dd1629c22 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use crate::{
    Input,
    config::{CAMERA_MOVEMENT_SPEED, CHUNK_SIZE, PIXEL_BUFFER_HEIGHT, PIXEL_BUFFER_WIDTH},
    sim::{chunk::Chunk, world::World},
};

pub struct Camera {
    // centre coords
    pub x: f64,
    pub y: f64,
    // zoom scale factor, 1.0 = board <-> frame
    pub zoom: f64,
}

impl Camera {
    pub fn handle_camera_input(&mut self, input: &Input, delta_time: f32) {
        // wasd movement
        let x: f32 = if input.is_left_pressed {
            -1.0
        } else if input.is_right_pressed {
            1.0
        } else {
            0.0
        };
        let y: f32 = if input.is_down_pressed {
            1.0
        } else if input.is_up_pressed {
            -1.0
        } else {
            0.0
        };

        if x == 0.0 && y == 0.0 {
            return;
        }

        let magnitude = (x.powi(2) + y.powi(2)).sqrt();
        let adjusted_x = x / magnitude * self.zoom as f32 * CAMERA_MOVEMENT_SPEED * delta_time;
        let adjusted_y = y / magnitude * self.zoom as f32 * CAMERA_MOVEMENT_SPEED * delta_time;

        self.x += adjusted_x as f64;
        self.y += adjusted_y as f64;
    }

    pub fn screen_position_to_world(&self, screen_x: f64, screen_y: f64) -> (f64, f64) {
        let camera_width = self.zoom * f64::from(PIXEL_BUFFER_WIDTH);
        let camera_height = self.zoom * f64::from(PIXEL_BUFFER_HEIGHT);
        let camera_start_x = self.x - camera_width / 2.0;
        let camera_start_y = self.y - camera_height / 2.0;
        (
            (screen_x / PIXEL_BUFFER_WIDTH as f64 * camera_width) + camera_start_x,
            (screen_y / PIXEL_BUFFER_HEIGHT as f64 * camera_height) + camera_start_y,
        )
    }

    pub fn write_frame_view(&self, frame: &mut [u8], world: &World) {
        puffin::profile_function!();

        for i in 0..frame.len() / 4 {
            let idx = i * 4;
            frame[idx] = 0xFF;
            frame[idx + 1] = 0xAA;
            frame[idx + 2] = 0xAA;
            frame[idx + 3] = 0xFF;
        }
        return;

        const BG: [u8; 4] = [0x00, 0x00, 0x00, 0xFF];
        for px in frame.chunks_exact_mut(4) {
            px.copy_from_slice(&BG);
        }

        // world-space rect covered by the screen
        let (xl, yl) = self.screen_position_to_world(0.0, 0.0);
        let (xu, yu) =
            self.screen_position_to_world(PIXEL_BUFFER_WIDTH as f64, PIXEL_BUFFER_HEIGHT as f64);

        let c = CHUNK_SIZE as i32;
        let cx0 = (xl.floor() as i32).div_euclid(c);
        let cx1 = (xu.ceil() as i32).div_euclid(c);
        let cy0 = (yl.floor() as i32).div_euclid(c);
        let cy1 = (yu.ceil() as i32).div_euclid(c);

        let scale = 1.0 / self.zoom; // pixels per cell

        for cy in cy0..=cy1 {
            for cx in cx0..=cx1 {
                let Some(&idx) = world.chunk_position_to_chunk_idx.get(&(cx, cy)) else {
                    continue;
                };
                self.write_chunk(frame, &world.chunks[idx], cx, cy, xl, yl, scale);
            }
        }
    }

    fn write_chunk(
        &self,
        frame: &mut [u8],
        chunk: &Chunk,
        cx: i32,
        cy: i32,
        xl: f64,
        yl: f64,
        scale: f64,
    ) {
        let c = CHUNK_SIZE as i32;
        let w = PIXEL_BUFFER_WIDTH as i32;
        let h = PIXEL_BUFFER_HEIGHT as i32;

        for ly in 0..c {
            let wy = (cy * c + ly) as f64;
            let sy0 = (((wy - yl) * scale).floor() as i32).max(0);
            let sy1 = (((wy + 1.0 - yl) * scale).floor() as i32).min(h);
            if sy0 >= sy1 {
                continue;
            }

            for lx in 0..c {
                let wx = (cx * c + lx) as f64;
                let sx0 = (((wx - xl) * scale).floor() as i32).max(0);
                let sx1 = (((wx + 1.0 - xl) * scale).floor() as i32).min(w);
                if sx0 >= sx1 {
                    continue;
                }

                let color = chunk
                    .get_cell_at_local_position(lx as u8, ly as u8)
                    .material
                    .def()
                    .color;
                let rgba = [color.0, color.1, color.2, color.3];

                for sy in sy0..sy1 {
                    let start = (sy * w + sx0) as usize * 4;
                    let end = (sy * w + sx1) as usize * 4;
                    for px in frame[start..end].chunks_exact_mut(4) {
                        px.copy_from_slice(&rgba);
                    }
                }
            }
        }
    }
}