5
votes

People constantly tell me to use at least Vertex Arrays. But i think its not a good idea since i'm using glPushMatrix() with glTranslatef/glRotatef to position an object in the 3d world.

So, should i stop using glPushMatrix() and calculate the rotated/moved vertex positions in the world "manually" and then push their vertex data into a vertex array and then render it all at once?

But, all this will get even more messed up when i use different texture surfaces for all the objects on the screen which are also depth sorted.

So:

  1. I would have to store each object with the texture surface ID as well.
  2. I would sort all the visible objects by their Z position (the game is viewed top-down only, and all objects are flat).
  3. I would go through this sorted array:
  4. Create new buffer and copy only the vertex/texcoord/color/normal into this buffer:
  5. Every time the texture surface ID has changed from previous ID, i will bind to the correct texture ID:
  6. Upload the vertex data i collected.
  7. Free the buffer used for temporary vertex array.
  8. Repeat steps 4-7 until i have gone through all the data i sorted at the first place.
  9. Free my sorted array data, and repeat steps 1-9.

Am i doing it right?

Also, how should i design the data structure for the objects i will sort? For example, is it good to use std::vector to store the vertex data for each object? Or is there better alternative? I was thinking that the std::vector to store all this data would look like:

struct GameObject {
    int TexID;
    float z; // we will sort by this
    vector<VTCNStruct> VertexData; // store each point of the object here (including color/normal/texcoord points).
};

vector<GameObject> GameObjectBuffer; // push all sortable objects here

Also, at step 4: is it possible to use the already existing std::vector in this case? I've had the idea i that i must use raw arrays, like new float[100] for sending the vertex array to my GPU, or could i somehow use my already existing sorted std::vector here somehow (efficiently) without creating new buffer every time the texture ID changes?

2

2 Answers

10
votes

Pleae abandon glBegin/glEnd now, it's deprecated and was removed from OpenGL-4. You should definitely use vertex arrays, even better if you use vertex buffer objects.

Using immediate mode (glBegin/glEnd) the OpenGL driver has to build a vertex array from the function calls. As it's not clear how many vertces will arrive, it will eventually re-allocate the memory multiple times (the times in which GPUs directly executed the calls between glBegin/glEnd are long over).

Using a vertex array will never be less performant than immediate mode. It's okay to put multiple object's geometries into a single vertex array, you can separate them through the vertex index lists. Vertex index lists can be stored in buffer objects, too.

Then between drawing the objects adjust the modelview matrix.

Sorting order should be like the followig:

  1. divide into opaque and translucent objects. Sort the opaque
  2. Sort the opqaues by GL objects (texture, shader, materials, etc.) since switching GL objects is most expensive.
  3. For each distinct GL object group sort near to far and draw in that order
  4. Sort translucents far to near and draw in that order

You should not try fiddling around with buffer up-/downloads. Just upload the vertex data one time and then just adjust the index arrays and the order in which those are submitted. There's no need to worry about available OpenGL memory, there's no limit enforced. If your data doesn't fit into GPU RAM the driver is responsible for swapping it to system memory – that's also why you should sort by OpenGL objects first, because every time you switch OpenGL objects (glBindTexture, glBindBuffer, etc.) the driver may need to swap, so you want to keep those swapping operations to a minimum. The amount of data sent to the GPU in form of index arrays will be much less, than what's sent by immediate mode calls at a minimum:

  • Index array: 16 bit per vertex (16 bit size indices are most performant).

  • Immediate mode call: 4*32 bit per vertex

2
votes

You should keep one vertex array per object. Don't use glTranslatef/glRotatef, etc. Instead collapse the transforms yourself into one matrix. Use that matrix to depth sort your objects. Then draw your objects from front to back by pushing the objects transform matrix, drawing the vertex array, then popping the transform matrix.

This approach means that you aren't constantly creating and freeing arrays to store the vertex data in.

As to using std::vector, most implementations use a raw C array inside, thus, you can do &myVec[0] to get to that array. However, don't try to persist that pointer since the std::vector could change realloc its array and then your persisted pointer would no longer be valid.