简体   繁体   中英

Get member offset in a template for glVertexAttribPointer

Assume that I have my complete vertex data given in a struct Vertex in an array. In order to send the data to the vertex shader, I need to call the glVertexAttribPointer to set the correct mapping between Vertex members and the parameters of the shader.

I am trying to encapsulate an OpenGL function glVertexAttribPointer in a nice templated way.

glVertexAttribPointer serves as a kind of type description for a data upload from CPU to GPU, taking the following arguments as per specification :

  • GLUint index - a handle
  • GLint size - number of components (irrelevant for this problem)
  • GLenum type - an enum value represeting a type ( GL_FLOAT , GL_SHORT , etc...)
  • GLboolean normalized - normalization to [0..1] or [-1..1] (irrelevant for this problem)
  • GLsizei stride - byte difference between consecutive vertex elements. In my case that is the size of Vertex .
  • const GLvoid* pointer - offset for the first element in relation to the beginning of the data. In my case, that will be the offset of the member in relation to the beginning of the Vertex .

To achieve my goal I have come up with the following solution:

template <typename WholeVertex, typename Element>
void attribute(GLuint shaderProgram, const char* name, Element WholeVertex::* memberPointer) {
    GLint handle = glGetAttribLocation(shaderProgram, name);
    glVertexAttribPointer (
        handle,
        opengl_type<Element>::arity,
        opengl_type<Element>::type,
        GL_FALSE,
        sizeof(WholeVertex),
        (const GLvoid*)(memberPointer) //error
    );
}

where opengl_type is a my own trait class computing the correct GLenum value for a given type.

Unfortunately, this solution assumes that a member pointer is merely an integer offset and can be casted to (const GLvoid*) . To my knowledge, with an assumption that WholeVertex has a standard layout that is true. Still, in generic case it may not be true and the compiler prohibits me to perfom this kind of cast. In fact, I cannot do anything with the memberPointer without having an object.

I found out that there is a offsetof function-like macro which could compute what I need, but the problem is it takes the member name as an argument, which makes it unusuable if the name is unknown.

So, do you have any other idea how this wrapper function could be implemented? Maybe a different way of computing the offset? Or passing it as a different kind of parameter?

It's not easy to get away from the constness of the member pointer.

You won't like this hack very much but it works:

template <typename WholeVertex, typename Element>
void attribute(GLuint shaderProgram, const char* name, Element WholeVertex::* memberPointer) 
{
    GLint handle = glGetAttribLocation(shaderProgram, name);

    GLvoid *offset=0; 
    memcpy(&offset, &memberPointer, sizeof(GLvoid*));

    printf("ooook:%p\n",offset);

    glVertexAttribPointer (
        handle,
        opengl_type<Element>::arity,
        opengl_type<Element>::type,
        GL_FALSE,
        sizeof(WholeVertex),
        offset
    );
}


Perhaps a more preferable way using a union cast hack (still non compliant so maybe not to your liking either):

template <typename WholeVertex, typename Element>
void attribute(GLuint shaderProgram, const char* name, Element WholeVertex::* memberPointer) 
{
    GLint handle = glGetAttribLocation(shaderProgram, name);

    union
    {
      Element WholeVertex::* x;
      GLvoid* y;
    }
    offset;

    offset.x=memberPointer;

    glVertexAttribPointer (
        handle,
        opengl_type<Element>::arity,
        opengl_type<Element>::type,
        GL_FALSE,
        sizeof(WholeVertex),
        offset.y
    );
}


&Finally, by de-referencing a pointer to null ( but still a hack :/ ):

GLvoid* offset=reinterpret_cast<GLvoid*>(&((static_cast<WholeVertex*>(NULL))->*memberPointer));


In defence of these hacks:

  1. my understanding is that for a POD object, the current C++ standard ensures the value of memberPointer will be a simple offset calculation.

  2. glVertexAttribPointer is an API with hideous legacy of once upon a time trying to mainatain backwards compatability. non-compliant means are acceptable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM