The Good Old Matrix Library
For the past week or so, I've been rethinking some of my old assumptions about 3D engine and game programming, starting from the ground up. I've even been changing the way I do the most basic things, such as my matrix library. I'm posting this mostly as a chronicle of what I did and what I learned from it so you don't have to make my mistakes if you try this; this will probably be a sort of long readl...
Originally, I have my Vector{2|3|4}D classes and then Matrix4x4 and Matrix3x3 - five peice of duplicate code that operated the same way on their members except for a few small things (such as matrix determinant and vector projection). I had seen what was done by the guy who runs http://www.fluidstudios.com/ and liked it since it did all five of those in one thing only he used some tricks that I didn't like. And thus begin the template matrix library.
The first thing I did was define the basic Matrix class (the funny types are ones that I use and many of the projects I work with use for crossplatform stuff - they are just the basic types):
Once I had written all the operators (like +, -, and scalar * and /) using for loops, I tested it and it worked great. Then I tried specializing it as follows:
That went great until I relaized that not only would I have to duplicate the prototypes for operators in the class, but I would also have to redefine them. That took me back to square one because I'd end up with 3 implementations again at least (matrix, square matrix, and vector). I also couldn't name them what I wanted like SquareMatrix and Vector for the specializations.
After a bit of thought, I decided to use inheritance. Because I'd have no virtual functions or use any of the polymorphism mechanisms, it would be completely free - no speed cost at all. It would also allow me to have a distinction between a Matrix<1, 3> and a 3D vector. So, going back to the IDE, I wrote this:
This worked very well. It required a little trickery in the vector class to do matrix multiplication to work well but it eventually qorked quite nicely. Still a few kinks, such as how to further specialize things like Vector<3> for corss products - those were only minor and led to a little code duplication but not nearly as much as before.
Now that I had something that worked, I started putting all of this into my math namespace. This went smoothly (I could even move the default template parameters to the central math.h file) until I started putting in the specialized versions of the classes:
All of those files of course had inclusion guards (not shown for the sake of brevity). I also prefixed all the member delcarations outisde the class with the template stuff and the math:: scope. That was as far as I got with that working. When I tried to put the Vector<3> specialization in, GCC choked with an error that indicated to put it in the namespace. So, rather than just prefixing the specialized class with math::, I put it in the namespace:
And viola, it worked! That is the current state of things; I might post some specific stuff later but hopefully this will save anyone who tries to do this or something similar with templates a bit of time. If you didn't get all of the stuff with the templates, grab a good book on them - they are great when used properly.
EDIT: This might be more appropriate in the game programming forum; there was no math forum and since this is mostly 3D related, I put it here.
Originally, I have my Vector{2|3|4}D classes and then Matrix4x4 and Matrix3x3 - five peice of duplicate code that operated the same way on their members except for a few small things (such as matrix determinant and vector projection). I had seen what was done by the guy who runs http://www.fluidstudios.com/ and liked it since it did all five of those in one thing only he used some tricks that I didn't like. And thus begin the template matrix library.
The first thing I did was define the basic Matrix class (the funny types are ones that I use and many of the projects I work with use for crossplatform stuff - they are just the basic types):
Code:
template <uints m, uints n, typename T = real32> class Matrix
{
protected: T data[m * n];
public:
//Operators and other stuff
};
Once I had written all the operators (like +, -, and scalar * and /) using for loops, I tested it and it worked great. Then I tried specializing it as follows:
Code:
template <uints n, typename T> class Matrix<n, T>
{
//Same predeclarations including data; it was a bit awkward but I could go with it
protected: T data[n * n];
public: //...
};
That went great until I relaized that not only would I have to duplicate the prototypes for operators in the class, but I would also have to redefine them. That took me back to square one because I'd end up with 3 implementations again at least (matrix, square matrix, and vector). I also couldn't name them what I wanted like SquareMatrix and Vector for the specializations.
After a bit of thought, I decided to use inheritance. Because I'd have no virtual functions or use any of the polymorphism mechanisms, it would be completely free - no speed cost at all. It would also allow me to have a distinction between a Matrix<1, 3> and a 3D vector. So, going back to the IDE, I wrote this:
Code:
template <uints n, typename T = real32> class SquareMatrix : public Matrix<n, n, T>
{
public: //Square matrix specific stuff only, yay!
};
template <uints m, typename T = real32> class Vector : public Matrix<m, 1, T>
{
public: //Vector only stuff here
};
This worked very well. It required a little trickery in the vector class to do matrix multiplication to work well but it eventually qorked quite nicely. Still a few kinks, such as how to further specialize things like Vector<3> for corss products - those were only minor and led to a little code duplication but not nearly as much as before.
Now that I had something that worked, I started putting all of this into my math namespace. This went smoothly (I could even move the default template parameters to the central math.h file) until I started putting in the specialized versions of the classes:
Code:
//math.h
namespace math
{
template <uints m, uints n, typename T = real32> class Matrix;
template <uints n, typename T = real32> class SquareMatrix;
template <uints m, typename T = real32> class Vector;
}
#include "matrix.h"
#include "squarematrix.h"
#incldue "vector.h"
//matrix.h
#include "math.h"
template <uints m, uints n, typename T> class math::Matrix
{
protected: T data[m * n];
public:
//Operators and other stuff
};
//squarematrix.h
#include "math.h"
template <uints n, typename T> class math::SquareMatrix : public math::Matrix<n, n, T>
{
public: //Square matrix specific stuff only, yay!
};
//vector.h
#include "math.h"
template <uints m, typename T> class math::Vector : public math::Matrix<m, 1, T>
{
public: //Vector only stuff here
};
All of those files of course had inclusion guards (not shown for the sake of brevity). I also prefixed all the member delcarations outisde the class with the template stuff and the math:: scope. That was as far as I got with that working. When I tried to put the Vector<3> specialization in, GCC choked with an error that indicated to put it in the namespace. So, rather than just prefixing the specialized class with math::, I put it in the namespace:
Code:
namespace math
{
template <typename T> class Vector<3, T> : public Matrix<3, 1, T>
{
//duplicate members and that plus a few 3D only stuff like the cross product - I could deal with this little duplication
}
}
And viola, it worked! That is the current state of things; I might post some specific stuff later but hopefully this will save anyone who tries to do this or something similar with templates a bit of time. If you didn't get all of the stuff with the templates, grab a good book on them - they are great when used properly.
EDIT: This might be more appropriate in the game programming forum; there was no math forum and since this is mostly 3D related, I put it here.
Possibly Related Threads...
Thread: | Author | Replies: | Views: | Last Post | |
Looking for a good 2D sprite library | Joseph Duchesne | 3 | 3,993 |
Jul 16, 2003 07:23 PM Last Post: Josh |