# 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 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 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 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 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 NameglGenBuffersARB( 1, &uiVertexDataID);					// Bind The Buffer		glBindBufferARB( GL_ARRAY_BUFFER_ARB, uiVertexDataID );			// Load The DataglBufferDataARB( 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 on other sites
Lord_Evil    680
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 on other sites
BlackWind    212
wow!, thanks for that explanation, but i still have more questions hehe...

Quote:
 Original post by Lord_EvilCreation and updatesNo, 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 accessYou 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 NameglGenBuffersARB( 1, &uiVertexDataID);					// Bind The Buffer		glBindBufferARB( GL_ARRAY_BUFFER_ARB, uiVertexDataID );

and then, when my vertex data changes, i will call this:
// Load The DataglBufferDataARB( 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 pointerglBufferDataARB( 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 dataglBufferDataARB( 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_EvilThe offset value also could be different. Let's assume the vertex layout istexcoord (2 floats)normal (3 floats)position (3 floats)In interleaved VBOs the offsets would be:texcoordoffset = 0normaloffset = 2vertexoffset = 5

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

thanks a lot.....

##### 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 on other sites
BlackWind    212

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?

BlackWind    212
nobody?

##### 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 on other sites
BlackWind    212
so...

mapping is a good idea?

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

thanks a lot....

##### 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 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 on other sites
songho    268
Quote:
 Original post by BlackWindso...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 on other sites
BlackWind    212
thank you guys,
i'm gonna try, and if i have more questions... i'll come back hehe.....

[Edited by - BlackWind on March 27, 2007 2:33:27 PM]

## Create an account

Register a new account