1 /*
2 * Example program for the Allegro library, by Shawn Hargreaves
3 * converted to OpenGL/AllegroGL.
4 *
5 * This program demonstrates how to easily manipulate a camera
6 * in OpenGL to view a 3d world from any position and angle.
7 * Quaternions are used, because they're so easy to work with!
8 */
9
10
11 #include <stdio.h>
12 #include <math.h>
13
14 #include <allegro.h>
15 #include <alleggl.h>
16 #ifdef ALLEGRO_MACOSX
17 #include <OpenGL/glu.h>
18 #else
19 #include <GL/glu.h>
20 #endif
21
22
23 /* Define M_PI in case the compiler doesn't */
24 #ifndef M_PI
25 #define M_PI 3.1415926535897932384626433832795
26 #endif
27
28
29 /* Define a 3D vector type */
30 typedef struct VECTOR {
31 float x, y, z;
32 } VECTOR;
33
34
35 /* display a nice 12x12 chessboard grid */
36 #define GRID_SIZE 12
37
38
39
40 /* Parameters controlling the camera and projection state */
41 int viewport_w = 320; /* Viewport Width (pixels) */
42 int viewport_h = 240; /* Viewport Height (pixels) */
43 int fov = 48; /* Field of view (degrees) */
44 float aspect = 1; /* Aspect ratio */
45
46 /* Define the camera
47 * We need: One position vector, and one orientation QUAT
48 */
49 struct CAMERA {
50 VECTOR position;
51 QUAT orientation;
52 } camera;
53
54
55
56 /* A simple font to display some info on screen */
57 FONT *agl_font;
58
59
60
61 /* Sets up the viewport to designated values */
set_viewport()62 void set_viewport() {
63 glViewport((SCREEN_W - viewport_w) / 2, (SCREEN_H - viewport_h) / 2,
64 viewport_w, viewport_h);
65 }
66
67
68
69 /* Sets up the camera for displaying the world */
set_camera()70 void set_camera() {
71 float theta;
72
73 /* First, we set up the projection matrix.
74 * Note that SCREEN_W / SCREEN_H = 1.333333, so we need to multiply the
75 * aspect ratio by that value so that the display doesn't get distorted.
76 */
77 glMatrixMode(GL_PROJECTION);
78 glLoadIdentity();
79 gluPerspective((float)fov, aspect * 1.333333, 1.0, 120.0);
80 glMatrixMode(GL_MODELVIEW);
81 glLoadIdentity();
82
83 /* Macro to convert radians to degrees */
84 #define RAD_2_DEG(x) ((x) * 180 / M_PI)
85
86 /* Convert the QUAT to something OpenGL can understand
87 * We can use allegro_gl_apply_quat() here, but I'd just like
88 * to show how it can be done with regular GL code.
89 *
90 * Since we're working with the camera, we have to rotate first,
91 * and then translate. Objects are done the other way around.
92 */
93 theta = RAD_2_DEG(2 * acos(camera.orientation.w));
94 if (camera.orientation.w < 1.0f && camera.orientation.w > -1.0f) {
95 glRotatef(theta, camera.orientation.x, camera.orientation.y,
96 camera.orientation.z);
97 }
98
99 glTranslatef(-camera.position.x, -camera.position.y, -camera.position.z);
100
101 #undef RAD_2_DEG
102 }
103
104
105
106 /* Draw the (simple) world
107 * Notice how the camera doesn't affect the positioning.
108 */
draw_field()109 void draw_field() {
110
111 int i, j;
112
113 for (j = 0; j < GRID_SIZE; j++) {
114 for (i = 0; i < GRID_SIZE; i++) {
115 glPushMatrix();
116 glTranslatef(i * 2 - GRID_SIZE + 1, -2, j * 2 - GRID_SIZE + 1);
117
118 if ((i + j) & 1) {
119 glColor3ub(255, 255, 0);
120 }
121 else {
122 glColor3ub(0, 255, 0);
123 }
124
125 glBegin(GL_QUADS);
126 glVertex3f(-1, 0, -1);
127 glVertex3f(-1, 0, 1);
128 glVertex3f( 1, 0, 1);
129 glVertex3f( 1, 0, -1);
130 glEnd();
131 glPopMatrix();
132 }
133 }
134 }
135
136
137
138 /* For display, we'd like to convert the QUAT back to heading, pitch and roll
139 * These don't serve any purpose but to make it look human readable.
140 * Note: Produces incorrect results.
141 */
convert_quat(QUAT * q,float * heading,float * pitch,float * roll)142 void convert_quat(QUAT *q, float *heading, float *pitch, float *roll) {
143 MATRIX_f matrix;
144 quat_to_matrix(q, &matrix);
145
146 *heading = atan2(matrix.v[0][2], matrix.v[0][0]);
147 *pitch = asin(matrix.v[0][1]);
148 *roll = atan2(matrix.v[2][1], matrix.v[2][0]);
149 }
150
151
152
153 /* Draws the overlay over the field. The position of the overlay is
154 * independent of the camera.
155 */
draw_overlay()156 void draw_overlay() {
157 float heading, pitch, roll;
158 int color;
159 VECTOR v;
160
161 /* Set up the viewport so that it takes up the whole screen */
162 glViewport(0, 0, SCREEN_W, SCREEN_H);
163
164 /* Draw a line around the viewport */
165 allegro_gl_set_projection();
166
167 glColor3ub(255, 0, 0);
168 glDisable(GL_DEPTH_TEST);
169
170 glBegin(GL_LINE_LOOP);
171 glVertex2i((SCREEN_W - viewport_w) / 2, (SCREEN_H - viewport_h) / 2);
172 glVertex2i((SCREEN_W + viewport_w) / 2 - 1,
173 (SCREEN_H - viewport_h) / 2);
174 glVertex2i((SCREEN_W + viewport_w) / 2 - 1,
175 (SCREEN_H + viewport_h) / 2 - 1);
176 glVertex2i((SCREEN_W - viewport_w) / 2,
177 (SCREEN_H + viewport_h) / 2 - 1);
178 glEnd();
179
180 /* Overlay some text describing the current situation */
181 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
182 color = 0;
183 glTranslatef(-0.375, -0.375, 0);
184 allegro_gl_printf(agl_font, 0, 0, 0, color,
185 "Viewport width: %03d pix (w/W changes)", viewport_w);
186 allegro_gl_printf(agl_font, 0, 8, 0, color,
187 "Viewport height: %03d pix (h/H changes)", viewport_h);
188 allegro_gl_printf(agl_font, 0, 16, 0, color,
189 "Field Of View: %02d deg (f/F changes)", fov);
190 allegro_gl_printf(agl_font, 0, 24, 0, color,
191 "Aspect Ratio: %.2f (a/A changes)", aspect);
192 allegro_gl_printf(agl_font, 0, 32, 0, color,
193 "X position: %+.2f (x/X changes)", camera.position.x);
194 allegro_gl_printf(agl_font, 0, 40, 0, color,
195 "Y position: %+.2f (y/Y changes)", camera.position.y);
196 allegro_gl_printf(agl_font, 0, 48, 0, color,
197 "Z position: %+.2f (z/Z changes)", camera.position.z);
198
199 /* Convert the orientation QUAT into heading, pitch and roll to display */
200 convert_quat(&camera.orientation, &heading, &pitch, &roll);
201
202 allegro_gl_printf(agl_font, 0, 56, 0, color,
203 "Heading: %+.2f deg (left/right changes)", heading * 180 / M_PI);
204 allegro_gl_printf(agl_font, 0, 64, 0, color,
205 "Pitch: %+.2f deg (pgup/pgdn changes)", pitch * 180 / M_PI);
206 allegro_gl_printf(agl_font, 0, 72, 0, color,
207 "Roll: %+.2f deg (r/R changes)", roll * 180 / M_PI);
208
209 apply_quat(&camera.orientation, 0, 0, -1, &v.x, &v.y, &v.z);
210
211 allegro_gl_printf(agl_font, 0, 80, 0, color,
212 "Front Vector: %.2f, %.2f, %.2f", v.x, v.y, v.z);
213
214 apply_quat(&camera.orientation, 0, 1, 0, &v.x, &v.y, &v.z);
215
216 allegro_gl_printf(agl_font, 0, 88, 0, color,
217 "Up Vector: %.2f, %.2f, %.2f", v.x, v.y, v.z);
218 allegro_gl_printf(agl_font, 0, 96, 0, color,
219 "QUAT: %f, %f, %f, %f ", camera.orientation.w,
220 camera.orientation.x, camera.orientation.y, camera.orientation.z);
221
222 allegro_gl_unset_projection();
223
224 glBlendFunc(GL_ONE, GL_ZERO);
225 glEnable(GL_DEPTH_TEST);
226 }
227
228
229
230 /* draw everything */
render()231 void render()
232 {
233 set_viewport();
234
235 glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
236 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
237
238 set_camera();
239
240 draw_field();
241
242 draw_overlay();
243
244 glFlush();
245
246 allegro_gl_flip();
247 }
248
249
250
251 /* deal with user input */
process_input(void)252 void process_input(void) {
253
254 QUAT q;
255
256 poll_keyboard();
257
258 if (key[KEY_W]) {
259 if (key_shifts & KB_SHIFT_FLAG) {
260 if (viewport_w < SCREEN_W)
261 viewport_w += 8;
262 }
263 else {
264 if (viewport_w > 16)
265 viewport_w -= 8;
266 }
267 }
268
269 if (key[KEY_H]) {
270 if (key_shifts & KB_SHIFT_FLAG) {
271 if (viewport_h < SCREEN_H)
272 viewport_h += 8;
273 }
274 else {
275 if (viewport_h > 16)
276 viewport_h -= 8;
277 }
278 }
279
280 if (key[KEY_X]) {
281 if (key_shifts & KB_SHIFT_FLAG)
282 camera.position.x += 0.05;
283 else
284 camera.position.x -= 0.05;
285 }
286
287 if (key[KEY_Y]) {
288 if (key_shifts & KB_SHIFT_FLAG)
289 camera.position.y += 0.05;
290 else
291 camera.position.y -= 0.05;
292 }
293
294 if (key[KEY_Z]) {
295 if (key_shifts & KB_SHIFT_FLAG)
296 camera.position.z += 0.05;
297 else
298 camera.position.z -= 0.05;
299 }
300
301 if (key[KEY_UP]) {
302 VECTOR front;
303 /* Note: We use -1 here because Allegro's coordinate system
304 * is slightly different than OpenGL's.
305 */
306 apply_quat(&camera.orientation, 0, 0, -1, &front.x, &front.y, &front.z);
307 camera.position.x += front.x / 10;
308 camera.position.y += front.y / 10;
309 camera.position.z += front.z / 10;
310 }
311 if (key[KEY_DOWN]) {
312 VECTOR front;
313 apply_quat(&camera.orientation, 0, 0, -1, &front.x, &front.y, &front.z);
314 camera.position.x -= front.x / 10;
315 camera.position.y -= front.y / 10;
316 camera.position.z -= front.z / 10;
317 }
318
319
320 /* When turning right or left, we only want to change the heading.
321 * That is, we only want to rotate around the absolute Y axis
322 */
323 if (key[KEY_LEFT]) {
324 get_y_rotate_quat(&q, -1);
325 quat_mul(&camera.orientation, &q, &camera.orientation);
326 }
327 if (key[KEY_RIGHT]) {
328 get_y_rotate_quat(&q, 1);
329 quat_mul(&camera.orientation, &q, &camera.orientation);
330 }
331
332 /* However, when rolling or changing pitch, we do a rotation relative to
333 * the current orientation of the camera. This is why we extract the
334 * 'right' and 'front' vectors of the camera and apply a rotation on
335 * those.
336 */
337 if (key[KEY_PGUP]) {
338 VECTOR right;
339 apply_quat(&camera.orientation, 1, 0, 0, &right.x, &right.y, &right.z);
340 get_vector_rotation_quat(&q, right.x, right.y, right.z, -1);
341 quat_mul(&camera.orientation, &q, &camera.orientation);
342 }
343 if (key[KEY_PGDN]) {
344 VECTOR right;
345 apply_quat(&camera.orientation, 1, 0, 0, &right.x, &right.y, &right.z);
346 get_vector_rotation_quat(&q, right.x, right.y, right.z, 1);
347 quat_mul(&camera.orientation, &q, &camera.orientation);
348 }
349
350 if (key[KEY_R]) {
351 VECTOR front;
352 apply_quat(&camera.orientation, 0, 0, 1, &front.x, &front.y, &front.z);
353
354 if (key_shifts & KB_SHIFT_FLAG)
355 get_vector_rotation_quat(&q, front.x, front.y, front.z, -1);
356 else
357 get_vector_rotation_quat(&q, front.x, front.y, front.z, 1);
358
359 quat_mul(&camera.orientation, &q, &camera.orientation);
360 }
361
362 if (key[KEY_F]) {
363 if (key_shifts & KB_SHIFT_FLAG) {
364 if (fov < 96)
365 fov++;
366 }
367 else {
368 if (fov > 16)
369 fov--;
370 }
371 }
372
373 if (key[KEY_A]) {
374 if (key_shifts & KB_SHIFT_FLAG) {
375 aspect += 0.05;
376 if (aspect > 2)
377 aspect = 2;
378 }
379 else {
380 aspect -= 0.05;
381 if (aspect < .1)
382 aspect = .1;
383 }
384 }
385 }
386
387
388
main(void)389 int main(void) {
390
391 allegro_init();
392 install_allegro_gl();
393 install_keyboard();
394 install_timer();
395
396 /* Initialise the camera */
397 camera.orientation = identity_quat;
398 camera.position.x = 0;
399 camera.position.y = 0;
400 camera.position.z = 4;
401
402 /* Set up AllegroGL */
403 allegro_gl_clear_settings();
404 allegro_gl_set (AGL_COLOR_DEPTH, 16);
405 allegro_gl_set (AGL_Z_DEPTH, 16);
406 allegro_gl_set (AGL_DOUBLEBUFFER, 1);
407 allegro_gl_set (AGL_RENDERMETHOD, 1);
408 allegro_gl_set (AGL_WINDOWED, TRUE);
409 allegro_gl_set (AGL_SUGGEST, AGL_Z_DEPTH | AGL_DOUBLEBUFFER
410 | AGL_RENDERMETHOD | AGL_WINDOWED | AGL_COLOR_DEPTH);
411
412 if (set_gfx_mode(GFX_OPENGL, 640, 480, 0, 0) != 0) {
413 set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
414 allegro_message ("Error setting OpenGL graphics mode:\n%s\n"
415 "Allegro GL error : %s\n",
416 allegro_error, allegro_gl_error);
417 return 1;
418 }
419
420 /* Set up OpenGL */
421 glEnable(GL_DEPTH_TEST);
422 glCullFace(GL_BACK);
423 glEnable(GL_CULL_FACE);
424 glEnable(GL_TEXTURE_2D);
425 glEnable(GL_BLEND);
426
427 glShadeModel(GL_SMOOTH);
428
429 /* Build the font we'll use to display info */
430 agl_font = allegro_gl_convert_allegro_font_ex(font,
431 AGL_FONT_TYPE_TEXTURED, -1.0, GL_ALPHA8);
432
433 glBindTexture(GL_TEXTURE_2D, 0);
434
435 /* Run the example program */
436 while (!key[KEY_ESC]) {
437 render();
438
439 process_input();
440 rest(2);
441 }
442
443 allegro_gl_destroy_font(agl_font);
444
445 return 0;
446 }
447 END_OF_MAIN()
448
449