Upgrading from glVertex3f to Vertex Arrays

Started by
6 comments, last by BytePtr 12 years, 8 months ago
Hi.

Many times i have read that glVertex calls are deprecated and shouldn't be used.
I work on my map editor that currently uses: glBegin, glEnd, glVertex3f etc.

Map is made of blocks (maximum 50 different blocks). The coordinates of each block are hard-coded into header file.
Array is made for getting the coordinates by block index: 1..50.

One cube example of that data (these coordinates is used by glVertex3f):

float CUBE_RAW_DATA[numCubeTypes][numSides][4][3] = {
{ // CUBE1
{ // FACE 1
{ 0.00, 0.00, 1 },
{ 1.00, 0.00, 1 },
{ 1.00, 0.50, 0 },
{ 0.00, 0.50, 0 }
},
{ // FACE 2
{ 1.00, 0.00, 1 },
{ 0.00, 0.00, 1 },
{ 0.00, 0.00, 1 },
{ 1.00, 0.00, 1 }
},
{ // FACE 3
{ 0.00, 0.50, 0 },
{ 1.00, 0.50, 0 },
{ 1.00, 0.00, 0 },
{ 0.00, 0.00, 0 }
},
{ // FACE 4
{ 0.00, 0.00, 1 },
{ 0.00, 0.50, 0 },
{ 0.00, 0.00, 0 },
{ 0.00, 0.00, 1 }
},
{ // FACE 5
{ 1.00, 0.50, 0 },
{ 1.00, 0.00, 1 },
{ 1.00, 0.00, 1 },
{ 1.00, 0.00, 0 }
}
},
...
};


Each cube face is drawn like this:

int Which = 1; // Draw CUBE NR 1 :: SIDE1

glBindTexture(GL_TEXTURE_2D, SIDE1_TEX);
glBegin(GL_QUADS);
for (int j=0; j < 4; j++) {
glVertex3f(CUBE_RAW_DATA[Which][0][j][0],
CUBE_RAW_DATA[Which][0][j][1],
CUBE_RAW_DATA[Which][0][j][2]);
}
glEnd();
};


These ( i mean commands to draw each side) are called in every frame. Because i need to react to map array changes. If in some map coordinates the CUBE ID changes, then it means, it must take specific cube with that index from the CubeData array and draw it.
Same is done for texture coordinates, i mean they are also hard-coded.

So, what's the "best" way to convert this to vertex arrays?
Of course, i need to include texture coordinates. Each cube side can have it's own textureID.


Can somebody please post me some structs or something like that, just to restructure my rendering method using vertex arrays.
Should i make struct like:

struct TVertex {
float position[3];
float normal[3];
int textureID;
struct TTexCoord {
float tu, tv;
};
};


Make some array and use it for each cube? Maybe im completely wrong. I just don't know how to make it better or rewrite it for vertex arrays.

I don't ask you to code for me but just general directions how i should do this.


That's why i ask your help.

TIA.
Advertisement

Hi.

Many times i have read that glVertex calls are deprecated and shouldn't be used.
I work on my map editor that currently uses: glBegin, glEnd, glVertex3f etc.

Map is made of blocks (maximum 50 different blocks). The coordinates of each block are hard-coded into header file.
Array is made for getting the coordinates by block index: 1..50.

One cube example of that data (these coordinates is used by glVertex3f):

float CUBE_RAW_DATA[numCubeTypes][numSides][4][3] = {
{ // CUBE1
{ // FACE 1
{ 0.00, 0.00, 1 },
{ 1.00, 0.00, 1 },
{ 1.00, 0.50, 0 },
{ 0.00, 0.50, 0 }
},
{ // FACE 2
{ 1.00, 0.00, 1 },
{ 0.00, 0.00, 1 },
{ 0.00, 0.00, 1 },
{ 1.00, 0.00, 1 }
},
{ // FACE 3
{ 0.00, 0.50, 0 },
{ 1.00, 0.50, 0 },
{ 1.00, 0.00, 0 },
{ 0.00, 0.00, 0 }
},
{ // FACE 4
{ 0.00, 0.00, 1 },
{ 0.00, 0.50, 0 },
{ 0.00, 0.00, 0 },
{ 0.00, 0.00, 1 }
},
{ // FACE 5
{ 1.00, 0.50, 0 },
{ 1.00, 0.00, 1 },
{ 1.00, 0.00, 1 },
{ 1.00, 0.00, 0 }
}
},
...
};


Each cube face is drawn like this:

int Which = 1; // Draw CUBE NR 1 :: SIDE1

glBindTexture(GL_TEXTURE_2D, SIDE1_TEX);
glBegin(GL_QUADS);
for (int j=0; j < 4; j++) {
glVertex3f(CUBE_RAW_DATA[Which][0][j][0],
CUBE_RAW_DATA[Which][0][j][1],
CUBE_RAW_DATA[Which][0][j][2]);
}
glEnd();
};


These ( i mean commands to draw each side) are called in every frame. Because i need to react to map array changes. If in some map coordinates the CUBE ID changes, then it means, it must take specific cube with that index from the CubeData array and draw it.
Same is done for texture coordinates, i mean they are also hard-coded.

So, what's the "best" way to convert this to vertex arrays?
Of course, i need to include texture coordinates. Each cube side can have it's own textureID.


Can somebody please post me some structs or something like that, just to restructure my rendering method using vertex arrays.
Should i make struct like:

struct TVertex {
float position[3];
float normal[3];
int textureID;
struct TTexCoord {
float tu, tv;
};
};


Make some array and use it for each cube? Maybe im completely wrong. I just don't know how to make it better or rewrite it for vertex arrays.

I don't ask you to code for me but just general directions how i should do this.


That's why i ask your help.

TIA.


You could use Display Lists instead, they would speed up your rendering adn you don't need to change much.

It is like:

int dl;
glGenList(dl);

glNewList(id);
//your rendering here
glEndList();

Then to call it:
glCallList(dl);
You shouldn't quote so long posts.

The display lists do not help anything. I have tried them and i don't see any difference. And they are also old.
I tried to draw "some" cubes with vertex arrays and i saw performance improvements i really liked.

In my editor there could be alot of cubes visible and there always is, so speed is important.


But i don't know how to restructure the code / array of coordinates for vertex arrays.
So i could still draw them by index.


Thank you anyway.

Should i make struct like:

struct TVertex {
float position[3];
float normal[3];
int textureID;
struct TTexCoord {
float tu, tv;
};
};


Make some array and use it for each cube? Maybe im completely wrong. I just don't know how to make it better or rewrite it for vertex arrays.


Yeah, switching to modern 3D API idioms sometimes requires rethinking your thinking, if you'd allow me such pun. If you're concerned about glVertex3f deprecation, then you should probably be ready to migrate not just to vertex arrays, but to programmable pipeline entirely. There's little gain in partial migration anyway: you could switch to client-side vertex arrays, but these are also deprecated.

For your situation, I'd advise you to thoroughly divide your raw data and your metadata. Raw data is geometry only: positions, normals, texture coordinates. These you can store in vertex buffer (if your map is hard coded as you say you may store it in one large buffer, or divide it into several buffers if you plan to use some space partitioning scheme).

Keep in mind that GL_QUADS as primitive type is removed in core profiles of modern GL as well, so you may need to store six vertices instead of four for each face (two triangles per face).

To render geometry stored in vertex buffer (or buffers) you'll need some metadata as well: this will include indices for individual faces along with TextureIDs. Since your vertices don't seem to share any data, there'd be no point in using index buffers, so you can store all your metadata in one additional client-side array. For example, you may end up with something like this:


// Raw data
struct Vertex {
float pos[3];
float normal[3];
float uv[2];
};

// Store all your vertices in a contiguous array (to load it into vertex buffer later)
Vertex CUBE_RAW_DATA[numCubeTypes*numSides*6] = { /* ... */ }; // 6 vertices per single face

// Metadata
struct FaceDescr {
int index; // index for the first vertex of this face inside vertex buffer. No point to store number of vertices, as it is fixed constant for any face
int textureID; // texture ID for this face
};

// Store the geometry layout in a separate array
FaceDescr CUBE_FACES[numCubeTypes][numSides] = { /* ... */ };


Note that CUBE_RAW_DATA is a contiguous array of Vertex. Chunks of 6 Vertex structs store all the data for a single face. "numSides" of such chunks form single cube.
Thus, CUBE_RAW_DATA[0] stores first vertex of the first face of the first cube, CUBE_RAW_DATA[6] stores first vertex of the second face of the first cube, CUBE_RAW_DATA[numSides*6] stores first vertex of the first face of the second cube and so on, so that CUBE_FACES array will look like this (pseudo-code):


FaceDescr CUBE_FACES[numCubeTypes][numSides] =
{
{ 0, CUBE1_FACE1_TEX },
{ 6, CUBE1_FACE2_TEX },
// ...
{ numSides*6, CUBE2_FACE1_TEX },
{ (numSides+1)*6, CUBE2_FACE2_TEX },
// ...
};


To store your raw vertex data in vertex buffers you may use the same approach as described in this article. Basically, you create vertex array object (VAO) that stores your vertex layout (e.g. vertex attribute types, sizes and offsets) and vertex buffer object (VBO) that stores actual data. When it's time to render a face you fetch face metadata from CUBE_FACES array, bind the VAO and texture and call glDrawArrays() with the GL_TRIANGLES primitive type, index of the first vertex and number of vertices in the face:


glBindVertexArray(vao);


int Which = 1;


// Draw faces
for (int i = 0; i < numSides; ++i)
{
glBindTexture(GL_TEXTURE_2D, CUBE_FACES[Which].textureID);
// Each face is drawn with a single command:
glDrawArrays(GL_TRIANGLES, CUBE_FACES[Which].index, 6);
}



If you're going after GL2.1, which does not have VAOs, there would be a little more work, because you'll need to manually set up arrays and array pointers every time:


glBindBuffer(GL_ARRAY_BUFFER, vbo);

glEnableVertexAttribArray( 0 ); // 0 is assumed to be attribute index for vertex position
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0 );

glEnableVertexAttribArray( 1 ); // 1 is assumed to be attribute index for vertex normal
glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(3*sizeof(float) );

glEnableVertexAttribArray( 2 ); // 2 is assumed to be attribute index for vertex texture coordinates
glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(6*sizeof(float) );

// draw as above


Another word of advice: modern GL does not allow using client-side memory to store geometry. Thus, there is no point in hard-coding your coordinates. You may consider storing them in a file instead, and load them only to transfer to vertex buffer.
WBW, capricorn
Thank you for such long reply and explanation.

Just a question to you, don't you think that VAO make things so complicated?

if your map is hard coded as you say[/quote]

Well basically map file contains only cube indexes (from 1 to 50).
Renderer reads the index from mapfile, takes vertex coordinates from the cubes array and draws specific cube.
This is done for each cube.

So you say that vertex arrays are also deprecated? Pretty hard to believe. And they will be removed from newer OpenGL?
And old code stops working?


I will re-read your post many times to understand this all better.
I basically got the idea but it seems pretty complicated. Or im wrong?
No, old code will not stop working just because a new version of GL is out.
Sig: http://glhlib.sourceforge.net
an open source GLU replacement library. Much more modern than GLU.
float matrix[16], inverse_matrix[16];
glhLoadIdentityf2(matrix);
glhTranslatef2(matrix, 0.0, 0.0, 5.0);
glhRotateAboutXf2(matrix, angleInRadians);
glhScalef2(matrix, 1.0, 1.0, -1.0);
glhQuickInvertMatrixf2(matrix, inverse_matrix);
glUniformMatrix4fv(uniformLocation1, 1, FALSE, matrix);
glUniformMatrix4fv(uniformLocation2, 1, FALSE, inverse_matrix);

Just a question to you, don't you think that VAO make things so complicated?



No, they actually do quite the contrary. VAOs are usually set up once and used many, many times. They potentially make your code shorter, but what's more important, save function calls and also may save some GPU performance.


So you say that vertex arrays are also deprecated? Pretty hard to believe. And they will be removed from newer OpenGL?
[/quote]

Client-side vertex arrays are already removed in core profile of modern GL. You can take a look at current GL specification (4.1), there's a whole section there that lists everything that is being removed. This whole deprecation/removal thing may require some getting used to when you come from pre-3.0 GL.


And old code stops working?
[/quote]

As V-man said, old code won't stop working per se, because legacy GL context creation routines cannot create non-backwards compatible GL contexts: they will either create a 2.1 context (possibly a 3.0 one, because technically it doesn't remove anything by itself), or 3.0+ context with compatibility profile (when applicable: 3.1, for example, has no compatibility profile, but may expose GL_ARB_compatibility extension instead). At the very least, if old code stopped working, we'd have already lost a bunch of good applications and games created before they even invented GL3.0 :)




I will re-read your post many times to understand this all better.
I basically got the idea but it seems pretty complicated. Or im wrong?
[/quote]

It may seem complicated at first, but actually it's all pretty straight. Actually, the code would be quite similar even for client-side vertex arrays: glEnableClientState vs glEnableVertexAttribArray, glVertexPointer et al. vs glVertexAttribPointer and so on.
WBW, capricorn
Ok, once i got this all i will try to render one cube first with your pseudo code. I don't mean to copy paste but i will try to make your explanation to work.
If i will get into problems, i will let you know.


Thanks.

This topic is closed to new replies.

Advertisement