Skip to main content
GameDev.net gamedev.net
🔒 Locked

Vertex Array problem

Started by azjerei Jan 18, 2005 at 2:29 PM 33 replies 4.6k views
Original Post
azjerei
azjerei
I am pondering on the idea of adding vertex arrays to my game engine, to see if I can get that extra speed boost I need. Though.. I see multiple things becoming unclear should I proceed and implement these: 1. I can no longer do a collision check against the rendered poly. I will probably have to do that in a separate function on a per-object basis. 2. I can no longer cull any vertices not within my frustum. 3. I can no longer apply my own lighting to faces that are within a light's distance. How would I be able to do these things? #1 is probably doable as I wrote, but the other two should be near impossible if I let a vertex array take care of the drawing for me...
Decibit
Decibit
2 can be solved just like 1. Perform culling in a separate function on a per-object basis. Precalculating a simplified convex model out of the original mesh may boost your performance.
fireside
fireside
I'm just learning about vertex arrays, but I think the array size can be whatever you decide, like, a portion of a models vertices, a portion of a terrain, etc. The faces are drawn from an indice array that points to a vertex array. I've only used one for drawing a box, so I don't know much. I would think they could be used for level of detail work, etc.

I found this powerpoint(translated to html) that helped me understand them a little better:
http://64.233.167.104/search?q=cache:nyXEGIKVixMJ:www.cs.dal.ca/~sbrooks/csci3161/lectures/files/18-PolygonalModels-x2.pdf+glDrawElements+example&hl=en
Fen
Fen
I have nearly the same problem as you... I'm also thinking about integrating vertex arrays into a engine...

But I think thak a good way would be to create data structures that will hold every information about the 3d objects ( Vertex -> Poly -> Object ) and than put all of the data into one (or several) arrays - into a Rendering List.

1. do all transformations, frustum culling, collision detection, etc using structures such as polys, object3d, etc
2. put the data into rendering list
3. call glDrawArrays or sth similar...

What do you think about that?
Se my programming blog: Code and Graphics
azjerei
azjerei
I don't know actually... I think I am better off not using either Vertex Arrays or Vertex Buffer Objects, I lose so many of the features I have defined already.

What I am considering is the use of Octrees, but last time I tried implementing those, I ran into some other form of problem, making me really think of how modern game engines really DO manage to handle large areas when I cannot handle a mid-size, mid-detail area without getting my FPS whipped down by several notches.
_the_phantom_
_the_phantom_
the problem with not using VA or VBO is that you spend all your CPU time passing vertex data to the gpu to draw, you might as well have a card for 7 or so years ago if ya gonna do that sort of thing.
Promit
Promit
Quote:
Original post by azjerei
I am pondering on the idea of adding vertex arrays to my game engine, to see if I can get that extra speed boost I need. Though.. I see multiple things becoming unclear should I proceed and implement these:

Do it.

Quote:

1. I can no longer do a collision check against the rendered poly. I will probably have to do that in a separate function on a per-object basis.

You probably shouldn't be doing a per poly check to begin with. Doing checks on a per-object basis is a lot better.

Quote:

2. I can no longer cull any vertices not within my frustum.

GL does this internally. Culling should be done per-object.

Quote:

3. I can no longer apply my own lighting to faces that are within a light's distance.

Shaders. Even a software vertex shader will run way faster than computing your lighting like that.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
azjerei
azjerei
Well.. ok.. if I was to decide to use shaders for lighting, how would I go about doing that? I'll need a shader that is supported by most cards and that is easy to get to work. So far, I have no understood anything of any of the shader tutorials I have seen. They all seem so infernally complex in contrast to everything else (well, almost everything else) in OpenGL.
zedzeek
zedzeek
azjerei dont worry about indivual polygons/vertices, with any card made in the last 5 years its quicker to send the whole model to be rendered

>>3. I can no longer apply my own lighting to faces that are within a light's distance.<<

apply lighting to the whole model
azjerei
azjerei
Applying lighting to a whole model will look very bad since I have made my own lighting system with all forms of lights, both static and dynamic.

About the culling.. are you 100% certain that OpenGL culls faces not in a view frustum automatically? If so, then I am really wondering why there is even such functions to do that "by hand"... I am a bit confused.

Doing per-object collision will not work.. I have spheroid collision, which must check against a single plane, and our landscape is not exactly flat.

I think it would be better leaving it be... or doing Octrees, or whatever... *sigh*
_the_phantom_
_the_phantom_
yes, OpenGL Culls what isnt on screen. The reason it used to be faster todo it by hand was because the CPU was doing all the work anyways, these days the hardware handles issues such as this. However, that doesnt mean you should just throw all your objects at the scene and let OGL cull them, as its still faster to cull out the objects which dont cross into the scene than to pass them in anyways.

As for your lighting system you'll have to rethink how you approch it all, you'll probably want to pre-process all the data and batch various sections together somehow or use shaders of some sort (depending on how complex your lighting model is, however the first option should probably do you as I doubt you are doing per-pixel lighting without having touched shaders yet).

Per-object collision is only the start, you have to work out if the object even hits anything else (simple sphere check) and then work down to the per-poly level as required. This isnt such as problem as you make it out to be, look at stuff like UT2K4, they use the D3D version of Vertex arrays and yet can still do proper collision detection.

Octrees arent some 'magic bullet' that'll speed things up, you'll probably get some speed up, sure as you wont have to check all the vertices to see if you can see them however you will still be hand feeding the GPU and using up all the CPU time doing it, so you'll still be wasting a lot of time and anything above a low amount of polys will crash your frame rate into the ground.

Simple put if you want the extra speed which will come from using VA/VBO instead of glvertex3f et al commands you are going to have to rethink how you do things. What you want doto isnt impossible, it just going to require some thought/redesign from your current method.

However, that said if you dont want to put the effort in and are happy wasting all your CPU and GPU time hand feeding the gfx card your data then go ahead, but dont moan because changing from one system to another doesnt work how you want it to any more.
azjerei
azjerei
I cannot really agree that OGL culls what is not on the screen.. oh well.. if ppl say it does, then is maybe does so without really doing so.

The problem is that I cannot rewrite anything at this moment. VA's and VBO's looked small enough in terms of how much to change, but then I would have to rewrite lots of things, as we have now come to see, and I cannot do that.

*sigh*

I think I'll frankly need a good structured and easy to follow engine/tutorial that explains it all to be able to understand how to do it. I cannot experiment, it takes too long.
azjerei
azjerei
Okey, stage one in implementing Vertex Arrays has been initialized. We agreed on giving it a shot, slightly halting development a little, in favour of getting a faster game.

But... I ran into trouble almost immediately. Take a look at these images...





Strange looking landscape huh? Well, that is what I get when I render the level landscape with the Vertex Array system.

Here is the code I have written for the Vertex Arrays:

Reading level file:
// Read vertice number for this object and allocate memory.} else if (!strcmp(strWord, MIS_NUM_VERTS)) {	fscanf(m_FilePointer, " %d", &pObject->numOfVerts);	pObject->pVerts = new CVector3 [pObject->numOfVerts];	pObject->pNormals = new CVector3 [pObject->numOfVerts];	// Create Vertex Arrays for vertices and vertex normals.	pObject->vertexArray = new float [pObject->numOfVerts * 3];	pObject->normalArray = new float [pObject->numOfVerts * 3];// Read texture coordinate number and allocate memory.} else if (!strcmp(strWord, MIS_NUM_TEXVERTS)) {	fscanf(m_FilePointer, " %d", &pObject->numTexVertex);	pObject->pTexVerts = new CVector2 [pObject->numTexVertex];	// Create Vertex Array for texture coordinates.	pObject->texCoordArray = new float [pObject->numTexVertex * 2];// Read object vertices.} else if (!strcmp(strWord, MIS_VERT)) {	fscanf(m_FilePointer, " %d", &indx);	fscanf(m_FilePointer, " %f %f %f",		&(pObject->pVerts[indx].x), 		&(pObject->pVerts[indx].y), 		&(pObject->pVerts[indx].z));	// Set Vertex Array data for vertices.	pObject->vertexArray[v++] = pObject->pVerts[indx].x;	pObject->vertexArray[v++] = pObject->pVerts[indx].y;	pObject->vertexArray[v++] = pObject->pVerts[indx].z;// Read object texture vertices.} else if (!strcmp(strWord, MIS_TEXVERT)) {	fscanf(m_FilePointer, " %d", &indx);	fscanf(m_FilePointer, " %f %f", 		&(pObject->pTexVerts[indx].x), &(pObject->pTexVerts[indx].y));	// Set Vertex Array data for texture coordinates.	pObject->texCoordArray[t++] = pObject->pTexVerts[indx].x;	pObject->texCoordArray[t++] = pObject->pTexVerts[indx].y;// Read object vertex normals.} else if (!strcmp(strWord, MIS_NORMAL)) {	fscanf(m_FilePointer, " %d", &indx);	fscanf(m_FilePointer, " %f %f %f",		&(pObject->pNormals[indx].x),		&(pObject->pNormals[indx].y),		&(pObject->pNormals[indx].z));	// Set Vertex Array data for vertices.	pObject->normalArray[n++] = pObject->pNormals[indx].x;	pObject->normalArray[n++] = pObject->pNormals[indx].y;	pObject->normalArray[n++] = pObject->pNormals[indx].z;


As you can see, I use a separate vertex, normal and texcoord array list for each object. I assign them a dynamic size depending on the number of vertices/texture coordinates of the objects, and then I set the values based on the read values from the file. I have checked that I assign the correct values... well.. it does it because without Vertex Arrays, the landscape draws just fine (though slower).

I store the arrays in a fashion of

vert0.x, vert0.y, vert0.z, vert1.x, vert1.y, vert1.z ...

and so on.. as you can see in the code. Maybe I should store them in another order??

Here is the rendering code:

void RenderLandscape(){	bool useDetailTexture = false;	// Activate the necessary Vertex Array properties.	glEnable(GL_VERTEX_ARRAY);	glEnable(GL_TEXTURE_COORD_ARRAY);	glEnable(GL_NORMAL_ARRAY);	for (int i = 0; i < g_3DModel.numOfObjects; i++)	{		// Draw only ground objects.		if (bItemList != false)			continue;		// Make sure we have valid objects just in case. (size() is in the vector class).		if(g_3DModel.pObject.size() <= 0) break;		// Get the current object that we are displaying.		t3DObject *pObject = &g_3DModel.pObject;		// Check visibility based on distance from object to player.		if (!ObjectDistanceCalc(player.vCharMove, pObject))			continue;		ActivateDetailTexturing();					// Does the material applied to this object have a detail texture?		if (g_TextureDetail[pObject->materialID] < 666) {			useDetailTexture = true;		} else {			useDetailTexture = false;		}		// Set up the detail texturing parameters.		if (useDetailTexture) {			SetupDetailTexturing(g_Texture[pObject->materialID],								 g_TextureDetail[pObject->materialID]);		} else {			SetupDetailTexturing(g_Texture[pObject->materialID], -1);		}		// Turn on backface culling.		glEnable(GL_CULL_FACE);		// Check if the object is to be rendered without culling.		if (!pObject->doCulling)			glDisable(GL_CULL_FACE);		// Set translucency and color.		if (pObject->transPercent < 1.0f) {			glColor4f(1.0f, 1.0f, 1.0f, pObject->transPercent);			glBlendFunc(GL_SRC_ALPHA, GL_ONE);			glEnable(GL_BLEND);		} else {			glColor4f(1.0f, 1.0f, 1.0f, 1.0f);			glDisable(GL_BLEND);		}		// Shiny objects should have specular coloring applied.		if (pObject->ShinyObj) {			glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, SunAmbience);			glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &specularShine);		} else {			GLfloat noSpecular[] = {0, 0, 0, 1.0f};			float noShininess = 0.0f;			glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, noSpecular);			glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &noShininess);		}		// Set up the Vertex Array lists.		glVertexPointer(3, GL_FLOAT, 0, pObject->vertexArray);		glTexCoordPointer(2, GL_FLOAT, 0, pObject->texCoordArray);		glNormalPointer(GL_FLOAT, 0, pObject->normalArray);		// Draw the Vertex Array lists.		glDrawArrays(g_ViewMode, 0, pObject->numOfFaces);		// Disable detail texturing.		DisableDetailTexturing();		// Disable possible set blending.		if (pObject->transPercent < 1.0f)			glDisable(GL_BLEND);	}	glDisable(GL_NORMAL_ARRAY);	glDisable(GL_TEXTURE_COORD_ARRAY);	glDisable(GL_VERTEX_ARRAY);}


Does your hawk-eyes see any screwup I've made? I cannot find anything... :(
Could be the order I am storing the arrays in.. I dunno.. never done this before. Thanks for convincing me into going with Vertex Arrays :)
_the_phantom_
_the_phantom_
the 'count' value in glDrawArrays refers to the number of vertices not the number of faces, so for triangles you'd multiple it by 3
azjerei
azjerei
Oh, then I misunderstood the explanation at opengl.net. So I should type something like pObject->numOfFaces*3 ?
Myopic Rhino
Myopic Rhino
Quote:
Original post by azjerei
I cannot really agree that OGL culls what is not on the screen.. oh well.. if ppl say it does, then is maybe does so without really doing so.
I think that maybe what's causing some confusion here is that OpenGL *does* do some processing of triangles that are outside of the view frustum, but the amount of work involved is so minimal on modern hardware that there is no point in doing per-polygon frustum culling on your own. Per-object culling using bounding volumes (rendering all fully and partially visible objects) will yield better performance than doing per-polygon frustum culling. For the terrain, just group together blocks of polygons and test them as a whole.

Anyway, you should definitely invest the time and effort to incorporate vertex arrays (and really, VBOs) into your engine. Since you're unfamiliar with them, it'll take some time to get them working correctly, but the performance gains will be worth it, and there is absolutely nothing that you're doing right now that can't be done with vertex arrays/VBOs. If you think otherwise, then you don't understand them fully.
azjerei
azjerei
As you can probably see in the post above yours, I have begun work with Vertex Arrays, but the outcome was not what I expected.

I tried changing the glDrawArrays(..) call to

glDrawArrays(GL_TRIANGLES, 0, pObject->numOfVerts * 3);

but that yielded nothing except an FPS of 1!!
Kalidor
Kalidor
This probably isn't the problem because I don't see any index loading code when you load the level, but it could help.

Are you sure that the order you store the vertices is the order of the triangles (ie: tri0=(vert0,vert1,vert2), tri1=(vert3,vert4,vert5), etc). If not then you will need an array of faces storing the indices into the vertex array for each triangle, and then use glDrawElements() or glDrawRangeElements().

Like I said, from the loading code you posted this probably isn't the problem, but it does look like it is creating triangles from the wrong vertices.
azjerei
azjerei
I do it like this, roughly:

1. Level is read from file.
--1.2. When the number of vertices is read for an object, that object's
own vertex array is created with a size equal to the number of verts.
The vertex normal array is also created in this step since it is of the
same size.
--1.3. When the number of texcoords are read, a texcoord array is created with
the size of the number of texture coordinates for the object. This array
also belongs to the object.
--1.4. When a vertice is read from the file, it assigns the coordinates of that
vertex into the vertex array. The vertices are stored like this in the
file:

# X.xx Y.yy Z.zz (ex. 0 99.00000 -89.07700 100.00000) where # is the
number of the vertex and X, Y and Z are the coordinates (floats).

The same procedure is done when reading a normal or a texture coordinate.
They are stored in the same manner.

So.. what the above thing does (well, I hope it does according to how I have written) is that for each object, it stores the vertices, normals and texcoords in arrays like so:

el0.x, el0.y, el0.z, el1.x, el1.y, el1.z, el2.x, el2.y, el2.z, el3.x, el3.y, el3.z ... and so on and so forth (el stands for element, meaning either vertex, normal or texcoord, though not in the same array!).

Is this the correct way to store them?
azjerei
azjerei
Kalidor, I think that you are correct. Just checked up on the Vertex Array tutorial that I used (not a good tutorial at all, it does not explain anything). It seems that I need to store the indices.. whatever that is.. never heard that term before now :)

Could anybody explain?

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.