CygnusX1
CygnusX1

Reputation: 21778

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:

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?

Upvotes: 1

Views: 568

Answers (1)

violet313
violet313

Reputation: 1932

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.

Upvotes: 1

Related Questions