48

I've written a model loader in C++ an OpenGL. I've used std::vectors to store my vertex data, but now I want to pass it to glBufferData(), however the data types are wildly different. I want to know if there's a way to convert between std::vector to the documented const GLvoid * for glBufferData().

Vertex type

typedef struct
{
    float x, y, z;
    float nx, ny, nz;
    float u, v;
}
Vertex;

vector<Vertex> vertices;

glBufferData() call

glBufferData(GL_ARRAY_BUFFER, vertices.size() * 3 * sizeof(float), vertices, GL_STATIC_DRAW);

I get the following (expected) error:

error: cannot convert ‘std::vector<Vertex>’ to ‘const GLvoid*’ in argument passing

How can I convert the vector to a type compatible with glBufferData()?

NB. I don't care about correct memory allocation at the moment; vertices.size() * 3 * sizeof(float) will most likely segfault, but I want to solve the type error first.

Bojangles
  • 99,427
  • 50
  • 170
  • 208
  • @john Edited \*cheesy, sheepish smile\*. – Bojangles Aug 24 '11 at 10:01
  • 1
    The main issue is whether your Vertex struct is laid out in the way that OpenGL expects. I have no idea about that, but assuming it is, then all I think you need to do is replace `vertices` with `&vectices[0]` in your call to glBuggerData. – john Aug 24 '11 at 10:06
  • @john I edited glBuggerData out! Anyway, I've made my `Vertex` struct in a very similar way to other examples I've seen, so I'm assuming OpenGL is happy with it. – Bojangles Aug 24 '11 at 10:08
  • @JamWaffles: Don't forget to fix that `3 * sizeof(float)` problem as Marcelo pointed out. You're not passing an array where each entry is 3 floats; you're passing an array where each entry is a `Vertex` object. That's much bigger than 3 floats. – Nicol Bolas Aug 24 '11 at 10:19

2 Answers2

62

If you have a std::vector<T> v, you may obtain a T* pointing to the start of the contiguous data (which is what OpenGL is after) with the expression &v[0].


In your case, this means passing a Vertex* to glBufferData:

glBufferData(
   GL_ARRAY_BUFFER,
   vertices.size() * sizeof(Vertex),
   &vertices[0],
   GL_STATIC_DRAW
);

Or like this, which is the same:

glBufferData(
   GL_ARRAY_BUFFER,
   vertices.size() * sizeof(Vertex),
   &vertices.front(),
   GL_STATIC_DRAW
);

You can rely on implicit conversion from Vertex* to void const* here; that should not pose a problem.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    Thanks very much for the explanation Tomalak. As @Marcelo has suggested, I'll be using `&vertices.front()`, unless there's a good reason I shouldn't be? – Bojangles Aug 24 '11 at 10:07
  • @JamWaffles: No reason at all. – Lightness Races in Orbit Aug 24 '11 at 10:08
  • 3
    This will work with vectors because vector data-storage is a C array under the hood. This will not work for many other STL containers (like lists sets and maps) where the data is not stored contiguously – doron Aug 24 '11 at 10:25
  • 2
    @doron: Whether `vector` storage is implemented with arrays is completely unspecified. What is important is that the elements are guaranteed to be stored _contiguously_. This is pretty much the same thing, but there _is_ a subtle difference in meaning there. (FYI, _no_ other standard container guarantees contiguous element storage, besides `std::array` of coure) – Lightness Races in Orbit Aug 24 '11 at 10:50
  • Why `sizeof(std::vector::value_type)`, why not `sizeof(Vertex)`? – Andreas Brinck Aug 24 '11 at 10:58
  • @Andreas: I think I started with `vertices::value_type`, realised my mistake, and expanded it rather than contracting it. :) – Lightness Races in Orbit Aug 24 '11 at 11:14
  • You can also use `vertices.data()` which returns a pointer directly. (&vertices[0] might actually fail more cleanly in the case of an accidentally empty vector though). – user673679 Dec 16 '14 at 17:35
  • @user673679: How so? `&v[0]`, `v.data()` and `&v.front()` are all 100% equivalent. Well, except that `v.data()` in itself is well-defined when `v` is empty, whereas the other two are not. – Lightness Races in Orbit Dec 16 '14 at 20:28
  • @LightnessRacesinOrbit well, I had an issue at one point where v.data() resulted in a crash, and &v[0] triggered an assertion about the index being out of range (IIRC), which made it easier to track down the bug. Entirely an implementation dependent thing though I guess. – user673679 Dec 16 '14 at 20:52
  • 1
    @user673679: Yes it is - that would have been debug mode range checking which the standard does not require of `&v[0]` and which you should never see in a release build. Though if `v.data()` "resulted in a crash" then you have a horribly broken stdlib implementation; it should have only crashed once you'd dereferenced it! The standard makes it explicitly clear that `v.data()` is a valid (but potentially undereferenceable) pointer, much like "end" iterators. – Lightness Races in Orbit Dec 16 '14 at 21:27
33

This should do the trick:

&vertices[0]

Some prefer &vertices.front(), but that's more typing and I'm bone lazy.

To be even lazier, you could overload glBufferData thus:

template <class T>
inline void glBufferData(GLenum target, const vector<T>& v, GLenum usage) {
    glBufferData(target, v.size() * sizeof(T), &v[0], usage);
}

Then you can write:

glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);

and also avoid bugs (your struct is bigger than 3 * sizeof(float)).

Marcelo Cantos
  • 181,030
  • 38
  • 327
  • 365
  • 2
    Thanks Marcelo. `&vertices.front()` is perfect. I was confused by the fact that you don't pass all the data to the function, only a pointer to the first element. All fixed. – Bojangles Aug 24 '11 at 10:07