This is available from
Fixed point FAQ, 2/2
  旼컴컴컴컴컴컴컴컴컴컴[ Rage Technologies, Inc. ]컴컴컴컴컴컴컴컴컴컴컴컴커
                               - Members -                                  
                         ] Myth: Ideas / Coder [                            
                        ] Night Stalker: Coder [                            
                         ] SKoRPiON: Musician [                             
                            - Support Board -                               
            ] Shadow Lands: (407) 851-2313, run by Night Stalker [          
      How to use Fixed Point (16.16) Math (Part 2 of 2) - by Night Stalker

[1] Introduction

    Welcome to part 2 of the 'How to use Fixed Point Math' series.  We will be
delving into the world of verticies and matricies and other miscellaneous
functions that can be used to assist in 3D rendering.

    This code is straight C.  It capitalizes on part 1's code to be able to do
fixed point multiplication and other operations.  If you haven't read part 1,
you might want to look it over before you continue.

[2] Vectors

    Alright, let's start off with vectors.  We need a good definition of a
vector, especially if we expect to do 3D math.  Since the only type that
seems to fit good with this restriction is your standard  vector,
I think the following definition would work fine:


typedef Fixed32 Vector[3];     // Vector


    Now, how would we access the specific  values inside this vector?
Easy.  You need to make 'macros' to access each value:


// Macros for Vector access
#define VECT_X      0
#define VECT_Y      1
#define VECT_Z      2


    So if you want the Y component of vector 'A', you would use 'A[VECT_Y]'.

[3] Basic vector math and stuff

    Ok, the vector access macros work great, but we need a faster way to 'make'
a vector.  Quite simply done, actually:


void make_vector(Vector v, Fixed32 x, Fixed32 y, Fixed32 z)
    *v++ = x;
    *v++ = y;
    *v   = z;


    See how easy that is?  :)  Just make a pointer to your vector, and
increment it along the way.

    Let's get into some basic math and other basic functions with vectors:


void VectCopy(Vector sv, Vector dv)
    memcpy(dv, sv, 12);

void VectAdd(Vector v1, Vector v2, Vector dv)
    *dv++ = v1[VECT_X] + v2[VECT_X];
    *dv++ = v1[VECT_Y] + v2[VECT_Y];
    *dv   = v1[VECT_Z] + v2[VECT_Z];

void VectSub(Vector v1, Vector v2, Vector dv)
    *dv++ = v1[VECT_X] - v2[VECT_X];
    *dv++ = v1[VECT_Y] - v2[VECT_Y];
    *dv   = v1[VECT_Z] - v2[VECT_Z];

Fixed32 VectLen(Vector v)
    // We use the High Precision Square Root here.
    return FixedSqrtHP(FixedSquare(*v) + FixedSquare(v[1]) + FixedSquare(v[2]));


    VectCopy() does just what it says.  It copies a vector to another one.
Could be useful, could be useless, but very easy to implement.

    VectAdd() and VectSub() also are pretty self-explanatory.

    But, what does VectLen() do?  It returns the length of a vector.  How do
you find the length of a standard  vector?  You don't actually find
the LENGTH of the vector, you find the distance between the vector and the
center of your world at <0, 0, 0>.  Now, if for some reason, you need the
length of the vector squared, you'll find this function is much faster than


Fixed32 VectLen_2(Vector v)
    return (FixedSquare(*v) + FixedSquare(v[1]) + FixedSquare(v[2]));


    As you can see, it doesn't use the square root.  So why square a square
root when you could just chop it off?

    If you're still confused about the length, think back to trigonometry
class in high school.  (Sorry if you're there now.. )  The Pythagorean
Equation was:

                                a = b + c

    But this is two dimensions, not three!  That's why our formula works:

                             a = x + y + z

    Knowing this, you should understand why this next function works:


Fixed32 VectDist(Vector v1, Vector v2)
    return FixedSqrtHP(FixedSquare((*v1) - (*v2)) +
                       FixedSquare(v1[1] - v2[1]) +
                       FixedSquare(v1[2] - v2[2]));

Fixed32 VectDist_2(Vector v1, Vector v2)
    return (FixedSquare((*v1) - (*v2)) + FixedSquare(v1[1] - v2[1]) +
            FixedSquare(v1[2] - v2[2]));


    Same set of rules, except instead of finding the length, you're finding
the distance between two vectors.  And again, if you need your distance value
squared, use VectDist_2().

[4] Even more vector math

    Allright, let's get into some stuff you either learned in Calculus 3, or
are getting ready to.  (Myself being the latter of the two... )


void ScaleVect(Vector v, Fixed32 factor)
    *v++ = FixedMul(*v, factor);
    *v++ = FixedMul(*v, factor);
    *v   = FixedMul(*v, factor);

void NormalizeVect(Vector v)
    Fixed32 factor;

    factor = OneOver(VectLen(v));

    *v++ = FixedMul(*v, factor);
    *v++ = FixedMul(*v, factor);
    *v   = FixedMul(*v, factor);

Fixed32 DotProduct(Vector v1, Vector v2)
    return (FixedMul(v1[VECT_X], v2[VECT_X]) +
            FixedMul(v1[VECT_Y], v2[VECT_Y]) +
            FixedMul(v1[VECT_Z], v2[VECT_Z]));

void CrossProduct(Vector v1, Vector v2, Vector dv)
    *dv++ = FixedMul(v1[VECT_Y], v2[VECT_Z]) - FixedMul(v1[VECT_Z], v2[VECT_Y]);
    *dv++ = FixedMul(v1[VECT_Z], v2[VECT_X]) - FixedMul(v1[VECT_X], v2[VECT_Z]);
    *dv   = FixedMul(v1[VECT_X], v2[VECT_Y]) - FixedMul(v1[VECT_Y], v2[VECT_X]);

Fixed32 CrossZProduct(Vector v1, Vector v2)
    return FixedMul(*v1, v2[VECT_Y]) - FixedMul(v1[VECT_Y], *v2);


    ScaleVect() is pretty self-explanatory.  You pass in a factor, and it
multiplies the entire vector by that factor.

    NormalizeVect() normalizes a vector (hence it's name).  If you don't really
understand what it does, picture this:  You have a polygon (make it 3 sided,
for ease) floating in space.  If you take a normal to this polygon, you will
have a vector standing straight out (perpendicular) in the middle of the
polygon.  Just picture the same thing happening for a vector.

    DotProduct() and CrossProduct() do just what they say.  They take the dot
product and the cross product of two vectors, respectively.  These are quite
useful in calculating light sources.

    CrossZProduct() simply returns the Z component of the cross product, just
in case you might need it for something.  :)

[5] Matricies

    Okay, you know everything about vectors now.  Let's hit matricies.  Again,
we need a good definition.  Well, since most 3D operations can be done with a
3-by-4 matrix (allowing transformational, rotational, and scalar matrix
multiplication), the following definition suits our needs perfectly:


typedef Fixed32 Matrix[12];    // Matrix[3][4]


    And we might as well define an identity matrix so we can create one


Fixed32 identity_matrix[12] =
    ONE,     0,     0,   0,
      0,   ONE,     0,   0,
      0,     0,   ONE,   0,

void make_identity_matrix(Matrix m)
    memcpy(m, identity_matrix, 48);


    What about scaling matricies and rotational matricies, you ask?  Allright,
here they are:


void make_scale_matrix(Matrix m, Vector sv)
    *m++ = *sv++;  *m++ =     0;  *m++ =  0;   *m++ =  0;
    *m++ =     0;  *m++ = *sv++;  *m++ =  0;   *m++ =  0;
    *m++ =     0;  *m++ =     0;  *m++ = *sv;  *m   =  0;

void make_trans_matrix(Matrix m, Vector tv)
    *m++ = ONE;  *m++ =   0;  *m++ =   0;  *m++ = *tv++;
    *m++ =   0;  *m++ = ONE;  *m++ =   0;  *m++ = *tv++;
    *m++ =   0;  *m++ =   0;  *m++ = ONE;  *m   = *tv;

void make_xrot_matrix(Matrix m, Iangle theta)
    Fixed32 trig_sin, trig_cos;

    CosSin(theta, &trig_cos, &trig_sin);

    *m++ = ONE;  *m++ =         0;  *m++ =         0;  *m++ = 0;
    *m++ =   0;  *m++ =  trig_cos;  *m++ =  trig_sin;  *m++ = 0;
    *m++ =   0;  *m++ = -trig_sin;  *m++ = -trig_cos;  *m   = 0;

void make_yrot_matrix(Matrix m, Iangle theta)
    Fixed32 trig_sin, trig_cos;

    CosSin(theta, &trig_cos, &trig_sin);

    *m++ =  trig_cos;  *m++ =   0;  *m++ = -trig_sin;  *m++ = 0;
    *m++ =         0;  *m++ = ONE;  *m++ =         0;  *m++ = 0;
    *m++ = -trig_sin;  *m++ =   0;  *m++ =  trig_cos;  *m   = 0;

void make_zrot_matrix(Matrix m, Iangle theta)
    Fixed32 trig_sin, trig_cos;

    CosSin(theta, &trig_cos, &trig_sin);

    *m++ =  trig_cos;  *m++ =  trig_sin;  *m++ =   0;  *m++ = 0;
    *m++ = -trig_sin;  *m++ =  trig_cos;  *m++ =   0;  *m++ = 0;
    *m++ =         0;  *m++ =         0;  *m++ = ONE;  *m   = 0;


    And a simple copy function to copy one matrix to another:


void MatrixCopy(Matrix sm, Matrix dm)
    memcpy(dm, sm, 48);


    Allright, which ones of you are totally lost about this matrix ordeal and
who isn't?  Those of you that understand, you can skip the next paragraph...

    Having matricies is an ideal way to transform verticies.  The basic idea
is to create a matrix and multiply it by a vector.  So if you had an identity
matrix and multiplied it by all the vectors in your object, nothing would
happen.  However, if you started with an x-rotational matrix of 20 Iangles,
and multiplied each vector by this matrix, your object would rotate on the
X axis by 20 Iangles.  You can also concatenate matricies together to say,
rotate and scale at the same time with just a single matrix!  Hopefully you
understand now.  :)

    Allright, now you need to concatenate two matricies together.  Here's how
you do it:


void ConcatMatrices(Matrix m1, Matrix m2, Matrix dm)
    int i, temp;

    temp = 0;
    i = 3;
    while (i--)
        *dm++ = FixedMul(m1[temp  ], m2[0]) +
                FixedMul(m1[temp+1], m2[4]) +
                FixedMul(m1[temp+2], m2[8]);

        *dm++ = FixedMul(m1[temp  ], m2[1]) +
                FixedMul(m1[temp+1], m2[5]) +
                FixedMul(m1[temp+2], m2[9]);

        *dm++ = FixedMul(m1[temp  ], m2[2]) +
                FixedMul(m1[temp+1], m2[6]) +
                FixedMul(m1[temp+2], m2[10]);

        *dm++ = FixedMul(m1[temp  ], m2[3]) +
                FixedMul(m1[temp+1], m2[7]) +
                FixedMul(m1[temp+2], m2[11]) + m1[temp+3];

        temp += 4;


    Yep, that's it.  If you forgot matrix multiplication, go open your Algebra
book and look it up.  :)

    There's also a couple of other "quicker" methods of adding translation and
rotational matricies together instead of concatenating them together.  You
might find these functions a bit useful to pull out that little bit of extra


void trans_matrix(Matrix m, Vector dv)
    m[ 3] += *dv++;
    m[ 7] += *dv++;
    m[11] += *dv;

void append_xrot_matrix(Matrix m, Iangle theta)
    Fixed32 trig_sin, trig_cos;
    Fixed32 temp0, temp1, temp2;

    CosSin(theta, &trig_cos, &trig_sin);

    temp0 = FixedMul(trig_cos, m[4]) + FixedMul(-trig_sin, m[8]);
    temp1 = FixedMul(trig_cos, m[5]) + FixedMul(-trig_sin, m[9]);
    temp2 = FixedMul(trig_cos, m[6]) + FixedMul(-trig_sin, m[10]);

    m[8]  = FixedMul(trig_sin, m[4]) + FixedMul(trig_cos, m[8]);
    m[9]  = FixedMul(trig_sin, m[5]) + FixedMul(trig_cos, m[9]);
    m[10] = FixedMul(trig_sin, m[6]) + FixedMul(trig_cos, m[10]);

    m[4]  = temp0;
    m[5]  = temp1;
    m[6]  = temp2;

void append_yrot_matrix(Matrix m, Iangle theta)
    Fixed32 trig_cos, trig_sin;
    Fixed32 temp0, temp1, temp2;

    CosSin(theta, &trig_cos, &trig_sin);

    temp0 = FixedMul(trig_cos, m[0]) + FixedMul(trig_sin, m[8]);
    temp1 = FixedMul(trig_cos, m[1]) + FixedMul(trig_sin, m[9]);
    temp2 = FixedMul(trig_cos, m[2]) + FixedMul(trig_sin, m[10]);

    m[8]  = FixedMul(-trig_sin, m[0]) + FixedMul(trig_cos, m[8]);
    m[9]  = FixedMul(-trig_sin, m[1]) + FixedMul(trig_cos, m[9]);
    m[10] = FixedMul(-trig_sin, m[2]) + FixedMul(trig_cos, m[10]);

    m[0]  = temp0;
    m[1]  = temp1;
    m[2]  = temp2;

void append_zrot_matrix(Matrix m, Iangle theta)
    Fixed32 trig_cos, trig_sin;
    Fixed32 temp0, temp1, temp2;

    CosSin(theta, &trig_cos, &trig_sin);

    temp0 = FixedMul(trig_cos, m[0]) + FixedMul(-trig_sin, m[4]);
    temp1 = FixedMul(trig_cos, m[1]) + FixedMul(-trig_sin, m[5]);
    temp2 = FixedMul(trig_cos, m[2]) + FixedMul(-trig_sin, m[6]);

    m[4] = FixedMul(trig_sin, m[0]) + FixedMul(trig_cos, m[4]);
    m[5] = FixedMul(trig_sin, m[1]) + FixedMul(trig_cos, m[5]);
    m[6] = FixedMul(trig_sin, m[2]) + FixedMul(trig_cos, m[6]);

    m[0] = temp0;
    m[1] = temp1;
    m[2] = temp2;


    There's one important function I haven't given to you yet, and it's VITAL
to being able to do these transformations in the first place.  The following
function will take a vector and a matrix and multiply them together to give
you your new vector:


void XformVector(Matrix m, Vector sv, Vector dv)
    Fixed32 sv0, sv1, sv2;

    sv0 = *sv++;
    sv1 = *sv++;
    sv2 = *sv;

    *dv++ = FixedMul(*m++, sv0) + FixedMul(*m++, sv1) +
            FixedMul(*m++, sv2) + *m++;

    *dv++ = FixedMul(*m++, sv0) + FixedMul(*m++, sv1) +
            FixedMul(*m++, sv2) + *m++;

    *dv   = FixedMul(*m++, sv0) + FixedMul(*m++, sv1) +
            FixedMul(*m++, sv2) + *m;


[6] Conclusion

    Well, congradulations!  If you made it this far, then you understand just
about everything there is to know about fixed point math, vector and matrix

    Again, and as always, if you run into any problems, we'll be happy to help
you out.

                                                - Night Stalker


Look for other Rage Technologies, Inc. stuff coming soon:

        - Our first major demo, "Transvectoring".  The theme is to
          show off our new 3-D engine with lightsourcing and texture
          mapping... REALLY fast.  Also to show what objects beyond
          3D really look like.  For example, a 4D or a 5D cube.  Maybe
          more.  Expected release date:  Mid '95 (?)

        - Night Hawk 0.2 BBS.  The first BBS software to show that
          ANSI is dead, and RIP is a thing of the past.  Features
          include: True multitasking, full video and audio routines,
          and more.  Expected release date:  Early/Mid '96.


Other news:

        - Shadow Lands is still not up.  Blame Night Stalker.  He's too
          lazy to sell his old 486/33 to run the board on a DX4-100.
          We'll let you know when he gets off his duff and has Shadow
          Lands online.

        - Rage Technologies, Inc. has a mailing list.  If you'd like to
          get ahold of any one of us, send E-mail to:


        - Rage Technologies, Inc. also has an experimental FTP server
          running.  If you would like to get any Rage products, simply
          anonymous FTP to:  All Rage files
          are located in /pub/ragetech.

Back to the3D Game Programming Resource page.