Look up and down

Description

Our camera heading lets us look around horizontally (i.e. left and right). To look around vertically, we'll need to capture another angle.

This change introduces the camera pitch which captures the vertical angle (i.e. up and down). The functionality is similar to the camera heading, but we're using a different rotational matrix to spin the camera around the X axis. We're also using the conventions outlined here to define pitch:

Note that we're not including pitch in our velocity. Even if you're looking down or up, you'll still move parallel to the X-Z plane. This is how we'll likely implement it when we support walking on the world, so leaving it as-is for now.

Note that we are clamping the pitch between straight up (PI/2) and straight down (-PI/2). If you exceed those values you would flip the world upside down, which can be very disorienting.

Screenshot

Commands

git clone git@github.com:atsheehan/iridium
cd iridium
git checkout 1b3134f16dd5e9804ee95e1aa2b4626c9adea5d5
cargo run --release

Code Changes

Modified shaders/cube.vertGitHub

@@ -5,6 +5,7 @@
55
66 uniform vec3 camera_position;
77 uniform float camera_heading;
8+ uniform float camera_pitch;
89 uniform vec3 position;
910 uniform float aspect_ratio = 1.0;
1011
@@ -96,7 +97,15 @@
9697 sin(camera_heading), 0.0, cos(camera_heading), 0.0,
9798 0.0, 0.0, 0.0, 1.0);
9899
99- mat4 world_to_camera_transform = camera_translation * camera_heading_rotation;
100+ mat4 camera_pitch_rotation =
101+ mat4(1.0, 0.0, 0.0, 0.0,
102+ 0.0, cos(camera_pitch), sin(camera_pitch), 0.0,
103+ 0.0, -sin(camera_pitch), cos(camera_pitch), 0.0,
104+ 0.0, 0.0, 0.0, 1.0);
105+
106+ mat4 world_to_camera_transform = camera_translation *
107+ camera_heading_rotation *
108+ camera_pitch_rotation;
100109
101110 mat4 camera_to_clip_transform =
102111 mat4(1.0, 0.0, 0.0, 0.0,
@@ -5,6 +5,7 @@
5
6 uniform vec3 camera_position;
7 uniform float camera_heading;
 
8 uniform vec3 position;
9 uniform float aspect_ratio = 1.0;
10
@@ -96,7 +97,15 @@
96 sin(camera_heading), 0.0, cos(camera_heading), 0.0,
97 0.0, 0.0, 0.0, 1.0);
98
99- mat4 world_to_camera_transform = camera_translation * camera_heading_rotation;
 
 
 
 
 
 
 
 
100
101 mat4 camera_to_clip_transform =
102 mat4(1.0, 0.0, 0.0, 0.0,
@@ -5,6 +5,7 @@
5
6 uniform vec3 camera_position;
7 uniform float camera_heading;
8+ uniform float camera_pitch;
9 uniform vec3 position;
10 uniform float aspect_ratio = 1.0;
11
@@ -96,7 +97,15 @@
97 sin(camera_heading), 0.0, cos(camera_heading), 0.0,
98 0.0, 0.0, 0.0, 1.0);
99
100+ mat4 camera_pitch_rotation =
101+ mat4(1.0, 0.0, 0.0, 0.0,
102+ 0.0, cos(camera_pitch), sin(camera_pitch), 0.0,
103+ 0.0, -sin(camera_pitch), cos(camera_pitch), 0.0,
104+ 0.0, 0.0, 0.0, 1.0);
105+
106+ mat4 world_to_camera_transform = camera_translation *
107+ camera_heading_rotation *
108+ camera_pitch_rotation;
109
110 mat4 camera_to_clip_transform =
111 mat4(1.0, 0.0, 0.0, 0.0,

Modified src/render.rsGitHub

@@ -188,6 +188,8 @@
188188 .set_uniform_vec3("camera_position", camera.position());
189189 self.cube_program
190190 .set_uniform_f32("camera_heading", &camera.heading());
191+ self.cube_program
192+ .set_uniform_f32("camera_pitch", &camera.pitch());
191193 }
192194 }
193195
@@ -188,6 +188,8 @@
188 .set_uniform_vec3("camera_position", camera.position());
189 self.cube_program
190 .set_uniform_f32("camera_heading", &camera.heading());
 
 
191 }
192 }
193
@@ -188,6 +188,8 @@
188 .set_uniform_vec3("camera_position", camera.position());
189 self.cube_program
190 .set_uniform_f32("camera_heading", &camera.heading());
191+ self.cube_program
192+ .set_uniform_f32("camera_pitch", &camera.pitch());
193 }
194 }
195

Modified src/world.rsGitHub

@@ -15,6 +15,7 @@
1515 position: Vec3(0.0, 0.0, 0.0),
1616 velocity: Vec3(0.0, 0.0, 0.0),
1717 heading: 0.0,
18+ pitch: 0.0,
1819 };
1920
2021 Self {
@@ -87,8 +88,13 @@
8788 self.camera.velocity = self.camera.velocity.set_y(0.0);
8889 }
8990
90- pub(crate) fn update_camera_direction(&mut self, dx: f32, _dy: f32) {
91+ pub(crate) fn update_camera_direction(&mut self, dx: f32, dy: f32) {
92+ const MIN_PITCH: f32 = -std::f32::consts::FRAC_PI_2;
93+ const MAX_PITCH: f32 = std::f32::consts::FRAC_PI_2;
94+
9195 self.camera.heading += dx * MOUSE_SENSITIVITY;
96+ self.camera.pitch =
97+ (self.camera.pitch + dy * MOUSE_SENSITIVITY).clamp(MIN_PITCH, MAX_PITCH);
9298 }
9399
94100 pub(crate) fn camera(&self) -> &Camera {
@@ -100,6 +106,7 @@
100106 position: Vec3,
101107 velocity: Vec3,
102108 heading: f32,
109+ pitch: f32,
103110 }
104111
105112 impl Camera {
@@ -110,4 +117,8 @@
110117 pub(crate) fn heading(&self) -> f32 {
111118 self.heading
112119 }
120+
121+ pub(crate) fn pitch(&self) -> f32 {
122+ self.pitch
123+ }
113124 }
@@ -15,6 +15,7 @@
15 position: Vec3(0.0, 0.0, 0.0),
16 velocity: Vec3(0.0, 0.0, 0.0),
17 heading: 0.0,
 
18 };
19
20 Self {
@@ -87,8 +88,13 @@
87 self.camera.velocity = self.camera.velocity.set_y(0.0);
88 }
89
90- pub(crate) fn update_camera_direction(&mut self, dx: f32, _dy: f32) {
 
 
 
91 self.camera.heading += dx * MOUSE_SENSITIVITY;
 
 
92 }
93
94 pub(crate) fn camera(&self) -> &Camera {
@@ -100,6 +106,7 @@
100 position: Vec3,
101 velocity: Vec3,
102 heading: f32,
 
103 }
104
105 impl Camera {
@@ -110,4 +117,8 @@
110 pub(crate) fn heading(&self) -> f32 {
111 self.heading
112 }
 
 
 
 
113 }
@@ -15,6 +15,7 @@
15 position: Vec3(0.0, 0.0, 0.0),
16 velocity: Vec3(0.0, 0.0, 0.0),
17 heading: 0.0,
18+ pitch: 0.0,
19 };
20
21 Self {
@@ -87,8 +88,13 @@
88 self.camera.velocity = self.camera.velocity.set_y(0.0);
89 }
90
91+ pub(crate) fn update_camera_direction(&mut self, dx: f32, dy: f32) {
92+ const MIN_PITCH: f32 = -std::f32::consts::FRAC_PI_2;
93+ const MAX_PITCH: f32 = std::f32::consts::FRAC_PI_2;
94+
95 self.camera.heading += dx * MOUSE_SENSITIVITY;
96+ self.camera.pitch =
97+ (self.camera.pitch + dy * MOUSE_SENSITIVITY).clamp(MIN_PITCH, MAX_PITCH);
98 }
99
100 pub(crate) fn camera(&self) -> &Camera {
@@ -100,6 +106,7 @@
106 position: Vec3,
107 velocity: Vec3,
108 heading: f32,
109+ pitch: f32,
110 }
111
112 impl Camera {
@@ -110,4 +117,8 @@
117 pub(crate) fn heading(&self) -> f32 {
118 self.heading
119 }
120+
121+ pub(crate) fn pitch(&self) -> f32 {
122+ self.pitch
123+ }
124 }