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.
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.