Sign in to follow this  

Vertex Array problem

This topic is 4708 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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...

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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*

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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[i] != 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[i];

// 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 :)

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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!!

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
Yes, I managed to figure that out. I do even save them in the level file format I am using, though I did not use the term "indices" for that list.

However.. I put that in, but whenever I have the indice assignment activated, I get a strange crashing error when loading textures.. with it disabled, the game runs fine, though without indices and thus no landscape... Blargh.. :)

Share this post


Link to post
Share on other sites
Nevermind, found the error. Now it loads correctly. But.. I see nothing of the landscape (as compared to prior this, when I at least saw something that resembled polygons). Here follows the new code I have:

Loading of indices. I have stored the indices in the level format file, and when I read the number of faces (which is equivalent to (number of indices / 3), I assign the "indice" list of this object the correct size.

// Read face number for this object and allocate memory.
} else if (!strcmp(strWord, MIS_NUM_FACES)) {
fscanf(m_FilePointer, " %d", &pObject->numOfFaces);

pObject->pFaces = new tFace [pObject->numOfFaces];

// Create the indice list.
pObject->indices = new int [pObject->numOfFaces * 3];



Next, filling in the indice list for this object. It reads the values from the file (they are stored correctly, did multiple tests) and puts them in the indice list.

// Read object faces.
} else if (!strcmp(strWord, MIS_FACE)) {
fscanf(m_FilePointer, " %d", &indx);

fscanf(m_FilePointer, " %d %d %d",
&(pObject->pFaces[indx].vertIndex[0]),
&(pObject->pFaces[indx].vertIndex[1]),
&(pObject->pFaces[indx].vertIndex[2]));

// Fill in the data in the indice list.
pObject->indices[d++] = pObject->pFaces[indx].vertIndex[0];
pObject->indices[d++] = pObject->pFaces[indx].vertIndex[1];
pObject->indices[d++] = pObject->pFaces[indx].vertIndex[2];



Finally, the rendering function, which now looks like this:

void RenderLandscape()
{
bool useDetailTexture = false;

// Activate the necessary Vertex Array properties.
glEnableClientState(GL_VERTEX_ARRAY);

for (int i = 0; i < g_3DModel.numOfObjects; i++)
{
// Draw only ground objects.
if (bItemList[i] != 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[i];

// 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);

// Draw the Vertex Array lists.
glDrawElements(GL_TRIANGLES, pObject->numOfFaces, GL_UNSIGNED_BYTE, pObject->indices);

// Disable detail texturing.
DisableDetailTexturing();

// Disable possible set blending.
if (pObject->transPercent < 1.0f)
glDisable(GL_BLEND);
}

glDisableClientState(GL_VERTEX_ARRAY);
}



I am beginning to suspect that I have to send the texture coordinates as well, though should it not work without those and just draw a color I specify instead?

Share this post


Link to post
Share on other sites
I am storing them as unsigned ints.. made the change from GL_UNSIGNED_BYTE to GL_UNSIGNED_INT in the vertex array. I guess I could use something with less memory footprint.

I should not do ->numOfFaces * 3, that's incorrect since I only want to draw the number of faces for the object.

Share this post


Link to post
Share on other sites

This topic is 4708 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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