Use of VBO's with frame animation MD2's/MD3's (etc) = benefits?

Jones
Unregistered
 
Post: #1
Would the use the use of VBO's really benefit rendering frame based animation models, such as MD2's and MD3's?

I'm asking this because I've just started writing a class to facilitate the use of VBO's and I've realized how complicated it could be become. (Using VBO's with MD2s and MD3s.)

A worst case MD2 with the maximum number of vertices and frames would chew up 25 megabytes of RAM (a more average one might take up about 7 - 10) when completely unpacked and calculated. If not pre-calculated then you wouldn't really be benefitting from VBO's, it would be like using vertex arrays to re-submit the data to the video card.

I currently use for loops with glVertex3f (and co.) to draw my models. And looking at the mess of indices and vertices I have now with that system I can't help but fear the task of writing code to organize all of that into something OpenGL might be able to use. It would require a lot of looping on load to calculate all the (interpolated) frames not stored in the model. Personally, I hate long loading times in games. It's annoying. Annoyed

It seems silly to do all that, somehow. It occurs to me that perhaps I'm not using VBO's correctly. Would they be more efficient for rendering large static meshes like mountains and levels? How much of a hit would a program take for using immediate drawing of models over VBO assisted rendering? I can display 20 of my test MD2's using normal rendering and get very fast animation. I could probably do more models, I just havn't tried.

Thanks very much for your time and opinions,
Jones.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #2
As always, the only real answer is profile-and-see...

You have several options:
1) all the geometry for all the frames of each model in one static VBO
2) one static VBO per frame
3) one dynamic VBO per object, and upload the vertices you need each frame
4) ... and probably more Wink

My instinct says that 1) or 2) are your best options, but I wouldn't like to guess which is actually faster. Profile and see Rasp
Quote this message in a reply
Jones
Unregistered
 
Post: #3
I'll try number 2, and see. Is there max number of VBO's? If there is then that might cause trouble.
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #4
Unless you have billions of VBOs, you shouldn't have a problem. However, you may run into a limitation of VRAM, so be careful.
Quote this message in a reply
Jones
Unregistered
 
Post: #5
I think I've found an error, in the OpenGL man pages...

pyOpenGL and the Official Apple developer documentation states that in the function:

Code:
glDrawRangeElements( GLenum mode,
                                 GLuint start,
                                 GLuint end,
                                 GLsizei count,
                                 GLenum type,
                                 const GLvoid *indices )

Quote:type Specifies the type of the values in count. Must be one of
GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or GL_UNSIGNED_INT.

'type' actually specifies the type of data in *indices, not count. I got rather confused for a moment there, but I'm pretty sure it must be an error in the specs.

Weird.
Quote this message in a reply
Member
Posts: 87
Joined: 2006.08
Post: #6
Definitly do not use 1 VBO per model. Most GL implementations will load an entire resource (buffer object, texture, etc), as a single unit. If you pack every frame into a single buffer object, you're burning a lot of VRAM to store every frame, when you need to access only one.

The situation for textures is similar. This is why I cringe when someone reports that they are using a 3D texture to do an animated surface, rather than a large collection of 2D textures.

1) If you think the memory usage is reasonable considering any other constraints your app has, use one STATIC_DRAW VBO per frame.
2) If the memory usage is excessive, and you have CPU time to spare, use a STREAM_DRAW VBO and generate vertex data on the fly.
3) Best of both worlds: Do the interpolation in a vertex shader if at all possible.
Quote this message in a reply
Jones
Unregistered
 
Post: #7
Frogblast Wrote:Definitly do not use 1 VBO per model. Most GL implementations will load an entire resource (buffer object, texture, etc), as a single unit. If you pack every frame into a single buffer object, you're burning a lot of VRAM to store every frame, when you need to access only one.

The situation for textures is similar. This is why I cringe when someone reports that they are using a 3D texture to do an animated surface, rather than a large collection of 2D textures.

1) If you think the memory usage is reasonable considering any other constraints your app has, use one STATIC_DRAW VBO per frame.
2) If the memory usage is excessive, and you have CPU time to spare, use a STREAM_DRAW VBO and generate vertex data on the fly.
3) Best of both worlds: Do the interpolation in a vertex shader if at all possible.

Thanks for these extra options. I haven't even begun getting into shaders yet. Rolleyes So I think I'll pass on that choice for now. If I'm going to be streaming to VBO's, would the advantage be the same as passing vertex arrays every display loop?

What exactly is the matter with animated surfaces using 3D textures? If you've only got 2 or 3 frames in can't be *that* bad right?

Thanks!
Quote this message in a reply
Member
Posts: 87
Joined: 2006.08
Post: #8
Jones Wrote:Thanks for these extra options. I haven't even begun getting into shaders yet. Rolleyes So I think I'll pass on that choice for now. If I'm going to be streaming to VBO's, would the advantage be the same as passing vertex arrays every display loop?

There are several large advantages of VBO vs. normal vertex arrays:
1) Removing unnecessary copies of the vertex data. With normal vertex arrays, you generate the data into application memory, and that data may be copied by the GL software into the HW command buffer, to be read by the HW (write+read+write+read). With streaming VBOs, you should generate your data directly into a mapped buffer object, which the HW can then read directly (write+read). See the Apple OpenGL Programming Guide for an example on that. Furthemore, since the CPU is no longer copying all of your vertex data, it can result in significantly reduced CPU usage (even if your actual throughput doesn't increase much).
2) The VBO case will perform even better if you choose activate GL Multithreading for your context.

Jones Wrote:What exactly is the matter with animated surfaces using 3D textures? If you've only got 2 or 3 frames in can't be *that* bad right?
Thanks!

If you only have 2 frames, then the lost granularity in memory management is not a very large deal, but it still exists. Furthermore, you can't have 3 frames unless ARB_texture_non_power_of_two is exported. Also, using 3D textures will prevent you from using mipmaps, since your depth dimension will need to be reduced as well. Using a sequence of 2D textures has no such disadvantages.
Quote this message in a reply
Jones
Unregistered
 
Post: #9
Frogblast Wrote:If you only have 2 frames, then the lost granularity in memory management is not a very large deal, but it still exists. Furthermore, you can't have 3 frames unless ARB_texture_non_power_of_two is exported. Also, using 3D textures will prevent you from using mipmaps, since your depth dimension will need to be reduced as well. Using a sequence of 2D textures has no such disadvantages.

Excellent points, indeed. I shall try to remember them. Smile I've been puzzled by the 3D power of two depth values before. I remember wondering why it didn't work for quite some time. Rasp
Quote this message in a reply
Jones
Unregistered
 
Post: #10
Well, I just compiled and tested the first version of my 'ImportMD2()' function for turning MD2's into VBO's.

Results:
Nothing is displayed. (MD2 should've been draw in immediate mode first.)
I press cmd-q.
Millions of these type of errors poor from the console:

Code:
OpenGL(22007,0xa000ed88) malloc: ***  Deallocation of a pointer not malloced: 0x74686d65; This could be a double free(), or free() called with the middle of an allocated block; Try setting environment variable Ma...

Debugger *finally* kicks into action and tells me that xCode is automatically trying to delete my MD2 class on exit. (I didn't bother to write the Unload() function in after main.) Pointers of data that existed *before* the model was imported no longer exist. Weird. The import function doesn't touch the pointer addresses directly...

Meh, i'll fix it. Just a little progress update.
Quote this message in a reply
Jones
Unregistered
 
Post: #11
I must once again respectfully request your insight(s).

OpenGL is crashing when I tell it to render my MD2 VBO's. GDB shows this list of events:

Code:
#0    0xffff89ac in objc_msgSend_rtp
#1    0x006fa83c in gldPageoffBuffer
#2    0x005e4ff8 in gleExecuteVertexArrayRange
#3    0x00647d88 in gleDrawArraysOrElements_VBO_Exec
#4    0x0003e950 in VBO::DrawFrame at VBO_s.cpp:114

And then GDB tells me 'Unable to disassemble objc_msgSend_rtp.' Not that seeing the asm of that function would help. Rasp

Anyways, I dropped 'gleDrawArraysOrElements_VBO_Exec' into google and got a couple of threads on the Apple forums about VBO's being broken. I also got a thread on this forum about the subject. I don't use any libraries like GLEW to manage my extensions, but so far I only use two. the glDrawRangeElementsEXT and ARB buffer object extensions. I should admit that this is my first experience with extensions at all. Rolleyes

But back to the google results, if you search what I did you'll find there are several items about Mac OS X having trouble for this reason. (Drop any item in that list in and you get results...) Now is this problem (which I've been scratching my head about for a while) something widespread which I should just work around, or is it my fault? I can post the source (it's only about... 150 lines, in this case) that causes trouble, if you'd like to take a peek. Ninja

But first, off the top of your heads, do you know of any silly mistake I could be making that would cause that error? (Silly as in I still frequently forget to initialise FreeImage, and then wonder why nothing is working. Rasp)

Thanks!
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #12
I haven't had any problems with VBOs for a long time now. If you're on a recent 10.4.x, it's almost certainly your fault. Check your parameters to gl*Pointer, glBuffer*Data and glDraw*Elements very carefully.
Quote this message in a reply
Member
Posts: 87
Joined: 2006.08
Post: #13
Can you please post the example sources, and the hardware/software you are using?
Quote this message in a reply
Jones
Unregistered
 
Post: #14
Sure then, here is the relevant VBO class code. As far as I can tell, everything here is correct and should work fine.

If your not sure how an MD2 works, here's a brief explanation:

1) Each frame of the model holds vertex lists with coordinates.
2) Texture coordinates are stored once.
3) One list of triangles contains pointers to vertices (grabbed by frame) and the texture coordinates.

Code:
bool VBO::ImportMD2(MD2 model) {

    if (!model.modelLoaded) {
        return(FALSE);
    }
    
    int numFrames = model.header->num_Frames;
    numVBO = (numFrames * 2) + 3;
    
    vboID = (GLuint*)malloc(sizeof(GLuint) * (numVBO + 1));
    glGenBuffersARB(numVBO, vboID);
    
    int numVerts = model.header->num_Vertices;
    
    float *verts = (float*)malloc(sizeof(float) * numVerts * 3);
    float *norms = (float*)malloc(sizeof(float) * numVerts * 3);
        
    for (int f = 0; f < numFrames; f++) {
        
        for (int v = 0; v < numVerts; v++) {
            
            for (int s = 0; s < 3; s++) {
            
                verts[(v * 3) + s] = ((float)model.frames[f].vertices[v].v[s]
                                   * model.frames[f].scale[s])
                                   + model.frames[f].translate[s];
                norms[(v * 3) + s] = model.normals[(f * numVerts) + v][s];
            }
            
        }
        
        glBindBufferARB(    GL_ARRAY_BUFFER_ARB, vboID[f * 2]    );
        glBufferDataARB(    GL_ARRAY_BUFFER_ARB, numVerts * 3 * sizeof(float),
                            verts, GL_STATIC_DRAW_ARB    );
        
        glBindBufferARB(    GL_ARRAY_BUFFER_ARB, vboID[(f * 2) + 1]    );
        glBufferDataARB(    GL_ARRAY_BUFFER_ARB, numVerts * 3 * sizeof(float),
                            norms, GL_STATIC_DRAW_ARB    );
                            
    }
    
    int numTexCrds = model.header->num_TexCoords;
    
    float *texCrds = (float*)malloc(sizeof(float) * numTexCrds * 2);
    
    for (int t = 0; t < numTexCrds; t++) {
    
        texCrds[(t * 2)] =    (float)model.texpos[t].s
                            / model.header->skinWidth;
        texCrds[(t * 2) + 1] =    (float)model.texpos[t].t
                                / model.header->skinHeight;
    }
    
    glBindBufferARB(    GL_ARRAY_BUFFER_ARB, vboID[numVBO - 2]    );
    glBufferDataARB(    GL_ARRAY_BUFFER_ARB, numTexCrds * 2 * sizeof(float),
                        texCrds, GL_STATIC_DRAW_ARB    );
                        
    numIndices = model.header->num_Triangles * 3;
    
    int *texInds = (int*)malloc(sizeof(int) * numIndices);
    int *verInds = (int*)malloc(sizeof(int) * numIndices);
    
    for (int i = 0; i < numIndices / 3; i++) {
        
        for (int u = 0; u < 3; u++) {
        
            texInds[(i * 3) + u] = (int)model.triangles[i].texture[u];// * 2;
            verInds[(i * 3) + u] = (int)model.triangles[i].vertex[u];// * 3;
        
        }
        
    }

    //glBindBufferARB(    GL_ELEMENT_ARRAY_BUFFER_ARB, vboID[numVBO - 1]    );
    //glBufferDataARB(    GL_ELEMENT_ARRAY_BUFFER_ARB, numIndices * sizeof(int),
    //                    texInds, GL_STATIC_DRAW_ARB    );
                        
    glBindBufferARB(    GL_ELEMENT_ARRAY_BUFFER_ARB, vboID[numVBO]    );
    glBufferDataARB(    GL_ELEMENT_ARRAY_BUFFER_ARB, numIndices * sizeof(int),
                        verInds, GL_STATIC_DRAW_ARB );
                        
    free(verts);
    free(norms);
    free(texCrds);
    free(texInds);
    free(verInds);
}

void VBO::DrawFrame(int frame) {

    glBindBufferARB(    GL_ARRAY_BUFFER_ARB, vboID[frame]    );
    glVertexPointer(    3, GL_FLOAT, 0, (GLvoid*)((char*)NULL)    );
    
    glBindBufferARB(    GL_ARRAY_BUFFER_ARB, vboID[frame + 1]    );
    glNormalPointer(    GL_FLOAT, 0, (GLvoid*)((char*)NULL)    );
    
    glBindBufferARB(    GL_ARRAY_BUFFER_ARB, vboID[numVBO - 2]    );
    glTexCoordPointer(    2, GL_FLOAT, 0, (GLvoid*)((char*)NULL)    );
    
    glBindBufferARB(    GL_ELEMENT_ARRAY_BUFFER, vboID[numVBO]    );
    glDrawRangeElementsEXT(        GL_TRIANGLES, 0, numIndices,
                                numIndices, GL_UNSIGNED_INT,
                                (GLvoid*)((char*)NULL)    );
    
    //glDrawElements(    GL_TRIANGLES,
    //                numIndices,
    //                GL_UNSIGNED_INT,
    //                (GLvoid*)((char*)NULL)    );
}

Thanks for taking a look, and I hope you spot the issue!

Jones Smile
Quote this message in a reply
Jones
Unregistered
 
Post: #15
Well, it's fixed. I'm not gonna tell you how because if I did it would make me look really stupid. Rasp

Anyways, I've just come to realize that you can't bind two buffers to GL_ELEMENT_ARRAY_BUFFER_ARB (one verts, one tex-coords) and expect it to work. Rasp

EDIT:

And now, the full explanation...

MD2's vertex coordinates do not necessarily line up with texture coordinates. Frequently, there are *more* texture coordinates that vertices. This requires two sets of indices. The thing is, you can only have one set of indices bound to the ELEMENT_ARRAY buffer at one time. This is sort of a roadblock. :/

I think the solution is to 'reverse engineer' the texture coordinates, to find the ones necessary and then shift them around until they line up with the vertices.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Unbinding VBO's Bersaelor 2 4,571 Aug 2, 2010 08:29 AM
Last Post: Bersaelor
  N-Body with GLSL and VBO's (how to approach)? mcMike 4 6,001 May 24, 2008 11:06 AM
Last Post: mcMike
  help about VBO's bonanza 5 3,696 Sep 6, 2007 10:57 PM
Last Post: bonanza