How can glm data types be transmitted directly to the GPU via OpenGL buffers?

advertisements

I have written math utilities myself, which do everything I need for the simple graphics I am programming. However, I don't know how to make them eligible for passing directly to OpenGL. This can be done with glm, e.g.:

std::vector<glm::vec3> locations;

[...]

glGenBuffers(NUM_BUFFERS, _vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo[POSITION_VBO]);

// throw data in vbo[0]
glBufferData(GL_ARRAY_BUFFER, _num_vertices * sizeof(locations[0]), &locations[0], GL_STATIC_DRAW);

I would like to be able to do this with my own vec3 type, math::vec3f, as I wouldn't like to have "wasted" my own time writing these utilities. Its implementation can be seen here:

namespace math
{
    template<typename _type> class vec2;

    template<typename _type>
    class vec3
    {
        private:
            _type _gl_a[3];

        public:
            _type x, y, z;

            vec3() {};
            vec3(_type _x, _type _y, _type _z)
            {
                _gl_a[0] = x = _x;
                _gl_a[1] = y = _y;
                _gl_a[2] = z = _z;
            }

            vec3(vec2<_type> v, _type w)
            {
                _gl_a[0] = x = v.x;
                _gl_a[1] = y = v.y;
                _gl_a[2] = z = w;
            }

            inline vec3<_type> operator=(vec2<_type> &v)
            {
                _gl_a[0] = x = v.x;
                _gl_a[1] = y = v.y;
                _gl_a[2] = z = 0;
            }

            inline vec3<_type> operator+(_type other)        { return vec3<_type>(x + other,   y + other,   z + other); }
            inline vec3<_type> operator-(_type other)        { return vec3<_type>(x - other,   y - other,   z - other); }
            inline vec3<_type> operator*(_type other)        { return vec3<_type>(x * other,   y * other,   z * other); }
            inline vec3<_type> operator/(_type other)        { return vec3<_type>(x / other,   y / other,   z / other); }

            inline vec3<_type> operator+(vec3<_type> &other) { return vec3<_type>(x + other.x, y + other.y, z + other.z); }
            inline vec3<_type> operator-(vec3<_type> &other) { return vec3<_type>(x - other.x, y - other.y, z - other.z); }
            inline vec3<_type> operator*(vec3<_type> &other) { return vec3<_type>(x * other.x, y * other.y, z * other.z); }
            inline vec3<_type> operator/(vec3<_type> &other) { return vec3<_type>(x / other.x, y / other.y, z / other.z); }

            inline _type operator[](int i)
            {
                if(i < 0 || i >= 3)
                    return 0;

                _gl_a[0] = x;
                _gl_a[1] = y;
                _gl_a[2] = z;
                return _gl_a[i];
            }

            inline double magnitude()
            {
                return sqrt(x * x + y * y + z * z);
            }

            inline vec3<_type> normal()
            {
                double m = this->magnitude();
                return vec3<_type>(x / m, y / m, z / m);
            }

            inline _type dot(vec3<_type> other)
            {
                return x * other.x + y * other.y + z * other.z;
            }

            inline vec3<_type> cross(vec3<_type> other)
            {
                return vec3<_type>(y * other.z - other.y * z,
                                   z * other.x - other.z * x,
                                   x * other.y - other.x * y);
            }
    };

    typedef vec3<float>             vec3f;
    typedef vec3<double>            vec3d;
    typedef vec3<int>               vec3i;
    typedef vec3<unsigned int>      vec3ui;
    typedef vec3<short>             vec3s;
    typedef vec3<unsigned short>    vec3us;
};

Is it another operator overload function I have to add, or something entirely different?


glBufferData takes a void pointer. That's exactly what you do using the code piece with glm::vec3. However you can do this different ways.

Since GLM stores its data in a union, you can access the elements of a vector in multiple ways: by operator[] or by coordinates x, y, z. Since the elements are values, not pointers, you need the & operator to dereference them. So &myvec[0] has the same effect as &myvec.x.

Your code snippet takes the pointer of the 1st glm::vec3 in the std::vector. The memory address of each glm::vec3 is the memory address of its 1st element. This way you pass a pointer to an array of floats (the elements of the vector, if they are tightly packed - in GLM they are):

std::vector<glm::vec3> locations;

glBufferData(GL_ARRAY_BUFFER, _num_vertices * sizeof(locations[0]), &locations[0], GL_STATIC_DRAW);

// same as above
//glBufferData(GL_ARRAY_BUFFER, _num_vertices * sizeof(locations[0]), &locations[0][0], GL_STATIC_DRAW);

// same as above
//glBufferData(GL_ARRAY_BUFFER, _num_vertices * sizeof(locations[0]), &locations[0].x, GL_STATIC_DRAW);


I recommend you to become familiar with the concept of pointers, references and unions in C++, since the following part of my answer rely on them.


Your math::vec3 implementation has multiple issues.

1) As others already stated in the comments, you need both

_type operator[](int i);
_type operator[](int i) const;

to support const-ness.

2) Return a reference (_type&) instead of a value (_type). Currently, you return a value by _type operator[](int i);, thus your elements are read-only. Using references you can both read and write them.

_type& operator[](int i);
_type& operator[](int i) const;

3) Since you cannot have negative indices, you shouldn't use signed int for the indexing. Use an unsigned type: unsigned int does the job, but size_t is even better.

&_type operator[](size_t i);
&_type operator[](size_t i) const;

4) Testing against if (i < 0 || i >= 3) is not necessary. It just makes your element access a LOT slower in critical loops (when you access the elements many times). Using size_t you cannot have a value less than 0, and in a correct code you should never pass an index higher than the actual size of the vector.

5) You store your data twice: once in _gl_a[3] and once in x, y, z. This is a huge waste of memory. Instead, you should use a union to access the same data in multiple ways.

union // anonymous union
{
   _gl_a[3];
   struct { x, y, z, }; // anonymous struct
};

Once you have a correct implementation of your math::vec3, you will be able to use it in the same way as glm types.