camera_transformations/
camera_transformations.rs

1use macroquad::prelude::*;
2
3fn short_angle_dist(a0: f32, a1: f32) -> f32 {
4    let max = 360.0;
5    let da = (a1 - a0) % max;
6    2.0 * da % max - da
7}
8
9fn angle_lerp(a0: f32, a1: f32, t: f32) -> f32 {
10    a0 + short_angle_dist(a0, a1) * t
11}
12
13fn draw_cross(x: f32, y: f32, color: Color) {
14    let size = 0.1;
15    let thickness = 0.005;
16    draw_line(x - size, y, x + size, y, thickness, color);
17    draw_line(x, y - size, x, y + size, thickness, color);
18}
19
20#[macroquad::main("Camera")]
21async fn main() {
22    let mut target = (0., 0.);
23    let mut zoom = 1.0;
24    let mut rotation = 0.0;
25    let mut smooth_rotation: f32 = 0.0;
26    let mut offset = (0., 0.);
27
28    loop {
29        if is_key_down(KeyCode::W) {
30            target.1 -= 0.1;
31        }
32        if is_key_down(KeyCode::S) {
33            target.1 += 0.1;
34        }
35        if is_key_down(KeyCode::A) {
36            target.0 += 0.1;
37        }
38        if is_key_down(KeyCode::D) {
39            target.0 -= 0.1;
40        }
41        if is_key_down(KeyCode::Left) {
42            offset.0 -= 0.1;
43        }
44        if is_key_down(KeyCode::Right) {
45            offset.0 += 0.1;
46        }
47        if is_key_down(KeyCode::Up) {
48            offset.1 += 0.1;
49        }
50        if is_key_down(KeyCode::Down) {
51            offset.1 -= 0.1;
52        }
53        #[cfg(not(target_arch = "wasm32"))]
54        if is_key_down(KeyCode::Q) | is_key_down(KeyCode::Escape) {
55            break;
56        }
57
58        match mouse_wheel() {
59            (_x, y) if y != 0.0 => {
60                // Normalize mouse wheel values is browser (chromium: 53, firefox: 3)
61                #[cfg(target_arch = "wasm32")]
62                let y = if y < 0.0 {
63                    -1.0
64                } else if y > 0.0 {
65                    1.0
66                } else {
67                    0.0
68                };
69                if is_key_down(KeyCode::LeftControl) {
70                    zoom *= 1.1f32.powf(y);
71                } else {
72                    rotation += 10.0 * y;
73                    rotation = match rotation {
74                        angle if angle >= 360.0 => angle - 360.0,
75                        angle if angle < 0.0 => angle + 360.0,
76                        angle => angle,
77                    }
78                }
79            }
80            _ => (),
81        }
82
83        smooth_rotation = angle_lerp(smooth_rotation, rotation, 0.1);
84
85        clear_background(LIGHTGRAY);
86
87        set_camera(&Camera2D {
88            target: vec2(target.0, target.1),
89            ..Default::default()
90        });
91        draw_cross(0., 0., RED);
92
93        set_camera(&Camera2D {
94            target: vec2(target.0, target.1),
95            rotation: smooth_rotation,
96            ..Default::default()
97        });
98        draw_cross(0., 0., GREEN);
99
100        set_camera(&Camera2D {
101            target: vec2(target.0, target.1),
102            rotation: smooth_rotation,
103            zoom: vec2(zoom, zoom * screen_width() / screen_height()),
104            ..Default::default()
105        });
106        draw_cross(0., 0., BLUE);
107
108        set_camera(&Camera2D {
109            target: vec2(target.0, target.1),
110            rotation: smooth_rotation,
111            zoom: vec2(zoom, zoom * screen_width() / screen_height()),
112            offset: vec2(offset.0, offset.1),
113            ..Default::default()
114        });
115
116        // Render some primitives in camera space
117        draw_line(-0.4, 0.4, -0.8, 0.9, 0.05, BLUE);
118        draw_rectangle(-0.3, 0.3, 0.2, 0.2, GREEN);
119        draw_circle(0., 0., 0.1, YELLOW);
120
121        // Back to screen space, render some text
122        set_default_camera();
123        draw_text(
124            format!("target (WASD keys) = ({:+.2}, {:+.2})", target.0, target.1).as_str(),
125            10.0,
126            10.0,
127            15.0,
128            BLACK,
129        );
130        draw_text(
131            format!("rotation (mouse wheel) = {rotation} degrees").as_str(),
132            10.0,
133            25.0,
134            15.0,
135            BLACK,
136        );
137        draw_text(
138            format!("zoom (ctrl + mouse wheel) = {zoom:.2}").as_str(),
139            10.0,
140            40.0,
141            15.0,
142            BLACK,
143        );
144        draw_text(
145            format!("offset (arrow keys) = ({:+.2}, {:+.2})", offset.0, offset.1).as_str(),
146            10.0,
147            55.0,
148            15.0,
149            BLACK,
150        );
151        draw_text("HELLO", 30.0, 200.0, 30.0, BLACK);
152
153        next_frame().await
154    }
155}