The standard solution is to reduce the amount of per-vertex data by only specifying the 4 indices that have the highest weights (and rescaling the weights to add up to 1.0). That way, you can pass 4 weights as a vec4
and 4 indices as a uvec4
. Also, you should pass the weights as normalized shorts (or bytes if you can accept the quality loss) and indices as bytes if at all possible, to further reduce the per-vertex data storage.
Most real-time rendering systems are happy with these limitations.
If that's not acceptable for your needs, then you're going to have to use multiple attributes to send the data. Each attribute can only have 4 components. And while attributes can be arrayed, arraying them like your C struct would waste a bunch of attribute locations, since each array element is considered a separate location.
So you need to send values in multiples of 4. Instead of sending 10 weights/indices, send 12, grouped into 4-element groupings. So the GLSL would look like this:
layout(location = X) uvec4 indices[3]; //Stores 12 indices, consumes 3 locations.
layout(location = X + 3) vec4 weights[3]; //Stores 12 weights, consumes 3 locations.
The C data structure ought to also store 12 elements for each array.
To specify them in the vertex format, you would do this (where X
is the location given above):
glVertexAttribIPointer(3, 4, GL_INT, sizeof(Vertex), (void*)(offsetof(Vertex, groups) + 0));
glVertexAttribIPointer(4, 4, GL_INT, sizeof(Vertex), (void*)(offsetof(Vertex, groups) + 16));
glVertexAttribIPointer(5, 4, GL_INT, sizeof(Vertex), (void*)(offsetof(Vertex, groups) + 32));
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(offsetof(Vertex, weights) + 0));
glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(offsetof(Vertex, weights) + 16));
glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(offsetof(Vertex, weights) + 32));