Tricky Vector3

May 8, 2011

Anyone who’s done any work in 3D is familiar with some sort of Vector class. I’m not talking about STL’s std::vector, which is used more as a list, no I’m talking about specialised maths classes for the purpose of doing linear algebra: Vector2, Vector3, Vector4 and their 2D counterparts Matrix2x3, Matrix3x3 and Matrix4x4. Vector3 and Vector4 I tend to use for colours as well, where x, y, z, w maps to r, g, b, a. But I’ve always wanted my Vector classes to be able to use either of those names, and be able to use the Vector as an array of values.

i.e.:

Vector3 position;

position.X = 1.0f;

 

Vector3 color;

color.R = 1.0f;

 

Vector3 something;

something[0] = 1.0f;

are all valid code.

 

The way I’d been doing it before was using an anonymous union and an anonymous struct:

 

struct Vector3

{

    union

    {

        struct {float X; float Y; float Z;}

        struct {float R; float G; float B;}

        float array[3];

    };

    const float& operator[](size_t i) const { return array[i]; }

    float& operator[](size_t i) { return array[i]; }

};

And this did what I needed it to, but with an annoying side effect: I kept getting compiler warnings about my anonymous structs.

 

Anonymous structs are not a part of the C++ standard. The reason why this is so evades me, but it seems the popular compilers – Visual Studio and gcc – implemented extensions for supporting them a while ago. However, they still throw that warning at me, and I’m a “warnings as errors” kind of guy, so I determined to find a fix for this. Eventually.

 

Recently I stumbled upon a post on the GameDev forums which described how to do such a thing, and after staring at the code until it felt like my eyes were bleeding I came to understand it. So here is a standards-compliant Vector class:

 

struct Vector3

{

private:

    typedef float Vector3::* const locations[3];

    static const locations v;

public:

    union { float X; float R; };

    union { float Y; float G; };

    union { float Z; float B; };

    

    const float& operator[](size_t i) const { return this->*v[i]; }

    float& operator[](size_t i) { return this->*v[i]; }

};

const Vector3::locations Vector3::v = { &Vector3::X, &Vector3::Y, &Vector3::Z };

 

Confused? I know I was. Here’s an explanation:

The Vector3::* type is a special type called a pointer-to-member. I find it useful to think of it as an offset into any class of type Vector3 (e.g. the value for v[0] is found 2-bytes from the start of the ‘this’ pointer, although that’s apparently not technically true. The pointer-to-member operator ->* is used to access the value of the pointer-to-member in a specific instance of the class.

So in the Vector3 class there’s a constant static list of 3 pointer-to-members that let us overload the [] operator and treat X, Y and Z as if they were arrays. And the great thing is, there’s almost no memory overhead for it: a good compiler will recognise the const allocations and optimise the pointers away completely.

 

The anonymous unions allow you to refer to each component by either name (and them both being the same type removes any weirdness that might happen from assigning to one and then the other), and C++ doesn’t allow compilers to re-order or place extra bytes between members for alignment so it’s safe to treat <code>&X</code> as the start of a float array of length 3.

 

And that’s my new Vector3 class. For fun, here’s a templated version

template <typename T>

struct Vector3_t

{

private:

    typedef T Vector3_t<T>::* const locations[3];

    static const locations loc;

public:

    union { T X; T R; };

    union { T Y; T G; };

    union { T Z; T B; };

    const T& operator[](size_t i) const { return this->*loc[i]; }

    T& operator[](size_t i) { return this->*loc[i]; }

};

template<typename T>

const typename Vector3_t<T>::locations Vector3_t<T>::loc = { &Vector3_t<T>::X, &Vector3_t<T>::Y, &Vector3_t<T>::Z };

 

typedef Vector3_t<float> Vector3;

see here for an explanation of that second typename in the array initialisation.

Posted by | Categories: Blog |

Share with others

No Responses so far | Have Your Say!

Leave a Feedback

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Current day month ye@r *