fordcars - 6 months ago 21

C++ Question

I was using an OpenGL buffer with a bunch of GLfloats as a vertex buffer and all was well. The format of the GLfloats being

`[x1, y1, z1, x2, y2, z2, ...]`

But then, while following this tutorial, it tells me to use

`glm::vec3`

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

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

Now this code is valid, and I wonder how would OpenGL know how to fill in the buffer with glm::vec3 instead of GLfloats. Then I wonder, when I read the data back from the buffer, using:

`std::vector<glm::vec3> data;`

glGetBufferSubData(mTarget, offset, vertexCount * sizeof(glm::vec3), &data[0]);`

Will this make a bunch of glm::vec3? So the question is,

`glm::vec3`

Answer

According to OpenGL's documentation, `glBufferData()`

needs a pointer to the `data`

(i.e. an **array**, i.e. the coordinates of the vertices).

Let's first have a look at `glm::vec3`

's implementation.

If you check out glm's Github repo, you'll see that, *depending on your compilation flags*, `glm::vec3`

is a `typedef`

of `highp_vec3`

which is a `typedef`

of `tvec3<float, highp>`

.

`tvec3`

is declared in type_vec3.hpp (included by vec3.hpp) and the class (template) methods are defined in type_vec3.inl.

In particular, `operator[]`

's definition is:

```
template <typename T, precision P>
GLM_FUNC_QUALIFIER T & tvec3<T, P>::operator[](typename tvec3<T, P>::length_type i)
{
assert(i >= 0 && static_cast<detail::component_count_t>(i) < detail::component_count(*this));
return (&x)[i];
}
```

Given that piece of code, one would assume that `x`

is the first element of the "array" containing the coordinates of `glm::vec3`

. However, when we go back to type_vec3.h, we find:

```
union { T x, r, s; };
union { T y, g, t; };
union { T z, b, p; };
```

So `x`

, `y`

and `z`

are **separate attributes**. But thanks to how class/struct members are laid out, they can be viewed as a **single array starting from &x**.

We know now, that `glm::vec3`

(actually `tvec3`

) stores the coordinates in a contiguous manner. But does it also store other attributes ?

Well, we can continue to dive into the code, or use a simple program to give us the answer:

```
#include <iostream>
#include <ios>
#include <glm/vec3.hpp>
int main()
{
const glm::vec3 v;
const size_t sizeof_v = sizeof(v);
const size_t sizeof_xyz = sizeof(v.x) + sizeof(v.y) + sizeof(v.z);
std::cout << "sizeof(v) : " << sizeof_v << std::endl;
std::cout << "sizeof(xyz): " << sizeof_xyz << std::endl;
std::cout << "sizeof(v) == sizeof(xyz) : " << std::boolalpha << (sizeof_v == sizeof_xyz) << std::endl;
}
```

Which prints, on my machine:

```
sizeof(v) : 12
sizeof(xyz): 12
sizeof(v) == sizeof(xyz) : true
```

Therefore, `glm::vec3`

stores *only* the `(x, y, z)`

coordinates.

Now, if we create a `std::vector<glm::vec3> vertices;`

, it is safe to say that the layout of the data pointed by `&vertices[0]`

(which, in C++11 is `vertices.data()`

) is:

```
vertices == [vertice1 vertice2 ...]
== [vertice1.x vertice1.y vertice1.z vertice2.x vertice2.y vertice2.z ...]
```

Going back to the original issue -- `glBufferData()`

's requirements: when you pass `&vertices[0]`

you are actually passing the address (i.e. pointer) of the `data`

, just as expected by `glBufferData()`

. The same logic applies to `glGetBufferSubData()`

.