Sign in to follow this  
BlackWind

vertex arrays and animation......

Recommended Posts

BlackWind    212
hi, in my game, i have an octree for partioning my world, and i used vertex arrays for drawing it, that is not a problem... i also have this model (.ms3d) with bones animations and textures (which is a low poly model -- less than 3000 verts)....that is a problem... hehe.. my question is.... how do i implement vertex arrays (or something like that) for my animated model? my problem is that, when the model has an animation, all the vertex data change.... so the info i send for drawing my vertex array for the first time is not valid anymore... how can i solve it? 1.-would i have to "recompile" all the *verts info everytime i have an animation, so i can send it to draw with a vertex array? if yes... do i have to delete the old vertex array? or how can "update" it? 2.- Should i have to precompute all the possibles *verts changes with animations and store them in multiple vertex arrays, so when i have an animation, i send some ID, so depending on the ID, i can know which vertex array to draw? if yes... isn't that solution crazy? i could have a million of possibilities... is there a good way of doing it? 3.- should i stay with glBegin()..glVertex3f()...glEnd() ? 4.- What should i do? (any other solution?) thanks.....

Share this post


Link to post
Share on other sites
Lord_Evil    680
For software skinning just update the vertices' positions in the array or vertex buffer, then send it down the pipeline.

IIRC if you keep using vertex arrays you have the copy in system memory and send it down the pipeline everytime you call glVertexPointer etc. so you just update the array in system memory.

Using vertex buffers (VBOs) you'd send the vertices down once and then the driver loads them from video memory. With VBOs you can update portions of the data with glBufferSubData. And if you don't interleave the vertices (i.e. you don't put one vertex after another in the array) but rather store them component-wise / serialized (store positions in a row, then normals, then texcoords etc.) you can cut down the required memory bandwidth since you now are able to update just the positions and the normals.

Share this post


Link to post
Share on other sites
BlackWind    212
so the process will be something like this:
if the vertex data changed
send the new vertices to gl***Pointers
draw them with glDrawArrays or glDrawElements...

dont i have to remove the old data? because if i dont.... it will redraw the old AND the new data.. or not??

and for the VBO's ... wouldnt that require i high performance graphics card?

and one more question.... what is IIRC?

thanks....

Share this post


Link to post
Share on other sites
CodeMunkie    805
With VBOs you don't need to remove the old data, you can just bind your existing VBO and then call glBufferData or glBufferSubData. There is a nice whitepaper that explains how to use VBOs. It also has a section of useage tips when dealing with dynamic data.

VBO is an extension, so it only requires that your video card driver supports the extension. It does not depend on the actual capabilities of your video card. The way VBO is actually implemented and the way it performs will depend on your hardware, but as long as your driver supports VBO, you can use it.

IIRC = if I remember correctly

Share this post


Link to post
Share on other sites
BlackWind    212
ok thanks, i read the nvidia whitepaper and the nehe's tutorial on VBO's....

but i still have questions...

in my problem, the animation change the vertex data, that means that everytime i change the data i will have to do this?:

// Generate And Bind The Vertex Buffer

// Get A Valid Name
glGenBuffersARB( 1, &uiVertexDataID);

// Bind The Buffer
glBindBufferARB( GL_ARRAY_BUFFER_ARB, uiVertexDataID );

// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB, numberOfVerts*3*sizeof(float), pVertexData, GL_STREAM_DRAW_ARB ); // <-- PROBLEM HERE




problem: the STREAM_DRAW must be the USAGE flag that i should use? or the dynamic? or which one?

question: Is the "generate and binding" code good enough? what should i change/add/remove?


then, every frame (in my renderscene)

glEnableClientState( GL_VERTEX_ARRAY );

glBindBufferARB( GL_ARRAY_BUFFER_ARB, uiVertexDataID );

glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL );

glDrawArrays( GL_TRIANGLES, 0, numberOfVerts );

glDisableClientState( GL_VERTEX_ARRAY );





is that a good way? what changes should i make?

i have no experience in this, so i will welcome any tips/changes to code...

Share this post


Link to post
Share on other sites
Lord_Evil    680
Creation and updates
No, don't create a new VBO on every change. Just bind with glBindBuffer and update with glBufferData or glBufferSubData (updates portions of the VBO). The buffer id is generated once and stored for future use.

Buffer data access
You have 2 options:
1. Keep a copy of the data in client memory, update it there and then send the updated part to the VBO.
2. Use the mapping instructions to directly write to the VBO. Keep in mind that you have to lock the VBO and unlock it after you're done.

Usage
You have to use streaming or dynamic usage but I don't have the docs right here to tell you what could be th best option in your case. If I remember correctly (IIRC) streaming is for data that changes (almost) every frame whereas dynamic usage changes less frequently. It may however be the case that there isn't that much a difference between them at all, since the usage parameter is a hint for the driver on where to store the VBO.

Rendering
Your render code could indeed look like what you've posted.
If you're using normals, texcoord etc. it would expand to

glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );

glBindBufferARB( GL_ARRAY_BUFFER_ARB, uiVertexDataID );

glTexCoordPointer( 2, GL_FLOAT, stride, (char *) texcoordoffset );
glNormalPointer( 3, GL_FLOAT, stride, (char *) normaloffset );
glVertexPointer( 3, GL_FLOAT, stride, (char *) vertexoffset );

glDrawArrays( GL_TRIANGLES, 0, numberOfVerts );

glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
glDisableClientState( GL_VERTEX_ARRAY );




Storage
Here stride can have two values:
For interleaved VBOs it would be stride = vertexSize
For serialized VBOs it would be stride = 0.

The offset value also could be different. Let's assume the vertex layout is

texcoord (2 floats)
normal (3 floats)
position (3 floats)

In interleaved VBOs the offsets would be:
texcoordoffset = 0
normaloffset = 2
vertexoffset = 5

This means that from the beginning of each vertex the corresponding component is found at the given offset.

For serialized VBOs the offsets could look like this:
texcoordoffset = 0
normaloffset = vertexCount * 2
vertexoffset = vertexCount * 5

Here the components are stored as non-interleaved arrays, i.e. all texcoords first, then normals, then positions.

If your positions and normals change you update only the corresponding portions via glBufferSubData (in interleaved VBOs you'd have to update the whole buffer).

Share this post


Link to post
Share on other sites
BlackWind    212
wow!, thanks for that explanation, but i still have more questions hehe...

Quote:
Original post by Lord_Evil
Creation and updates
No, don't create a new VBO on every change. Just bind with glBindBuffer and update with glBufferData or glBufferSubData (updates portions of the VBO). The buffer id is generated once and stored for future use.

Buffer data access
You have 2 options:
1. Keep a copy of the data in client memory, update it there and then send the updated part to the VBO.
2. Use the mapping instructions to directly write to the VBO. Keep in mind that you have to lock the VBO and unlock it after you're done.


so, that means that in my initialization code i shoul have this:


// Get A Valid Name
glGenBuffersARB( 1, &uiVertexDataID);

// Bind The Buffer
glBindBufferARB( GL_ARRAY_BUFFER_ARB, uiVertexDataID );



and then, when my vertex data changes, i will call this:

// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB, numberOfVerts*3*sizeof(float), pVertexData, GL_STREAM_DRAW_ARB );



is that correct?

now, in the nvidia whitepaper, i read that if want to use the mapping, i should tell that i dont want to use the old data (because the gpu may be still working with it), so i must call glBufferDataARB with a null pointer, but i'm not very sure if this would be right:

// call with a null pointer
glBufferDataARB( GL_ARRAY_BUFFER_ARB, 0, NULL, GL_STREAM_DRAW_ARB );

// map the buffer (lock)
glMapBufferARB(ARRAY_BUFFER_ARB, WRITE_ONLY_ARB) // <-- WRITE ONLY? or //READ_WRITE?

// this contains the new modify data
glBufferDataARB( GL_ARRAY_BUFFER_ARB, numberOfVerts*3*sizeof(float), pVertexData, GL_STREAM_DRAW_ARB );

// now, call the render stuff ( glEnableClientState, glVertexPointer, drawArrays..etc )
// and then:
glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);



is that correct?
i think it may not, so..... how would it be?

also, what are the benefits of mapping?

Quote:
Original post by Lord_Evil
The offset value also could be different. Let's assume the vertex layout is
texcoord (2 floats)
normal (3 floats)
position (3 floats)

In interleaved VBOs the offsets would be:
texcoordoffset = 0
normaloffset = 2
vertexoffset = 5


why will the vertexOffset will be 5 and no 3? i dont get it....

thanks a lot.....

Share this post


Link to post
Share on other sites
CodeMunkie    805
That code is correct. However, if you can afford to keep a copy of your vertex data in system memory, I would say forget using the mapping method. The problem with mapping is that if the data is in video memory or even AGP memory, the driver will have to copy it to system memory when you map it. You will then update all or part of the data. Finally, when you unmap it, the driver has to copy the data back. Now, you can't really get around having to update the GPU's copy of the data, but if you can keep your own working copy of the data you can at least avoid the GPU->CPU copy. Use glBufferData with a NULL pointer as the paper states to avoid stalling the pipeline. Then just call glBufferData or glBufferSubData to update the GPUs copy of the data.

The offset for vertexoffset is 5 because you have to jump 2 to get past the texture coords, and then jump another 3 to get past the normal.

Share this post


Link to post
Share on other sites
BlackWind    212
ok, thanks a lot for your answers...

but then, why does the mapping method exist?
if its more expensive... why would someone want to use it? in which case is it better to map the data?

Share this post


Link to post
Share on other sites
_the_phantom_    11250
The mapping function doesn't have to copy it back into system ram; it is possible to map a section of the graphics card's memory into the address space of your program so that you can update it directly, avoiding the copy.

Share this post


Link to post
Share on other sites
CodeMunkie    805
phantom, you are correct. I just reread the extension spec and it does say that the GL may be able to map the pointer into client address space. Very interesting, I don't know where I read that a copy will always occur. Of course, if it can't map to client space then the GL will have to make a copy and you would see a hit. Well, my main point anyway was that if you are just going to overwrite the entire buffer, you can just as easily call glBufferData with the new data rather than map, copy data to mapped buffer, unmap. BlackWind, in light of this "revelation", go with mapping since you already have the code there. The important thing is just to get it working. You can always switch to using only glBufferData if you are not happy with the speed and you suspect that it is the mapping.

Share this post


Link to post
Share on other sites
_the_phantom_    11250
On important point, when you first create the VBO if you don't intend on filling it with data right away you still need to call glBufferData() with a NULL address for the data in order for the driver to reserve the correct amount of memory, otherwise calls to glMapBuffer() will fail.

As for which one to use, well it really depends on how you are using and generating the data.

So, if each frame you are geneating the vertex information, putting it into a buffer and then copying that to a VBO it might well be faster to write directly to the VBO instead via a map.

However, if you have your data in static buffers then you might want to glSubBufferData() update things. Or even create a bunch of static VBOs on start up and just cycle them.

One other issue with VBOs is the idea of pipeline stalling. Basically, if you have the VBO locked for an update then the GPU can't read from it and, by the same token, if the GPU is reading then you can't lock it for writing. With this in mind often the best thing to do is to get the driver to discard the old VBO and create you some new space.

This is done by calling glBufferData() with a NULL address. This basically says 'I'm done with this buffer for now, give me some new space and discard the old when you are done with it'. At which point you are free to update/map the buffer and write to it as before without stalling things. I dare say if the buffer isn't in use all that will happen is you'll get the same buffer back again.

Note, this only really works when you regenerate the whole buffer of infomation, if you are only changing part of it then this method might not be optimal.

Share this post


Link to post
Share on other sites
songho    268
Quote:
Original post by BlackWind
so...

mapping is a good idea?

and the code i posted above is the way to do it?
or how should it be?

You do not need glBufferDataARB() call in between glMapBufferARB() and glUnmapBufferARB(). Once you map a VBO, use the returned pointer as a normal pointer to an array, and you are going to directly modify vertex data in VBO. So you don't need to call glBufferDataARB() to copy vertex data to VBO. Instead, you may have an update function between map/unmap.

It is difficult to tell which is faster in performance wise; Mapping vs BufferData, and it depends on many situations. In my experience, computing vertex data in a mapped VBO (probably in AGP) is a little slower than computing data in system memory, but mapping technique does not require copying again the updated data to the VBO. Overall, I see the similar performance results.




Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this