3D math routines

Allegro contains some 3d helper functions for manipulating vectors, constructing and using transformation matrices, and doing perspective projections from 3d space onto the screen. It is not, and never will be, a fully fledged 3d library (the goal is to supply generic support routines, not shrink-wrapped graphics code :-) but these functions may be useful for developing your own 3d code.

Allegro uses a right-handed coordinate system, i.e. if you point the thumb of your right hand along the x axis, and the index finger along the y axis, your middle finger points in the direction of the z axis.

Allegro's world coordinate system typically has the positive x axis right, the positive y axis up, and the positive z axis out of the screen. What all this means is this: Assume, the viewer is located at the origin (0/0/0) in world space, looks along the negative z axis (0/0/-1), and is oriented so up is along the positive y axis (0/1/0). Then something located at (100/200/-300) will be 100 to the right, 200 above, and 300 in front of the viewer. Just like in OpenGL. (Of course, both OpenGL and Allegro allow to use a different system.) Here's a short piece of code demonstrating the transformation pipeline of a point from world space to the screen.

   /* First, set up the projection viewport. */
   set_projection_viewport (0, 0, SCREEN_W, SCREEN_H);

/* Next, get a camera matrix, depending on the * current viewer position and orientation. */ get_camera_matrix_f (&m, 0, 0, 0, /* Viewer position, in this case, 0/0/0. */ 0, 0, -1, /* Viewer direction, in this case along negative z. */ 0, 1, 0, /* Up vector, in this case positive y. */ 32, /* The FOV, here 45°. */ (float)SCREEN_W / (float)SCREEN_H)); /* Aspect ratio. */

/* Applying the matrix transforms the point 100/200/-300 * from world space into camera space. The transformation * moves and rotates the point so it is relative to the * camera, scales it according to the FOV and aspect * parameters, and also flips up and front direction - * ready to project the point to the viewport. */ apply_matrix_f (&m, 100, 200, -300, &x, &y, &z);

/* Finally, the point is projected from * camera space to the screen. */ persp_project_f (cx, cy, cz, &sx, &sy);

For more details, look at the function descriptions of set_projection_viewport(), get_camera_matrix(), and persp_project(), as well as the relevant example programs.

All the 3d math functions are available in two versions: one which uses fixed point arithmetic, and another which uses floating point. The syntax for these is identical, but the floating point functions and structures are postfixed with '_f', eg. the fixed point function cross_product() has a floating point equivalent cross_product_f(). If you are programming in C++, Allegro also overloads these functions for use with the 'fix' class.

3d transformations are accomplished by the use of a modelling matrix. This is a 4x4 array of numbers that can be multiplied with a 3d point to produce a different 3d point. By putting the right values into the matrix, it can be made to do various operations like translation, rotation, and scaling. The clever bit is that you can multiply two matrices together to produce a third matrix, and this will have the same effect on points as applying the original two matrices one after the other. For example, if you have one matrix that rotates a point and another that shifts it sideways, you can combine them to produce a matrix that will do the rotation and the shift in a single step. You can build up extremely complex transformations in this way, while only ever having to multiply each point by a single matrix.

Allegro actually cheats in the way it implements the matrix structure. Rotation and scaling of a 3d point can be done with a simple 3x3 matrix, but in order to translate it and project it onto the screen, the matrix must be extended to 4x4, and the point extended into 4d space by the addition of an extra coordinate, w=1. This is a bad thing in terms of efficiency, but fortunately an optimisation is possible. Given the 4x4 matrix:
   ( a, b, c, d )
   ( e, f, g, h )
   ( i, j, k, l )
   ( m, n, o, p )
a pattern can be observed in which parts of it do what. The top left 3x3 grid implements rotation and scaling. The three values in the top right column (d, h, and l) implement translation, and as long as the matrix is only used for affine transformations, m, n and o will always be zero and p will always be 1. If you don't know what affine means, read Foley & Van Damme: basically it covers scaling, translation, and rotation, but not projection. Since Allegro uses a separate function for projection, the matrix functions only need to support affine transformations, which means that there is no need to store the bottom row of the matrix. Allegro implicitly assumes that it contains (0,0,0,1), and optimises the matrix manipulation functions accordingly. Read chapter "Structures and types defined by Allegro" for an internal view of the MATRIX/_f structures.