vertex arrays and animation......

Started by
14 comments, last by BlackWind 17 years ago
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.....
Advertisement
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.
If I was helpful, feel free to rate me up ;)If I wasn't and you feel to rate me down, please let me know why!
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....
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
"When you die, if you get a choice between going to regular heaven or pie heaven, choose pie heaven. It might be a trick, but if it's not, mmmmmmm, boy."
How to Ask Questions the Smart Way.
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...
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).
If I was helpful, feel free to rate me up ;)If I wasn't and you feel to rate me down, please let me know why!
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 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_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.....
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.
"When you die, if you get a choice between going to regular heaven or pie heaven, choose pie heaven. It might be a trick, but if it's not, mmmmmmm, boy."
How to Ask Questions the Smart Way.
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?
nobody?

This topic is closed to new replies.

Advertisement