Index: new-game.cpp
===================================================================
--- new-game.cpp	(revision dc19a39683c3c29a219677f71afacb0f89f193ef)
+++ new-game.cpp	(revision 7e106674c060e8d918013011c754ad9b0e79b951)
@@ -32,4 +32,6 @@
 #include <map>
 
+#include "utils.h"
+
 using namespace std;
 using namespace glm;
@@ -75,4 +77,5 @@
    unsigned int id;
    ObjectType type;
+   bool deleted;
 
    // Currently, model_transform should only have translate, and rotation and scale need to be done in model_base since
@@ -90,5 +93,4 @@
    vector<GLfloat> texcoords;
    vector<GLfloat> normals;
-   bool deleted;
    vec3 bounding_center;
    GLfloat bounding_radius;
@@ -106,4 +108,6 @@
    vector<GLfloat> particleVelocities;
    vector<GLfloat> particleTimes;
+   GLfloat startTime;
+   GLfloat duration;
 };
 
@@ -127,5 +131,4 @@
 
 struct BufferInfo {
-   unsigned int vbo_base;
    unsigned int ubo_base;
    unsigned int ubo_offset;
@@ -182,6 +185,7 @@
 unsigned char* loadImage(string file_name, int* x, int* y);
 
-void printVector(string label, vec3& v);
-void print4DVector(string label, vec4& v);
+void printVec3(string label, const vec3& v);
+void printVec4(string label, const vec4& v);
+void printMat4(string label, const mat4& m);
 
 void initObject(SceneObject* obj);
@@ -228,5 +232,5 @@
 Asteroid* createAsteroid(vec3 pos);
 Laser* createLaser(vec3 start, vec3 end, vec3 color, GLfloat width);
-ParticleEffect* createExplosion();
+ParticleEffect* createExplosion(mat4 model_mat);
 
 void translateLaser(Laser* laser, const vec3& translation, GLuint ubo);
@@ -237,7 +241,5 @@
 void renderMainMenuGui();
 
-void renderScene(map<GLuint, BufferInfo>& shaderBufferInfo,
-                  map<ObjectType, ShaderModelGroup>& modelGroups, GLuint ubo);
-
+void renderScene(map<ObjectType, ShaderModelGroup>& modelGroups, GLuint ubo);
 void renderSceneGui(map<string, vector<UIValue>> valueLists);
 
@@ -287,6 +289,4 @@
 Laser* rightLaser = NULL;
 EffectOverTime* rightLaserEffect = NULL;
-
-SceneObject* objExplosion;
 
 map<string, vector<UIValue>> valueLists;
@@ -488,11 +488,11 @@
 
    defineModelGroupAttrib(modelGroups[TYPE_SHIP], "vertex_position", ATTRIB_POINT_VARYING,
-      3, GL_FLOAT, offsetof(SceneObject, points));
+      3, GL_FLOAT, offset_of(&SceneObject::points));
    defineModelGroupAttrib(modelGroups[TYPE_SHIP], "vertex_color", ATTRIB_POINT_VARYING,
-      3, GL_FLOAT, offsetof(SceneObject, colors));
+      3, GL_FLOAT, offset_of(&SceneObject::colors));
    defineModelGroupAttrib(modelGroups[TYPE_SHIP], "vertex_normal", ATTRIB_POINT_VARYING,
-      3, GL_FLOAT, offsetof(SceneObject, normals));
+      3, GL_FLOAT, offset_of(&SceneObject::normals));
    defineModelGroupAttrib(modelGroups[TYPE_SHIP], "ubo_index", ATTRIB_OBJECT_VARYING,
-      1, GL_UNSIGNED_INT, offsetof(SceneObject, ubo_offset));
+      1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset));
 
    defineModelGroupUniform(modelGroups[TYPE_SHIP], "view", ATTRIB_UNIFORM,
@@ -508,11 +508,11 @@
 
    defineModelGroupAttrib(modelGroups[TYPE_ASTEROID], "vertex_position", ATTRIB_POINT_VARYING,
-      3, GL_FLOAT, offsetof(SceneObject, points));
+      3, GL_FLOAT, offset_of(&SceneObject::points));
    defineModelGroupAttrib(modelGroups[TYPE_ASTEROID], "vertex_color", ATTRIB_POINT_VARYING,
-      3, GL_FLOAT, offsetof(SceneObject, colors));
+      3, GL_FLOAT, offset_of(&SceneObject::colors));
    defineModelGroupAttrib(modelGroups[TYPE_ASTEROID], "vertex_normal", ATTRIB_POINT_VARYING,
-      3, GL_FLOAT, offsetof(SceneObject, normals));
+      3, GL_FLOAT, offset_of(&SceneObject::normals));
    defineModelGroupAttrib(modelGroups[TYPE_ASTEROID], "ubo_index", ATTRIB_OBJECT_VARYING,
-      1, GL_UNSIGNED_INT, offsetof(SceneObject, ubo_offset));
+      1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset));
 
    defineModelGroupUniform(modelGroups[TYPE_ASTEROID], "view", ATTRIB_UNIFORM,
@@ -528,9 +528,9 @@
 
    defineModelGroupAttrib(modelGroups[TYPE_LASER], "vertex_position", ATTRIB_POINT_VARYING,
-      3, GL_FLOAT, offsetof(SceneObject, points));
+      3, GL_FLOAT, offset_of(&SceneObject::points));
    defineModelGroupAttrib(modelGroups[TYPE_LASER], "vt", ATTRIB_POINT_VARYING,
-      2, GL_FLOAT, offsetof(SceneObject, texcoords));
+      2, GL_FLOAT, offset_of(&SceneObject::texcoords));
    defineModelGroupAttrib(modelGroups[TYPE_LASER], "ubo_index", ATTRIB_OBJECT_VARYING,
-      1, GL_UNSIGNED_INT, offsetof(SceneObject, ubo_offset));
+      1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset));
 
    defineModelGroupUniform(modelGroups[TYPE_LASER], "view", ATTRIB_UNIFORM,
@@ -547,16 +547,11 @@
    shaderBufferInfo[modelGroups[TYPE_EXPLOSION].shaderProgram] = BufferInfo(); // temporary
 
-   // The last parameter (offset) is only used for populating the buffers since the distance
-   // between each item is needed there. However, the explosion vbos are populated using different
-   // code anyway and don't use that argument, so I may as well pass in 0 here.
    defineModelGroupAttrib(modelGroups[TYPE_EXPLOSION], "v_i", ATTRIB_POINT_VARYING,
-      3, GL_FLOAT, 0);
+      3, GL_FLOAT, offset_of(&ParticleEffect::particleVelocities));
    defineModelGroupAttrib(modelGroups[TYPE_EXPLOSION], "start_time", ATTRIB_POINT_VARYING,
-      1, GL_FLOAT, 0);
+      1, GL_FLOAT, offset_of(&ParticleEffect::particleTimes));
    defineModelGroupAttrib(modelGroups[TYPE_EXPLOSION], "ubo_index", ATTRIB_OBJECT_VARYING,
-      1, GL_UNSIGNED_INT, offsetof(SceneObject, ubo_offset));
-
-   defineModelGroupUniform(modelGroups[TYPE_EXPLOSION], "explosion_start_time", ATTRIB_UNIFORM,
-      1, UNIFORM_1F, &curTime);
+      1, GL_UNSIGNED_INT, offset_of(&SceneObject::ubo_offset));
+
    defineModelGroupUniform(modelGroups[TYPE_EXPLOSION], "cur_time", ATTRIB_UNIFORM,
       1, UNIFORM_1F, &curTime);
@@ -577,7 +572,5 @@
    vector<SceneObject>::iterator obj_it;
 
-   populateBuffers(objects,
-      shaderBufferInfo, modelGroups,
-      ubo);
+   populateBuffers(objects, shaderBufferInfo, modelGroups, ubo);
 
    float cam_speed = 1.0f;
@@ -613,7 +606,6 @@
    proj_mat = make_mat4(proj_arr);
 
-   initializeParticleEffectBuffers(shaderBufferInfo, modelGroups, ubo);
-
    /* TODO: Fix the UBO binding code based on the following forum post (in order to support multiple ubos):
+      (Also, I bookmarked a great explanation of this under )
 
       No, you're misunderstanding how this works. UBO binding works exactly like texture object binding.
@@ -689,4 +681,7 @@
 
    glUseProgram(modelGroups[TYPE_EXPLOSION].shaderProgram);
+   bindUniformData(modelGroups[TYPE_EXPLOSION].attribs["view"]);
+   bindUniformData(modelGroups[TYPE_EXPLOSION].attribs["proj"]);
+
    glUniformBlockBinding(modelGroups[TYPE_EXPLOSION].shaderProgram, explosion_sp_models_ub_index, ub_binding_point);
    glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
@@ -843,30 +838,29 @@
          // this code moves the asteroids
          for (unsigned int i = 0; i < objects.size(); i++) {
-            if (objects[i]->type == TYPE_ASTEROID && !objects[i]->deleted) {
-               transformObject(*objects[i], translate(mat4(1.0f), vec3(0.0f, 0.0f, 0.04f)), ubo);
-
-               vec3 obj_center = vec3(view_mat * vec4(objects[i]->bounding_center, 1.0f));
-
-               if ((obj_center.z - objects[i]->bounding_radius) > -NEAR_CLIP) {
-                  removeObjectFromScene(*objects[i], ubo);
-               }
-               if (((Asteroid*)objects[i])->hp <= 0) {
-                  // TODO: Optimize this so I don't recalculate the camera rotation every time
-                  float cam_pitch = -50.0f * 2.0f * 3.14159f / 360.0f;
-                  mat4 pitch_mat = rotate(mat4(1.0f), cam_pitch, vec3(1.0f, 0.0f, 0.0f));
-                  mat4 model_mat = translate(mat4(1.0f), objects[i]->bounding_center + vec3(0.0f, 0.0f, 0.0f)) * pitch_mat;
-
-                  removeObjectFromScene(*objects[i], ubo);
-                  score++;
-
-                  objExplosion->model_mat = model_mat;
-
-                  // initiate an explosion
-                  glUseProgram(modelGroups[TYPE_EXPLOSION].shaderProgram);
-
-                  bindUniformData(modelGroups[TYPE_EXPLOSION].attribs["explosion_start_time"]);
-
-                  glBindBuffer(GL_UNIFORM_BUFFER, ubo);
-                  glBufferSubData(GL_UNIFORM_BUFFER, objExplosion->ubo_offset * sizeof(mat4), sizeof(mat4), value_ptr(objExplosion->model_mat));
+            if (!objects[i]->deleted) {
+               if (objects[i]->type == TYPE_ASTEROID) {
+                  transformObject(*objects[i], translate(mat4(1.0f), vec3(0.0f, 0.0f, 0.04f)), ubo);
+
+                  vec3 obj_center = vec3(view_mat * vec4(objects[i]->bounding_center, 1.0f));
+
+                  if ((obj_center.z - objects[i]->bounding_radius) > -NEAR_CLIP) {
+                     removeObjectFromScene(*objects[i], ubo);
+                  }
+                  if (((Asteroid*)objects[i])->hp <= 0) {
+                     // TODO: Optimize this so I don't recalculate the camera rotation every time
+                     float cam_pitch = -50.0f * 2.0f * 3.14159f / 360.0f;
+                     mat4 pitch_mat = rotate(mat4(1.0f), cam_pitch, vec3(1.0f, 0.0f, 0.0f));
+                     mat4 model_mat = translate(mat4(1.0f), objects[i]->bounding_center) * pitch_mat;
+
+                     removeObjectFromScene(*objects[i], ubo);
+                     score++;
+
+                     addObjectToScene(createExplosion(model_mat), shaderBufferInfo, modelGroups, ubo);
+                  }
+               } else if (objects[i]->type == TYPE_EXPLOSION) {
+                  ParticleEffect* explosion = (ParticleEffect*)objects[i];
+                  if (glfwGetTime() >= explosion->startTime + explosion->duration) {
+                     removeObjectFromScene(*objects[i], ubo);
+                  }
                }
             }
@@ -971,5 +965,5 @@
             break;
          case STATE_GAME:
-            renderScene(shaderBufferInfo, modelGroups, ubo);
+            renderScene(modelGroups, ubo);
             renderSceneGui(valueLists);
             break;
@@ -1277,20 +1271,22 @@
 }
 
-void printVector(string label, vec3& v) {
+void printVec3(string label, const vec3& v) {
    cout << label << " -> (" << v.x << "," << v.y << "," << v.z << ")" << endl;
 }
 
-void print4DVector(string label, vec4& v) {
+void printVec4(string label, const vec4& v) {
    cout << label << " -> (" << v.x << "," << v.y << "," << v.z << "," << v.w << ")" << endl;
 }
 
+void printMat4(string label, const mat4& m) {
+   cout << label << ": " << endl;
+   cout << "[ " << m[0][0] << " " << m[1][0] << " " << m[2][0] << " " << m[3][0] <<  " ]" << endl;
+   cout << "[ " << m[0][1] << " " << m[1][1] << " " << m[2][1] << " " << m[3][1] <<  " ]" << endl;
+   cout << "[ " << m[0][2] << " " << m[1][2] << " " << m[2][2] << " " << m[3][2] <<  " ]" << endl;
+   cout << "[ " << m[0][3] << " " << m[1][3] << " " << m[2][3] << " " << m[3][3] <<  " ]" << endl;
+}
+
+// TODO: Pass a reference, not a pointer
 void initObject(SceneObject* obj) {
-   // Each objects must have at least 3 points, so the size of
-   // the points array must be a positive multiple of 9
-   if (obj->points.size() == 0 || (obj->points.size() % 9) != 0) {
-      // TODO: Maybe throw some kind of error here instead
-      return;
-   }
-
    obj->id = objects.size(); // currently unused
    obj->num_points = obj->points.size() / 3;
@@ -1321,4 +1317,5 @@
 }
 
+// TODO: Check if I can pass in a reference to obj instead (do this for all other functions as well)
 void addObjectToScene(SceneObject* obj,
    map<GLuint, BufferInfo>& shaderBufferInfo,
@@ -1331,6 +1328,6 @@
    // Check if the buffers aren't large enough to fit the new object and, if so, call
    // populateBuffers() to resize and repopupulate them
-   if (modelGroups[obj->type].vboCapacity < (modelGroups[obj->type].numPoints + obj->num_points) ||
-      bufferInfo->ubo_capacity < (bufferInfo->ubo_offset + 1)) {
+   if ((modelGroups[obj->type].vboCapacity < (modelGroups[obj->type].numPoints + obj->num_points) ||
+      bufferInfo->ubo_capacity <= bufferInfo->ubo_offset)) {
 
       if (leftLaser != NULL && leftLaser->deleted) {
@@ -1356,4 +1353,5 @@
 }
 
+// TODO: Pass a reference, not a pointer
 void calculateObjectBoundingBox(SceneObject* obj) {
    GLfloat min_x = obj->points[0];
@@ -2025,43 +2023,4 @@
 }
 
-void initializeParticleEffectBuffers(map<GLuint, BufferInfo>& shaderBufferInfo,
-                  map<ObjectType, ShaderModelGroup>& modelGroups,
-                  GLuint ubo) {
-   float vv[EXPLOSION_PARTICLE_COUNT * 3]; // initial velocities vec3
-   float vt[EXPLOSION_PARTICLE_COUNT]; // initial times
-   float t_accum = 0.0f; // start time
-
-   for (int i = 0; i < EXPLOSION_PARTICLE_COUNT; i++) {
-      vt[i] = t_accum;
-      t_accum += 0.01f;
-
-      float randx = ((float)rand() / (float)RAND_MAX) - 0.5f;
-      float randy = ((float)rand() / (float)RAND_MAX) - 0.5f;
-      vv[i*3] = randx;
-      vv[i*3 + 1] = randy;
-      vv[i*3 + 2] = 0.0f;
-   }
-
-   glUseProgram(modelGroups[TYPE_EXPLOSION].shaderProgram);
-
-   bindUniformData(modelGroups[TYPE_EXPLOSION].attribs["proj"]);
-   bindUniformData(modelGroups[TYPE_EXPLOSION].attribs["view"]);
-
-   // the glBufferData calls need to stay here while the corresponding arrays
-   // are local to this function. Once they're made part of the explosion object, I might be able
-   // to move this code out of here
-
-   glBindBuffer(GL_ARRAY_BUFFER, modelGroups[TYPE_EXPLOSION].attribs["v_i"].buffer);
-   glBufferData(GL_ARRAY_BUFFER, sizeof(vv), vv, GL_STATIC_DRAW);
-
-   glBindBuffer(GL_ARRAY_BUFFER, modelGroups[TYPE_EXPLOSION].attribs["start_time"].buffer);
-   glBufferData(GL_ARRAY_BUFFER, sizeof(vt), vt, GL_STATIC_DRAW);
-
-   // TODO: createExplosion should take the velocities and times as arguments and copy them to the explosion object
-   // That way, this function could maybe be called every time a new explosion is created
-   objExplosion = createExplosion();
-   addObjectToScene(objExplosion, shaderBufferInfo, modelGroups, ubo);
-}
-
 void populateBuffers(vector<SceneObject*>& objects,
                   map<GLuint, BufferInfo>& shaderBufferInfo,
@@ -2122,5 +2081,4 @@
       // When populating the buffers, leave as much empty space as space taken up by existing objects
       // to allow new objects to be added without immediately having to resize the buffers
-      shaderBufferInfo[shaderCountIt->first].vbo_base = lastShaderCount * 2;
       shaderBufferInfo[shaderCountIt->first].ubo_base = lastShaderUboCount * 2;
 
@@ -2188,16 +2146,16 @@
          glBufferData(GL_ARRAY_BUFFER, smg->vboCapacity * GLsizeof(attrib->type) * attrib->size, NULL, GL_DYNAMIC_DRAW);
       } else if (modelGroupIt->first == TYPE_EXPLOSION) {
+         attrib = &smg->attribs["v_i"];
+         glBindBuffer(GL_ARRAY_BUFFER, attrib->buffer);
+         glBufferData(GL_ARRAY_BUFFER, smg->vboCapacity * GLsizeof(attrib->type) * attrib->size, NULL, GL_DYNAMIC_DRAW);
+
+         attrib = &smg->attribs["start_time"];
+         glBindBuffer(GL_ARRAY_BUFFER, attrib->buffer);
+         glBufferData(GL_ARRAY_BUFFER, smg->vboCapacity * GLsizeof(attrib->type) * attrib->size, NULL, GL_DYNAMIC_DRAW);
+
          attrib = &smg->attribs["ubo_index"];
          glBindBuffer(GL_ARRAY_BUFFER, attrib->buffer);
          glBufferData(GL_ARRAY_BUFFER, smg->vboCapacity * GLsizeof(attrib->type) * attrib->size, NULL, GL_DYNAMIC_DRAW);
       }
-
-      // TODO: I could move the glBufferData calls for explosions here and call glBufferSubData in copyObjectDataToBuffers
-      // To make this work, I would need to store the explosion points and times in the explosion object so I could then copy
-      // them to the buffers.
-      // Also, confirm that rendering multiple exxplosions would be feasible. They might need different curTimes
-      // Also, store the explosion model mat(s) in the global ubo, just like all other objects
-      // This should just require setting their ubo_offset correctly
-      // (This already seems to be happening. I think I just need to add a ubo index buffer to explosions)
    }
 
@@ -2226,25 +2184,21 @@
       glBindBuffer(GL_ARRAY_BUFFER, it->second.buffer);
 
-      if (obj.type != TYPE_EXPLOSION || it->first == "ubo_index") {
-         switch (it->second.attribType) {
-            case ATTRIB_POINT_VARYING:
-               glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * GLsizeof(it->second.type) * it->second.size,
-                  obj.num_points * GLsizeof(it->second.type) * it->second.size, getVectorAttribPtr(obj, it->second.fieldOffset));
-               break;
-            case ATTRIB_OBJECT_VARYING:
-               for (unsigned int i = 0; i < obj.num_points; i++) {
-                  glBufferSubData(GL_ARRAY_BUFFER, (obj.vertex_vbo_offset + i) * GLsizeof(it->second.type) * it->second.size,
-                     GLsizeof(it->second.type) * it->second.size, getScalarAttribPtr(obj, it->second.fieldOffset));
-               }
-               break;
-            case ATTRIB_UNIFORM:
-               break;
-         }
-      }
-   }
-
-   if (obj.type != TYPE_EXPLOSION) {
-      obj.model_mat = obj.model_transform * obj.model_base;
-   }
+      switch (it->second.attribType) {
+         case ATTRIB_POINT_VARYING:
+            glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * GLsizeof(it->second.type) * it->second.size,
+               obj.num_points * GLsizeof(it->second.type) * it->second.size, getVectorAttribPtr(obj, it->second.fieldOffset));
+            break;
+         case ATTRIB_OBJECT_VARYING:
+            for (unsigned int i = 0; i < obj.num_points; i++) {
+               glBufferSubData(GL_ARRAY_BUFFER, (obj.vertex_vbo_offset + i) * GLsizeof(it->second.type) * it->second.size,
+                  GLsizeof(it->second.type) * it->second.size, getScalarAttribPtr(obj, it->second.fieldOffset));
+            }
+            break;
+         case ATTRIB_UNIFORM:
+            break;
+      }
+   }
+
+   obj.model_mat = obj.model_transform * obj.model_base;
 
    glBindBuffer(GL_UNIFORM_BUFFER, ubo);
@@ -2256,5 +2210,11 @@
       ostringstream oss;
       oss << "hp[" << obj.ubo_offset << "]";
-      glUniform1f(glGetUniformLocation(modelGroups[TYPE_ASTEROID].shaderProgram, oss.str().c_str()), ((Asteroid*)&obj)->hp);
+      glUniform1f(glGetUniformLocation(modelGroups[TYPE_ASTEROID].shaderProgram, oss.str().c_str()), ((Asteroid&)obj).hp);
+   } else if (obj.type == TYPE_EXPLOSION) {
+      glUseProgram(modelGroups[TYPE_EXPLOSION].shaderProgram);
+
+      ostringstream oss;
+      oss << "explosion_start_time[" << obj.ubo_offset << "]";
+      glUniform1f(glGetUniformLocation(modelGroups[TYPE_EXPLOSION].shaderProgram, oss.str().c_str()), ((ParticleEffect&)obj).startTime);
    }
 
@@ -2425,6 +2385,5 @@
 }
 
-void renderScene(map<GLuint, BufferInfo>& shaderBufferInfo,
-                  map<ObjectType, ShaderModelGroup>& modelGroups, GLuint ubo) {
+void renderScene(map<ObjectType, ShaderModelGroup>& modelGroups, GLuint ubo) {
 
    glUseProgram(modelGroups[TYPE_SHIP].shaderProgram);
@@ -2722,13 +2681,31 @@
 }
 
-ParticleEffect* createExplosion() {
+// TODO: Maybe pass in startTime instead of calling glfwGetTime() here
+ParticleEffect* createExplosion(mat4 model_mat) {
    ParticleEffect* obj = new ParticleEffect();
+
    obj->type = TYPE_EXPLOSION;
 
-   obj->points = {};
-   obj->colors = {};
-
    initObject(obj);
+
    obj->num_points = EXPLOSION_PARTICLE_COUNT;
+   obj->model_base = model_mat;
+   obj->startTime = glfwGetTime();
+   obj->duration = 0.5f; // This is also hard-coded in the shader. TODO; Pass this to the shader in an indexable ubo.
+
+   obj->particleVelocities.clear();
+   obj->particleTimes.clear();
+   float t_accum = 0.0f; // start time
+
+   for (int i = 0; i < EXPLOSION_PARTICLE_COUNT; i++) {
+      obj->particleTimes.push_back(t_accum);
+      t_accum += 0.01f;
+
+      float randx = ((float)rand() / (float)RAND_MAX) - 0.5f;
+      float randy = ((float)rand() / (float)RAND_MAX) - 0.5f;
+      obj->particleVelocities.push_back(randx);
+      obj->particleVelocities.push_back(randy);
+      obj->particleVelocities.push_back(0.0f);
+   }
 
    return obj;
