First person camera using transformations
DontKnow3D

i have tried to implement a first person camera, but as you might guess: It don't work. everything works fine in the whole 3d department of my code, except for the camera. if you look forwards everything works, but if you look 90 degrees to the side instead of the camera looking up and down when you move the mouse it spins in a circle and if you look 180 degrees behind yourself the camera is inverted. how do i fix this? i have tried many different formulas but none of them work.

mouse processing:

1switch (ev.type) { 2 case ALLEGRO_EVENT_MOUSE_AXES: 3 p.m.x += ev.mouse.dx; 4 p.m.y += ev.mouse.dy; 5 p.m.ax = p.m.x / MOUSE_SPEED;//angle x: 1 rotation = 2*PI 6 p.m.ay = p.m.y / -MOUSE_SPEED;//angle y: goes from -PI/2 to PI/2 7 al_set_mouse_xy(display, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2); 8 if (p.m.ay > PI / 2) { 9 p.m.ay = PI / 2; 10 p.m.y = ((PI / 2)*-MOUSE_SPEED); 11 } 12 if (p.m.ay < -PI / 2) { 13 p.m.ay = -PI / 2; 14 p.m.y = ((-PI / 2)*-MOUSE_SPEED); 15 } 16//------------------------------------------------ 17//----------calculate camera rotation------------- 18//------------------------------------------------ 19//-------------how do i do this?------------------ 20//------------------------------------------------ 21 p.m.cax = p.m.ay; 22 p.m.cay = p.m.ax; 23 p.m.caz = 0; 24 25 /*p.m.cax = cos(p.m.ax) * cos(p.m.ay)*PI; 26 p.m.cay = sin(p.m.ay)*PI; 27 p.m.caz = sin(p.m.ax) * cos(p.m.ay)*PI;*/ 28 break;

camera transformation code:

1 al_identity_transform(&tra); 2 al_translate_transform_3d(&tra, p.x, p.y, p.z); 3 al_rotate_transform_3d(&tra, 1, 0, 0, p.m.cax); 4 al_rotate_transform_3d(&tra, 0, 1, 0, p.m.cay); 5 al_rotate_transform_3d(&tra, 0, 0, 1, p.m.caz); 6 al_perspective_transform(&tra, -SCREEN_WIDTH / 2, -SCREEN_HEIGHT / 2, 1000, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 100000); 7 al_use_projection_transform(&tra); 8 9 al_flip_display();

whole code:

1#include<allegro5\allegro.h> 2#include<allegro5\allegro_font.h> 3#include<allegro5\allegro_ttf.h> 4#include<allegro5\allegro_color.h> 5#include<allegro5\allegro_primitives.h> 6#include<allegro5\allegro_image.h> 7#include<cmath> 8 9 10#define LOOP(var,max) for(var=0;var<max;++var) 11#define KD(keycode) al_key_down(&key,ALLEGRO_KEY_##keycode) 12 13#define SCREEN_WIDTH (100 * 16) 14#define SCREEN_HEIGHT (100 * 9) 15#define PI 3.141592653589793238462643 16 17#define MOVE_SPEED 30 18#define MOUSE_SPEED 500. //higher = lower speed 19 20typedef struct { 21 int x, y;//xy location of mouse 22 float ax, ay;//xy angle of mouse 23 float cax, cay, caz;//camera angle 24}mouse_t; 25 26class player_t { 27public: 28 mouse_t m = { 0 }; 29 float x = 0, y = 0, z = 0; 30}; 31 32void process_keyboard(player_t& p) { 33 ALLEGRO_KEYBOARD_STATE key; 34 al_get_keyboard_state(&key); 35 36 //move the player 37 if (KD(A)) { 38 p.x += MOVE_SPEED * cos(p.m.ax); 39 p.z += MOVE_SPEED * sin(p.m.ax); 40 } 41 if (KD(D)) { 42 p.x -= MOVE_SPEED * cos(p.m.ax); 43 p.z -= MOVE_SPEED * sin(p.m.ax); 44 } 45 if (KD(W)) { 46 p.z += MOVE_SPEED * cos(-p.m.ax); 47 p.x += MOVE_SPEED * sin(-p.m.ax); 48 } 49 if (KD(S)) { 50 p.z -= MOVE_SPEED * cos(-p.m.ax); 51 p.x -= MOVE_SPEED * sin(-p.m.ax); 52 } 53 if (KD(SPACE)) 54 p.y += MOVE_SPEED; 55 if (KD(CAPSLOCK) || KD(LSHIFT)) 56 p.y -= MOVE_SPEED; 57 if (KD(ESCAPE)) 58 exit(0); 59} 60 61void init() { 62 al_init(); 63 al_init_image_addon(); 64 al_init_primitives_addon(); 65 al_init_ttf_addon(); 66 al_init_font_addon(); 67 al_install_keyboard(); 68 al_install_mouse(); 69 al_set_new_display_option(ALLEGRO_DEPTH_SIZE, 16, ALLEGRO_SUGGEST); 70} 71 72int main() { 73 init(); 74 int i, j; 75 auto display = al_create_display(SCREEN_WIDTH, SCREEN_HEIGHT); 76 auto q = al_create_event_queue(); 77 auto fps = al_create_timer(1 / 60.); 78 ALLEGRO_EVENT ev; 79 ALLEGRO_TRANSFORM tra; 80 player_t p; 81 auto font = al_load_ttf_font("fonts/DeansgateCondensed-Bold.ttf", 64, 0);//just a generic font for testing 82 al_register_event_source(q, al_get_timer_event_source(fps)); 83 al_register_event_source(q, al_get_mouse_event_source()); 84 al_register_event_source(q, al_get_display_event_source(display)); 85 al_start_timer(fps); 86 al_set_render_state(ALLEGRO_DEPTH_TEST, 1); 87 auto image = al_load_bitmap("images/ground.jpeg");//random picture of some ground 88 /*p.m.x = SCREEN_WIDTH / 2; 89 p.m.y = SCREEN_HEIGHT / 2;*/ 90 al_set_mouse_xy(display, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2); 91 ALLEGRO_VERTEX cube[] = { 92 {-1000,-1000,-1000, 0,0,al_map_rgb_f(1,1,1)}, 93 {-1000,-1000, 1000, 0,0,al_map_rgb_f(1,1,1)}, 94 {-1000, 1000, 1000, 0,0,al_map_rgb_f(1,1,1)}, 95 96 {1000, 1000,-1000, 0,0,al_map_rgb_f(0,1,1)}, 97 {-1000,-1000,-1000, 0,0,al_map_rgb_f(0,1,1)}, 98 {-1000, 1000,-1000, 0,0,al_map_rgb_f(0,1,1)}, 99 100 {1000,-1000, 1000, 0,0,al_map_rgb_f(1,0,1)}, 101 {-1000,-1000,-1000, 0,0,al_map_rgb_f(1,0,1)}, 102 {1000,-1000,-1000, 0,0,al_map_rgb_f(1,0,1)}, 103 104 {1000, 1000,-1000, 0,0,al_map_rgb_f(1,1,0)}, 105 {1000,-1000,-1000, 0,0,al_map_rgb_f(1,1,0)}, 106 {-1000,-1000,-1000, 0,0,al_map_rgb_f(1,1,0)}, 107 108 {-1000,-1000,-1000, 0,0,al_map_rgb_f(0,0,1)}, 109 {-1000, 1000, 1000, 0,0,al_map_rgb_f(0,0,1)}, 110 {-1000, 1000,-1000, 0,0,al_map_rgb_f(0,0,1)}, 111 112 {1000,-1000, 1000, 0,0,al_map_rgb_f(0,1,0)}, 113 {-1000,-1000, 1000, 0,0,al_map_rgb_f(0,1,0)}, 114 {-1000,-1000,-1000, 0,0,al_map_rgb_f(0,1,0)}, 115 116 {-1000, 1000, 1000, 0,0,al_map_rgb_f(1,0,0)}, 117 {-1000,-1000, 1000, 0,0,al_map_rgb_f(1,0,0)}, 118 {1000,-1000, 1000, 0,0,al_map_rgb_f(1,0,0)}, 119 120 {1000, 1000, 1000, 0,0,al_map_rgb_f(0,0,0)}, 121 {1000,-1000,-1000, 0,0,al_map_rgb_f(0,0,0)}, 122 {1000, 1000,-1000, 0,0,al_map_rgb_f(0,0,0)}, 123 124 {1000,-1000,-1000, 0,0,al_map_rgb_f(1,1,1)}, 125 {1000, 1000, 1000, 0,0,al_map_rgb_f(1,1,1)}, 126 {1000,-1000, 1000, 0,0,al_map_rgb_f(1,1,1)}, 127 128 {1000, 1000, 1000, 0,0,al_map_rgb_f(0,1,1)}, 129 {1000, 1000,-1000, 0,0,al_map_rgb_f(0,1,1)}, 130 {-1000, 1000,-1000, 0,0,al_map_rgb_f(0,1,1)}, 131 132 {1000, 1000, 1000, 0,0,al_map_rgb_f(1,0,1)}, 133 {-1000, 1000,-1000, 0,0,al_map_rgb_f(1,0,1)}, 134 {-1000, 1000, 1000, 0,0,al_map_rgb_f(1,0,1)}, 135 136 {1000, 1000, 1000, 0,0,al_map_rgb_f(1,1,0)}, 137 {-1000, 1000, 1000, 0,0,al_map_rgb_f(1,1,0)}, 138 {1000,-1000, 1000, 0,0,al_map_rgb_f(1,1,0)}, 139 }; 140 ALLEGRO_VERTEX ground[] = { 141 { 1000,0,1000,al_get_bitmap_width(image),al_get_bitmap_height(image),al_map_rgb_f(1,1,1) }, 142 { -1000,0,1000,0,al_get_bitmap_height(image),al_map_rgb_f(1,1,1) }, 143 { -1000,0,-1000,0,0,al_map_rgb_f(1,1,1) }, 144 145 { 1000,0,1000,al_get_bitmap_width(image),al_get_bitmap_height(image),al_map_rgb_f(1,1,1) }, 146 { 1000,0,-1000,al_get_bitmap_width(image),0,al_map_rgb_f(1,1,1) }, 147 { -1000,0,-1000,0,0,al_map_rgb_f(1,1,1) }, 148 }; 149 while (1) { 150 al_wait_for_event(q, &ev); 151 switch (ev.type) { 152 case ALLEGRO_EVENT_MOUSE_AXES: 153 p.m.x += ev.mouse.dx; 154 p.m.y += ev.mouse.dy; 155 p.m.ax = p.m.x / MOUSE_SPEED;//angle x: 1 rotation = 2*PI 156 p.m.ay = p.m.y / -MOUSE_SPEED;//angle y: goes from -PI/2 to PI/2 157 al_set_mouse_xy(display, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2); 158 if (p.m.ay > PI / 2) { 159 p.m.ay = PI / 2; 160 p.m.y = ((PI / 2)*-MOUSE_SPEED); 161 } 162 if (p.m.ay < -PI / 2) { 163 p.m.ay = -PI / 2; 164 p.m.y = ((-PI / 2)*-MOUSE_SPEED); 165 } 166//------------------------------------------------ 167//----------calculate camera rotation------------- 168//------------------------------------------------ 169//-------------how do i do this?------------------ 170//------------------------------------------------ 171 p.m.cax = p.m.ay; 172 p.m.cay = p.m.ax; 173 p.m.caz = 0; //ev.mouse.z / 100.; 174 175 /*p.m.cax = cos(p.m.ax) * cos(p.m.ay)*PI; 176 p.m.cay = sin(p.m.ay)*PI; 177 p.m.caz = sin(p.m.ax) * cos(p.m.ay)*PI;*/ 178 179 break; 180 case ALLEGRO_EVENT_TIMER: 181 //key input 182 process_keyboard(p); 183 184 //clear display 185 al_clear_to_color(al_map_rgb_f(0.5, 0.5, 0.5)); 186 al_clear_depth_buffer(10000); 187 188 //draw the normalized angles (floating text around you) (2*PI = 1) 189 al_identity_transform(&tra); 190 al_translate_transform_3d(&tra, -p.x, -p.y, -p.z - 2000); 191 al_use_transform(&tra); 192 LOOP(j, 12) { 193 al_rotate_transform_3d(&tra, 0, 1, 0, PI / 6); 194 LOOP(i, 12) { 195 al_rotate_transform_3d(&tra, 1, 0, 0, PI / 6); 196 al_use_transform(&tra); 197 al_draw_multiline_textf(font, al_map_rgb_f(0, 1, 0.5), 50, 50, 0, 0, 0, "ax_%.4f ay_%.4f cax_%.4f cay_%.4f caz_%.4f", p.m.ax / PI / 2, p.m.ay / PI / 2, p.m.cax / PI / 2, p.m.cay / PI / 2, p.m.caz / PI / 2); 198 } 199 } 200 //draw the ground 201 al_identity_transform(&tra); 202 al_scale_transform_3d(&tra, 10, 0, 10); 203 al_translate_transform_3d(&tra, 0, 6000, 0); 204 al_use_transform(&tra); 205 al_draw_prim(ground, NULL, image, 0, 6, ALLEGRO_PRIM_TRIANGLE_LIST); 206 207 //draw the 5 floating cubes 208 al_identity_transform(&tra); 209 al_translate_transform_3d(&tra, 0, 0, -4000); 210 al_use_transform(&tra); 211 al_draw_prim(cube, NULL, NULL, 0, sizeof(cube) / sizeof(cube[0]), ALLEGRO_PRIM_TRIANGLE_LIST); 212 al_translate_transform_3d(&tra, 0, 5000, 0); 213 al_use_transform(&tra); 214 al_draw_prim(cube, NULL, NULL, 0, sizeof(cube) / sizeof(cube[0]), ALLEGRO_PRIM_TRIANGLE_LIST); 215 al_translate_transform_3d(&tra, 5000, -5000, 5000); 216 al_use_transform(&tra); 217 al_draw_prim(cube, NULL, NULL, 0, sizeof(cube) / sizeof(cube[0]), ALLEGRO_PRIM_TRIANGLE_LIST); 218 al_translate_transform_3d(&tra, -5000, -5000, 5000); 219 al_use_transform(&tra); 220 al_draw_prim(cube, NULL, NULL, 0, sizeof(cube) / sizeof(cube[0]), ALLEGRO_PRIM_TRIANGLE_LIST); 221 al_translate_transform_3d(&tra, -5000, 5000, 5000); 222 al_use_transform(&tra); 223 al_draw_prim(cube, NULL, NULL, 0, sizeof(cube) / sizeof(cube[0]), ALLEGRO_PRIM_TRIANGLE_LIST); 224 225 //do the camera transforms 226 al_identity_transform(&tra); 227 al_translate_transform_3d(&tra, p.x, p.y, p.z); 228 al_rotate_transform_3d(&tra, 1, 0, 0, p.m.cax); 229 al_rotate_transform_3d(&tra, 0, 1, 0, p.m.cay); 230 al_rotate_transform_3d(&tra, 0, 0, 1, p.m.caz); 231 al_perspective_transform(&tra, -SCREEN_WIDTH / 2, -SCREEN_HEIGHT / 2, 1000, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 100000); 232 al_use_projection_transform(&tra); 233 234 al_flip_display(); 235 break; 236 } 237 } 238}

(and i also cant manage to figure out how to draw a 'hud' or just simple things right on the display after the transformations)

Edgar Reynaldo

You have to understand there are two matrices involved. The projection matrix, and the view matrix. You're mixing the two, and you still aren't even trying to do what I showed you in the other thread.

Also, you are mixing 2D and 3D drawing without resetting your projection matrices properly.

You should read over the manual entry for transformations.

https://liballeg.org/a5docs/trunk/transformations.html

Specifically, look at the docs for al_build_camera_transform , al_orthographic_transform, and al_perspective_transform.

For 2D you use the orthographic transform for the projection matrix. For 3D you use the perspective transform for the projection matrix. For 2D view matrix, all you need is the identity. For 3D, you need to apply a camera transform to the view matrix.

First, see https://liballeg.org/a5docs/trunk/transformations.html#al_use_projection_transform for the difference between 2D and 3D projection matrices.

Second, see https://liballeg.org/a5docs/trunk/transformations.html#al_build_camera_transform for how to set up your camera. As I told you before, you need to use a forward, right, and up vector to define the orientation of the camera. You also need a position for the camera. Then the lookAt parameter becomes camera.position + forward_vector.

EDIT
You can also 'cheat' and calculate the look vector yourself using a little trig.

The compass heading becomes the rotation around the y-axis on the xz plane.

```      +Y   -Z
|   /
|  /
| /
-X_____|/____ +X
/|
/ |
/  |
+Z  -Y
```

And then the pitch become the rotation around the right vector.

```double heading = atan2(-dz,dx);
double pitch = acos((dist(xz)/dist(xyz));
```

I think that's right but I'm tired.

DontKnow3D

OK so; I have majorly rewritten how everything about my perspective/transformation/drawing works. I now first generate 1 mesh, where everything 3d is stored and then draw all of that at once to the screen. 2d operations work now too and i think i have gotten a much better grasp on how the perspective transformations work. i now also use the camera transformation. the only thing that doesent work now is: the camera. I think I'm using the camera correctly, but apparently I'm not. and if you see anything I'm doing wrong now with the transformations, pleas tell me.

Edgar Reynaldo

I think your formula is just a bit off. You have z and y reversed. It should be :

```   fw.x = cos(pitch)*cos(yaw);
fw.z = cos(pitch)*-sin(yaw);
fw.y = sin(pitch);
```

{"name":"611674","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/9\/4\/94b598fff0562e56a5c7bf072c62b984.png","w":800,"h":600,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/9\/4\/94b598fff0562e56a5c7bf072c62b984"}

Or you can build your forward vector using transforms :

1 void RebuildTransform() { 2 fw = Vec(0,0,-1); 3 rt = Vec(1,0,0); 4 up = Vec(0,1,0); 5 /// Apply yaw 6 ALLEGRO_TRANSFORM t1; 7 al_identity_transform(&t1); 8 al_rotate_transform(&t1 , up.x , up.y , up.z , yaw); 9 al_transform_coordinates(&t1 , &rt.x , &rt.y , &rt.z); 10 al_transform_coordinates(&t1 , &fw.x , &fw.y , &fw.z); 11 12 /// Apply pitch 13 al_identity_transform(&t1); 14 al_rotate_transform(&t1 , rt.x , rt.y , rt.z , yaw); 15 al_transform_coordinates(&t1 , &up.x , &up.y , &up.z); 16 al_transform_coordinates(&t1 , &fw.x , &fw.y , &fw.z); 17 18 /// Apply roll 19 //... 20 Vec look = pos + fw; 21 al_build_camera_transform(&t , pos.x , pos.y , pos.z , look.x , look.y , look.z , up.x , up.y , up.z); 22 }

DontKnow3D

ok thank you! you've been a great help in all of this. there is just 1 more thing id like to figure out. in my code right after i use the camera transformation, i draw the whole map mesh. now i would like to also draw other things and use transformations on them. but if i trie to do that, it doesent really work. the things i draw after the new transformations move with the camera rotation. how would i fix that?

1// ----------------camera transform--------------- 2 al_build_camera_transform(&cam, p.x, p.y, p.z, 3 p.x + p.m.cax, p.y + p.m.cay, p.z + p.m.caz, 4 0, 1, 0); 5 al_use_transform(&cam); 6 7 //draw 3d things 8 al_draw_prim(mesh.mesh, NULL, NULL, 0, mesh.size_of_mesh, ALLEGRO_PRIM_TRIANGLE_LIST);//drawing the mesh as before 9 10//now draw an extra cube 3 units below 0,0,0 11 ALLEGRO_TRANSFORM copy_of_cam, transform; 12 al_identity_transform(&transform); 13 al_copy_transform(&copy_of_cam, &cam); 14 15 al_translate_transform_3d(&transform,0, -3, 0); 16 al_compose_transform(&copy_of_cam, &transform); 17 al_use_transform(&copy_of_cam); 18 19 al_draw_prim(cube, NULL, NULL, 0, std::size(cube), ALLEGRO_PRIM_TRIANGLE_LIST); 20 21 //restore projection 22 //...

Edgar Reynaldo

You need to transform your objects first, before applying the camera transform.

Do the same thing, but swap the order.

```ALLEGRO_TRANSFORM t;
al_identity_transform(&t);
al_translate_transform(&t , 0 , -3 , 0);
al_compose_transform(&t , &cam);
al_use_transform(&t);
```

DontKnow3D

thank you! you've been a great help!
if anybody wants the full source code:https://github.com/dontknow3d/fps-camera-using-allegro-5