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