Index: color.vert
===================================================================
--- color.vert	(revision e165b853cf7814dc4e0137917e6c3ffb361b6de7)
+++ color.vert	(revision 14ff67c4e0dcfe093de8b89d89b47e028af8f03b)
@@ -1,8 +1,10 @@
 #version 410
 
-uniform mat4 model, view, proj;
+#define MAX_NUM_OBJECTS 1024
 
-layout (std140) uniform shader_data {
-  mat4 model_mat;
+uniform mat4 view, proj;
+
+layout (std140) uniform models {
+  mat4 model_mats[MAX_NUM_OBJECTS];
 };
 
@@ -10,4 +12,5 @@
 layout(location = 1) in vec3 vertex_color;
 layout(location = 2) in vec3 vertex_normal;
+layout(location = 3) in uint ubo_index;
 
 out vec3 position_eye, normal_eye, color, light_position_eye;
@@ -17,6 +20,6 @@
 
 void main() {
-  position_eye = vec3(view * model * vec4(vertex_position, 1.0));
-  normal_eye = vec3(view * model * vec4 (vertex_normal, 0.0));
+  position_eye = vec3(view * model_mats[ubo_index] * vec4(vertex_position, 1.0));
+  normal_eye = vec3(view * model_mats[ubo_index] * vec4 (vertex_normal, 0.0));
   color = vertex_color;
   light_position_eye = vec3(view * vec4(light_position_world, 1.0));
Index: new-game.cpp
===================================================================
--- new-game.cpp	(revision e165b853cf7814dc4e0137917e6c3ffb361b6de7)
+++ new-game.cpp	(revision 14ff67c4e0dcfe093de8b89d89b47e028af8f03b)
@@ -63,4 +63,8 @@
 
 const bool FULLSCREEN = false;
+const bool SHOW_FPS = false;
+const bool DISABLE_VSYNC = true;
+unsigned int MAX_UNIFORMS = 0; // Requires OpenGL constants only available at runtime
+
 int width = 640;
 int height = 480;
@@ -103,5 +107,4 @@
                   GLuint color_sp, GLuint texture_sp,
                   GLuint vao1, GLuint vao2,
-                  GLuint shader1_mat_loc, GLuint shader2_mat_loc,
                   GLuint points_vbo, GLuint normals_vbo,
                   GLuint colors_vbo, GLuint texcoords_vbo, GLuint selected_colors_vbo,
@@ -210,4 +213,28 @@
    glewExperimental = GL_TRUE;
    glewInit();
+
+   /*
+   * RENDERING ALGORITHM NOTES:
+   *
+   * Basically, I need to split my objects into groups, so that each group fits into
+   * GL_MAX_UNIFORM_BLOCK_SIZE. I need to have an offset and a size for each group.
+   * Getting the offset is straitforward. The size may as well be GL_MAX_UNIFORM_BLOCK_SIZE
+   * for each group, since it seems that smaller sizes just round up to the nearest GL_MAX_UNIFORM_BLOCK_SIZE
+   *
+   * I'll need to have a loop inside my render loop that calls glBindBufferRange(GL_UNIFORM_BUFFER, ...
+   * for every 1024 objects and then draws all those objects with one glDraw call.
+   *
+   * Since I'm currently drawing all my objects dynamically (i.e switcing the shaders they use),
+   * I'll table the implementation of this algorithm until I have a reasonable number of objects always using the same shader
+   */
+
+   GLint UNIFORM_BUFFER_OFFSET_ALIGNMENT, MAX_UNIFORM_BLOCK_SIZE;
+   glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &UNIFORM_BUFFER_OFFSET_ALIGNMENT);
+   glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &MAX_UNIFORM_BLOCK_SIZE);
+
+   MAX_UNIFORMS = MAX_UNIFORM_BLOCK_SIZE / sizeof(mat4);
+
+   cout << "UNIFORM_BUFFER_OFFSET_ALIGNMENT: " << UNIFORM_BUFFER_OFFSET_ALIGNMENT << endl;
+   cout << "MAX_UNIFORMS: " << MAX_UNIFORMS << endl;
 
    // Setup Dear ImGui binding
@@ -396,4 +423,5 @@
    GLsizeiptr textures_buffer_size = 0;
    GLsizeiptr ubo_buffer_size = 0;
+   GLsizeiptr model_mat_idx_buffer_size = 0;
 
    for (obj_it = objects.begin(); obj_it != objects.end(); obj_it++) {
@@ -401,4 +429,5 @@
       textures_buffer_size += obj_it->texcoords.size() * sizeof(GLfloat);
       ubo_buffer_size += 16 * sizeof(GLfloat);
+      model_mat_idx_buffer_size += obj_it->num_points * sizeof(GLuint);
    }
 
@@ -457,20 +486,32 @@
       offset += obj_it->normals.size() * sizeof(GLfloat);
    }
-
-   /*
+   glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
    GLuint ubo = 0;
    glGenBuffers(1, &ubo);
-
-   //glBindBuffer(GL_UNIFORM_BUFFER, ubo);
-   //glBufferData(GL_UNIFORM_BUFFER, ubo_buffer_size/2, NULL, GL_DYNAMIC_DRAW);
+   glBindBuffer(GL_UNIFORM_BUFFER, ubo);
+   glBufferData(GL_UNIFORM_BUFFER, ubo_buffer_size, NULL, GL_DYNAMIC_DRAW);
 
    offset = 0;
    for (obj_it = objects.begin(); obj_it != objects.end(); obj_it++) {
-      //glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(obj_it->model_mat), value_ptr(obj_it->model_mat));
-      //glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(obj_it->model_mat), value_ptr(objects[0].model_mat));
-      offset += 16 * sizeof(GLfloat);
-   }
-   //glBindBuffer(GL_UNIFORM_BUFFER, 0);
-   */
+      glBufferSubData(GL_UNIFORM_BUFFER, offset, sizeof(obj_it->model_mat), value_ptr(obj_it->model_mat));
+      offset += sizeof(obj_it->model_mat);
+   }
+   glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+   GLuint model_mat_idx_vbo = 0;
+   glGenBuffers(1, &model_mat_idx_vbo);
+   glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
+   glBufferData(GL_ARRAY_BUFFER, model_mat_idx_buffer_size, NULL, GL_DYNAMIC_DRAW);
+
+   offset = 0;
+   unsigned int idx = 0;
+   for (obj_it = objects.begin(); obj_it != objects.end(); obj_it++) {
+      for (int i = 0; i < obj_it->num_points; i++) {
+         glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(GLuint), &idx);
+         offset += sizeof(GLuint);
+      }
+      idx++;
+   }
 
    GLuint vao = 0;
@@ -481,4 +522,5 @@
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
+   glEnableVertexAttribArray(3);
 
    glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
@@ -487,4 +529,7 @@
    glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
+
+   glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
+   glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, 0, 0);
 
    GLuint vao2 = 0;
@@ -495,4 +540,5 @@
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
+   glEnableVertexAttribArray(3);
 
    glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
@@ -504,4 +550,7 @@
    glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
+
+   glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
+   glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, 0, 0);
 
    float speed = 1.0f;
@@ -538,13 +587,13 @@
    proj_mat = make_mat4(proj_arr);
 
-   GLuint model_test_loc = glGetUniformLocation(color_sp, "model");
+   GLuint ub_binding_point = 0;
+
    GLuint view_test_loc = glGetUniformLocation(color_sp, "view");
    GLuint proj_test_loc = glGetUniformLocation(color_sp, "proj");
-   GLuint ub_index = glGetUniformBlockIndex(color_sp, "shader_data");
-   cout << "UBO index: " << ub_index << endl;
-
-   GLuint model_mat_loc = glGetUniformLocation(texture_sp, "model");
+   GLuint color_sp_ub_index = glGetUniformBlockIndex(color_sp, "models");
+
    GLuint view_mat_loc = glGetUniformLocation(texture_sp, "view");
    GLuint proj_mat_loc = glGetUniformLocation(texture_sp, "proj");
+   GLuint texture_sp_ub_index = glGetUniformBlockIndex(texture_sp, "models");
 
    glUseProgram(color_sp);
@@ -552,5 +601,6 @@
    glUniformMatrix4fv(proj_test_loc, 1, GL_FALSE, value_ptr(proj_mat));
 
-   //glUniformBlockBinding(color_sp, ub_index, 0);
+   glUniformBlockBinding(color_sp, color_sp_ub_index, ub_binding_point);
+   glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
 
    glUseProgram(texture_sp);
@@ -558,4 +608,7 @@
    glUniformMatrix4fv(proj_mat_loc, 1, GL_FALSE, value_ptr(proj_mat));
 
+   glUniformBlockBinding(texture_sp, texture_sp_ub_index, ub_binding_point);
+   glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
+
    bool cam_moved = false;
 
@@ -568,5 +621,7 @@
 
    // disable vsync to see real framerate
-   //glfwSwapInterval(0);
+   if (DISABLE_VSYNC && SHOW_FPS) {
+      glfwSwapInterval(0);
+   }
 
    State curState = STATE_MAIN_MENU;
@@ -577,14 +632,16 @@
       previous_seconds = current_seconds;
 
-      elapsed_seconds_fps += elapsed_seconds;
-      if (elapsed_seconds_fps > 0.25f) {
-         fps = (double)frame_count / elapsed_seconds_fps;
-         cout << "FPS: " << fps << endl;
-
-         frame_count = 0;
-         elapsed_seconds_fps = 0.0f;
-      }
-
-      frame_count++;
+      if (SHOW_FPS) {
+         elapsed_seconds_fps += elapsed_seconds;
+         if (elapsed_seconds_fps > 0.25f) {
+            fps = (double)frame_count / elapsed_seconds_fps;
+            cout << "FPS: " << fps << endl;
+
+            frame_count = 0;
+            elapsed_seconds_fps = 0.0f;
+         }
+
+         frame_count++;
+      }
 
       if (fabs(last_position) > 1.0f) {
@@ -638,5 +695,4 @@
                color_sp, texture_sp,
                vao, vao2,
-               model_test_loc, model_mat_loc,
                points_vbo, normals_vbo,
                colors_vbo, texcoords_vbo, selected_colors_vbo,
@@ -863,5 +919,4 @@
                   GLuint color_sp, GLuint texture_sp,
                   GLuint vao1, GLuint vao2,
-                  GLuint shader1_mat_loc, GLuint shader2_mat_loc,
                   GLuint points_vbo, GLuint normals_vbo,
                   GLuint colors_vbo, GLuint texcoords_vbo, GLuint selected_colors_vbo,
@@ -890,6 +945,4 @@
 
    for (it = colored_objs.begin(); it != colored_objs.end(); it++) {
-      glUniformMatrix4fv(shader1_mat_loc, 1, GL_FALSE, value_ptr(objects[*it].model_mat));
-
       glDrawArrays(GL_TRIANGLES, objects[*it].vertex_vbo_offset, objects[*it].num_points);
    }
@@ -899,6 +952,4 @@
 
    for (it = selected_objs.begin(); it != selected_objs.end(); it++) {
-      glUniformMatrix4fv(shader1_mat_loc, 1, GL_FALSE, value_ptr(objects[*it].model_mat));
-
       glDrawArrays(GL_TRIANGLES, objects[*it].vertex_vbo_offset, objects[*it].num_points);
    }
@@ -908,6 +959,4 @@
 
    for (it = textured_objs.begin(); it != textured_objs.end(); it++) {
-      glUniformMatrix4fv(shader2_mat_loc, 1, GL_FALSE, value_ptr(objects[*it].model_mat));
-
       glDrawArrays(GL_TRIANGLES, objects[*it].vertex_vbo_offset, objects[*it].num_points);
    }
Index: texture.vert
===================================================================
--- texture.vert	(revision e165b853cf7814dc4e0137917e6c3ffb361b6de7)
+++ texture.vert	(revision 14ff67c4e0dcfe093de8b89d89b47e028af8f03b)
@@ -1,9 +1,16 @@
 #version 410
 
-uniform mat4 model, view, proj;
+#define MAX_NUM_OBJECTS 1024
+
+uniform mat4 view, proj;
+
+layout (std140) uniform models {
+  mat4 model_mats[MAX_NUM_OBJECTS];
+};
 
 layout(location = 0) in vec3 vertex_position;
 layout(location = 1) in vec2 vt;
 layout(location = 2) in vec3 vertex_normal;
+layout(location = 3) in uint ubo_index;
 
 out vec2 texture_coordinates;
@@ -14,6 +21,6 @@
 
 void main() {
-  position_eye = vec3(view * model * vec4(vertex_position, 1.0));
-  normal_eye = vec3(view * model * vec4 (vertex_normal, 0.0));
+  position_eye = vec3(view * model_mats[ubo_index] * vec4(vertex_position, 1.0));
+  normal_eye = vec3(view * model_mats[ubo_index] * vec4 (vertex_normal, 0.0));
   texture_coordinates = vt;
   light_position_eye = vec3(view * vec4(light_position_world, 1.0));
