Search code examples
c++openglstructvertex

How do I use a mixed type c++ struct and only pass some values to OpenGL?


I have read high and low and thought I understood C++ and OpenGL vertex data layouts, but I must be wrong somewhere...

I have a struct to create a Line object. Therefore it has two points (each of 3 floats to represent a vector position). It must also have an object ID to allow me to track the specific object on creation for collisions, etc later on in the application. The struct is shown below.

struct Point
{
    Vector position = { 0.0f, 0.0f, 0.0f };
};

struct Line
{
    Point B = { 0.0f,0.0f,0.0f };
    Point C = { 0.0f,0.0f,0.0f };
    int ID = 0;
};

I then create a simpe c++ STL vector of Lines and push back two line objects:

vector<Line> lines; 

Line w0;
    w0.B = { 2.0f,2.0f, 0.0f };
    w0.C = { 8.0f,2.0f, 0.0f }; 
    w0.ID = 0; 
    lines.push_back(w0);

Line w1;
    w1.B = { 10.0f,4.0f, 0.0f };
    w1.C = { 18.0f,4.0f, 0.0f };
    w1.ID = 1;
    lines.push_back(w1);

Further on I specify the glVertexAttribPointer as follows:

glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 0, (void*)(0));
glEnableVertexAttribArray(4);

This gives draws only 1 line and not the two I create objects for(!). If I remove the ID int variable from my struct I get both lines showing correctly. It appeared later that I may not have specified the glVertexAttribPointer correctly, so I changed it logically as follows:

glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Line), (void*)offsetof(Line, B));

It then drew only 1 line at completely different coordinates! Different combinations of offsets etc. didn't help. Can I ultimately use an int value, different from the rest of the struct and pass only the floats over to OpenGL? I really need the ID of the object and use it later in the application. There must be a way - I must be missing something..please help.


Solution

  • Just because you need to represent lines in a certain way inside your application doesn't mean that you have to feed exactly that data in exactly that way to OpenGL for drawing. OpenGL doesn't need this ID field. There seems to be no reason to upload this ID data to the GPU. Besides that, there's no way to make OpenGL vertex attribute arrays use a memory layout like the one you have with your array of Line structs. Think about what multiple Lines look like in memory:

    B1 C1 ID1  B2 C2 ID2  B3 C3 ID3  …
    

    Note how the gap between consecutive vertex positions is not fixed but is either 0 between two points of the same line segment or sizeof(int) between the end vertex of one line and the start vertex of the next line. There is no way to describe such a vertex attribute array with just a stride and base offset. And all of this is ignoring the fact that compilers are free to add padding bytes between struct members in whatever way they see fit. So your memory layout is not even guaranteed to look like that and, at least in theory, is subject to change depending on which version of which compiler you're using with which compile options.

    I would suggest to let go of the idea that the Line struct is a given and every aspect of your application must absolutely work with that exact data representation. You have to upload the data to the GPU for drawing at some point anyways. When you do, simply copy just the start and end points and skip the id. Apart from that, consider the fact that you could also just generally switch from the Array of Structures approach you have now (where you keep an array of Line structures) to a Structure of Arrays approach, i.e., have one array for all the line start points, one for all the end points, and one for the IDs. Depending on how exactly you process your data, this is often beneficial even on the CPU. Finally, there would be the option to upload the data to a Shader Storage Buffer and manually look up the vertex attributes in the vertex shader. I don't think I would recommend going that way here though…