7

I know there are a lot of resources about this on the internet but they didn't quite seem to help me.

What I want to achieve:
I am baking a mesh from data which stores the vertices inside a vector<Vector3>.
(Vector3 is a sctruct containg float x, y, z)
It stores triangles in a map<int, vector<int>>
(the key of the map is the submesh and the vector<int> the triangles)
the uv inside a vector<Vector2>
(Vector2 is a struct containing float x, y)
and a color value in vector<Color>
(the color value applies to vertices like the uv does)

Now I want to write a code that can read that data and draw it to the screen with maximum performance

What I got:

static void renderMesh(Mesh mesh, float x, float y, float z) {
    if (mesh.triangles.empty()) return;
    if (mesh.vertices.empty()) return;
    if (mesh.uvs.empty()) return;
    glColor3f(1, 1, 1);
    typedef std::map<int, std::vector<int>>::iterator it_type;
    for (it_type iterator = mesh.triangles.begin(); iterator != mesh.triangles.end(); iterator++) {
        int submesh = iterator->first;
        if (submesh < mesh.textures.size()) glBindTexture(GL_TEXTURE_2D, mesh.textures[submesh].id);
        else glBindTexture(GL_TEXTURE_2D, 0);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        for (int i = 0; i < iterator->second.size(); i += 3) {
            int t0 = iterator->second[i + 0];
            int t1 = iterator->second[i + 1];
            int t2 = iterator->second[i + 2];
            Vector3 v0 = mesh.vertices[t0];
            Vector3 v1 = mesh.vertices[t1];
            Vector3 v2 = mesh.vertices[t2];
            Color c0 = mesh.vertexColors[t0];
            Color c1 = mesh.vertexColors[t1];
            Color c2 = mesh.vertexColors[t2];
            Vector2 u0 = mesh.uvs[t0];
            Vector2 u1 = mesh.uvs[t1];
            Vector2 u2 = mesh.uvs[t2];
            glBegin(GL_TRIANGLES);
            glColor4f(c0.r / 255.0f, c0.g / 255.0f, c0.b / 255.0f, c0.a / 255.0f); glTexCoord2d(u0.x, u0.y); glVertex3f(v0.x + x, v0.y + y, v0.z + z);
            glColor4f(c1.r / 255.0f, c1.g / 255.0f, c1.b / 255.0f, c1.a / 255.0f); glTexCoord2d(u1.x, u1.y); glVertex3f(v1.x + x, v1.y + y, v1.z + z);
            glColor4f(c2.r / 255.0f, c2.g / 255.0f, c2.b / 255.0f, c2.a / 255.0f); glTexCoord2d(u2.x, u2.y); glVertex3f(v2.x + x, v2.y + y, v2.z + z);
            glEnd();
            glColor3f(1, 1, 1);
        }
    }
}

The problem:
I found out that the way I render is not the best way and that you can achieve higher performance with glDrawArrays (I think it was called).
Could you help me rewriting my code to fit with glDrawArrays, since what I found so far on the internet did not help me too much.

Thanks, and if there is any more information needed just ask.

Sheldon
  • 117
  • 1
  • 1
  • 9
  • 4
    Just a suggestion: the version of OpenGL your are learning has been deprecated for a number of years. I suggest you put your time in learning a more up to date version. See also: http://stackoverflow.com/questions/14300569/opengl-glbegin-glend – Richard Critten Jan 23 '16 at 12:30
  • If you move from OpenGL v1.0 to v3.5 - 4.5 you will transition to GL Calls that work with Shaders on the GPU as opposed to working on the CPU. Once you have the framework in place to use GLSL then it can be very efficient to create a Batch Process. Using Batches will maximize the amount of vertices per render call lowering the amount of CPU to GPU calls which will also reduce the bottleneck that is caused while working across the Bus. Working with multiple threads will help where a worker thread can pre-calculate and create stored memory buffers while the other thread does the render calls. – Francis Cugler Jan 23 '16 at 13:37

1 Answers1

3

The use of functions like glBegin and glEnd is deprecated. Functions like glDrawArrays have a better performance, but slightly more complicated to use.

The problem of glBegin render techniques is you have to communicate each vertex one by one each time you want to draw something. Today, graphic cards are able to render thousands of vertices very quickly, but if you give it one by one, the render will become laggy regardless your graphic card performance.

The main advantage of glDrawArrays is you have to initialize your arrays once, and then draw it with one call. So first, you need to fill at the start of your program an array for each attribute. In your case: positions, colors and texture coords. It must be float arrays, something like this:

std::vector<float> vertices;
std::vector<float> colors;
std::vector<float> textureCoords;

for (int i = 0; i < iterator->second.size(); i += 3) {
    int t0 = iterator->second[i + 0];
    int t1 = iterator->second[i + 1];
    int t2 = iterator->second[i + 2];
    vertices.push_back(mesh.vertices[t0].x);
    vertices.push_back(mesh.vertices[t0].y);
    vertices.push_back(mesh.vertices[t0].z);
    vertices.push_back(mesh.vertices[t1].x);
    vertices.push_back(mesh.vertices[t1].y);
    vertices.push_back(mesh.vertices[t1].z);
    vertices.push_back(mesh.vertices[t2].x);
    vertices.push_back(mesh.vertices[t2].y);
    vertices.push_back(mesh.vertices[t2].z);

    // [...] Same for colors and texture coords.
}

Then, in another function set only for display, you can use these arrays in order to draw it:

// Enable everything you need
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

// Set your used arrays
glVertexPointer(3, GL_FLOAT, 0, vertices.data());
glColorPointer(4, GL_FLOAT, 0, colors.data());
glTexCoordPointer(2, GL_FLOAT, 0, textureCoords.data());

// Draw your mesh
glDrawArrays(GL_TRIANGLES, 0, size); // 'size' is the number of your vertices.

// Reset initial state
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);

Of course, you'll have to enable other attributes you want to use, like texture or blending.


NOTE:

If you wish to learn about performance, there are also other functions using indices in order to reduce the size of data used, like glDrawElements.

There are also other more advanced OpenGL techniques that allows you to increase performance by saving your data directly on the graphic card memory, like Vertex Buffer Objects.

Community
  • 1
  • 1
Aracthor
  • 5,757
  • 6
  • 31
  • 59
  • Thanks, going to test it. "It must be float arrays" - so I can't use a `vector` for the colors? – Sheldon Jan 23 '16 at 12:47
  • @Sheldon Technically you can, but you'll have to change some parameters in my sample, like second argument of `glColorPointer`. Most of programs use float even for colors because it is the favorite type of OpenGL in itself. – Aracthor Jan 23 '16 at 12:50
  • @Sheldon be careful of what you call a "vertex". The third argument is the number of "points" of your mesh, but each point have 3 space coordinates (x, y, z). So if you give the size of the "vertices" vector as parameter, you give three times too many vertices that you should, and `glDrawArrays` shall look too far on your arrays. – Aracthor Jan 23 '16 at 13:28
  • Try to display just positions without texture or colors for start, it should be a white mesh. And try to render it with a non-black and non-white clear color, maybe it is the same color at the background one and you don't see it. – Aracthor Jan 23 '16 at 13:35
  • That was the first thing I tried. It didn't render. – Sheldon Jan 23 '16 at 13:39
  • @Sheldon Even just for one triangle of 9 coordinates and no color nor texture? It should works, as long as your `std::vector` is the same than the one you filled. – Aracthor Jan 23 '16 at 13:43
  • The problem may be your translation... Is the `player.position` the position of your mesh or of your camera? In the second case, you have to negate it on the first translation and positive it on the second one. – Aracthor Jan 23 '16 at 13:52
  • It's the camera's position. I don't quite see why I should make it negative first... but doing that didn't change anything. – Sheldon Jan 23 '16 at 13:54
  • So if you call `glLoadIdentity` on the start of your program in order to correctly use `glTranslate`, and use at least OpenGL 1.1, there is nothing that should forbid you to see something... – Aracthor Jan 23 '16 at 14:01
  • That sounds like you think I am a complete idiot... it is not working anyway. Yes, I use glLoadIdentity and yes, it would render the mesh in another color than the background but the mesh is still invisible. thanks for trying though. – Sheldon Jan 23 '16 at 14:06