8
\$\begingroup\$

I'm teaching myself 3D mathematics and in the process building my own rudimentary engine (of sorts). I was wondering what would be the best way to structure my C++ matrix class. There are a few options:

  1. Separate member variables:

    struct Mat4 {
        float
            m11, m12, m13, m14,
            m21, m22, m23, m24,
            m31, m32, m33, m34,
            m41, m42, m43, m44;
    
        // methods
    
    }
    
  2. A multi-dimensional array:

    struct Mat4 {
        float[4][4] m;
    
        // methods
    
    }
    
  3. An array of vectors

    struct Mat4 {
        Vec4[4] m;
    
        // methods
    
    }
    

I'm guessing there would be positives and negatives to each.

From 3D Math Primer for Graphics and Game Development, 2nd Edition p.155:

n-by-m matrix. Source (Wikipedia)

Matrices use 1-based indices, so the first row and column are numbered 1. For example, a12 (read “a one two,” not “a twelve”) is the element in the first row, second column. Notice that this is different from programming languages such as C++ and Java, which use 0-based array indices. A matrix does not have a column 0 or row 0. This difference in indexing can cause some confusion if matrices are stored using an actual array data type. For this reason, it’s common for classes that store small, fixed size matrices of the type used for geometric purposes to give each element its own named member variable, such as float a11, instead of using the language’s native array support with something like float elem[3][3].

So that's one vote for method one.

Is this really the accepted way to do things? It seems rather unwieldy if the only benefit would be sticking with the conventional math notation.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ Think about the interface of your matrix first (the public member functions), for instance you might need an GetRow or GetForwardVector, think about how to implement them most efficiently, then the internal datastructures just fall into place naturally \$\endgroup\$ Jul 7, 2012 at 14:22
  • 2
    \$\begingroup\$ In C++ you can use both arrays and member variables at the same time btw, Unions :) \$\endgroup\$
    – API-Beast
    Jul 7, 2012 at 14:36
  • 1
    \$\begingroup\$ I would definitely go for method one since for a lot of linear algebra you need a few specific cells in your matrix. The arrays are never going to contain just those you need so you will always need to index multiple arrays for simple questions, like the above mentioned forward vector. This is also how Microsoft does it in their XNA framework (but of course one company doing it isn't the best argument) \$\endgroup\$
    – Roy T.
    Jul 7, 2012 at 15:45
  • 2
    \$\begingroup\$ I would definitely stick with zero-indexed arrays - I do a lot of scientific computing, and it is a lot less confusing to convert the indices when moving between maths and code, than to be inconsistent within your code. If you go with option 2 or 3, it will be easier to make your routines work with any size of matrices, which you might well end up needing for something (for example, I guess you will probably have a matrix multiplication routine, which you might use for matrixmatrix or matrixvector). \$\endgroup\$
    – James
    Jul 7, 2012 at 20:56

1 Answer 1

13
\$\begingroup\$

Why choose? You can have both. (Without any added complexity to logic and without any additional memory requirement thanks to unions.)

struct Mat4
{
    union
    {
        struct
        {
          float m11, m12, m13, m14,
                m21, m22, m23, m24,
                m31, m32, m33, m34,
                m41, m42, m43, m44;
        };
        float[4][4] m;
    };
// methods
}
\$\endgroup\$
5
  • 1
    \$\begingroup\$ +1, I normally use float[4][4] m4x4 and float[16] m16 as well as the individual elements, but this is something that you tailor to your own program's requirements. \$\endgroup\$ Jul 7, 2012 at 17:20
  • 1
    \$\begingroup\$ Note that you often need compiler-specific extensions to make this specific thing work, as ISO C++ does not allow anonymous structures (but in practice it will work on most of the major compilers with extensions). \$\endgroup\$
    – user1430
    Jul 7, 2012 at 17:32
  • \$\begingroup\$ I guess you would get away with this approach, but... Is it "allowed" according to ISO c++ to read it as "m", when it was written as "m11, m22, ..."? \$\endgroup\$
    – André
    Jul 7, 2012 at 17:58
  • 1
    \$\begingroup\$ @Andre it is not explicitly allowed by C++ but every major compiler supports it (it’s called type-punning through anonymous unions) as a documented extension. It is also explicitly allowed in C. \$\endgroup\$ Jul 7, 2012 at 18:41
  • 1
    \$\begingroup\$ You mean I can have my cake and eat it too?! Thanks a bunch mate! (it works in D as well, hehe) \$\endgroup\$
    – brendanzab
    Jul 8, 2012 at 5:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .