Index: explosion.vert
===================================================================
--- explosion.vert	(revision f97e6385c34858b5fb7dfd35a784688a593f2048)
+++ explosion.vert	(revision dc19a39683c3c29a219677f71afacb0f89f193ef)
@@ -4,5 +4,4 @@
 
 uniform mat4 view, proj;
-uniform mat4 model_mat;
 layout (std140) uniform models {
   mat4 model_mats[MAX_NUM_OBJECTS];
@@ -14,5 +13,5 @@
 layout (location = 0) in vec3 v_i; // initial velocity
 layout (location = 1) in float start_time;
-//layout(location = 2) in uint ubo_index;
+layout(location = 2) in uint ubo_index;
 
 out float opacity;
@@ -40,6 +39,5 @@
    p += normalize(v_i) * mod(t, duration) / duration * 0.3; // allow time to loop around so particle emitter keeps going
 
-   //gl_Position = proj * view * model_mats[ubo_index] * vec4(p, 1.0);
-   gl_Position = proj * view * model_mats[0] * vec4(p, 1.0);
+   gl_Position = proj * view * model_mats[ubo_index] * vec4(p, 1.0);
    gl_PointSize = 15.0; // size in pixels
 }
Index: new-game.cpp
===================================================================
--- new-game.cpp	(revision f97e6385c34858b5fb7dfd35a784688a593f2048)
+++ new-game.cpp	(revision dc19a39683c3c29a219677f71afacb0f89f193ef)
@@ -103,4 +103,9 @@
 };
 
+struct ParticleEffect : SceneObject {
+   vector<GLfloat> particleVelocities;
+   vector<GLfloat> particleTimes;
+};
+
 struct EffectOverTime {
    float& effectedValue;
@@ -203,6 +208,5 @@
 void calculateObjectBoundingBox(SceneObject* obj);
 
-void initializeParticleEffectBuffers(vec3 origin,
-                  map<GLuint, BufferInfo>& shaderBufferInfo,
+void initializeParticleEffectBuffers(map<GLuint, BufferInfo>& shaderBufferInfo,
                   map<ObjectType, ShaderModelGroup>& modelGroups,
                   GLuint ubo);
@@ -224,5 +228,5 @@
 Asteroid* createAsteroid(vec3 pos);
 Laser* createLaser(vec3 start, vec3 end, vec3 color, GLfloat width);
-SceneObject* createExplosion();
+ParticleEffect* createExplosion();
 
 void translateLaser(Laser* laser, const vec3& translation, GLuint ubo);
@@ -285,5 +289,4 @@
 
 SceneObject* objExplosion;
-SceneObject* objFirst;
 
 map<string, vector<UIValue>> valueLists;
@@ -551,4 +554,6 @@
    defineModelGroupAttrib(modelGroups[TYPE_EXPLOSION], "start_time", ATTRIB_POINT_VARYING,
       1, GL_FLOAT, 0);
+   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,
@@ -556,6 +561,4 @@
    defineModelGroupUniform(modelGroups[TYPE_EXPLOSION], "cur_time", ATTRIB_UNIFORM,
       1, UNIFORM_1F, &curTime);
-   defineModelGroupUniform(modelGroups[TYPE_EXPLOSION], "model_mat", ATTRIB_UNIFORM,
-      1, UNIFORM_MATRIX_4F, NULL);
    defineModelGroupUniform(modelGroups[TYPE_EXPLOSION], "view", ATTRIB_UNIFORM,
       1, UNIFORM_MATRIX_4F, value_ptr(view_mat));
@@ -610,8 +613,5 @@
    proj_mat = make_mat4(proj_arr);
 
-   initializeParticleEffectBuffers(vec3(0.0f, -1.2f, 0.65f),
-      shaderBufferInfo,
-      modelGroups,
-      ubo);
+   initializeParticleEffectBuffers(shaderBufferInfo, modelGroups, ubo);
 
    /* TODO: Fix the UBO binding code based on the following forum post (in order to support multiple ubos):
@@ -866,5 +866,7 @@
 
                   bindUniformData(modelGroups[TYPE_EXPLOSION].attribs["explosion_start_time"]);
-                  bindUniformData(modelGroups[TYPE_EXPLOSION].attribs["model_mat"], value_ptr(objExplosion->model_mat));
+
+                  glBindBuffer(GL_UNIFORM_BUFFER, ubo);
+                  glBufferSubData(GL_UNIFORM_BUFFER, objExplosion->ubo_offset * sizeof(mat4), sizeof(mat4), value_ptr(objExplosion->model_mat));
                }
             }
@@ -2023,6 +2025,5 @@
 }
 
-void initializeParticleEffectBuffers(vec3 origin,
-                  map<GLuint, BufferInfo>& shaderBufferInfo,
+void initializeParticleEffectBuffers(map<GLuint, BufferInfo>& shaderBufferInfo,
                   map<ObjectType, ShaderModelGroup>& modelGroups,
                   GLuint ubo) {
@@ -2042,11 +2043,8 @@
    }
 
-   mat4 model_mat = translate(mat4(1.0f), origin);
-
    glUseProgram(modelGroups[TYPE_EXPLOSION].shaderProgram);
 
    bindUniformData(modelGroups[TYPE_EXPLOSION].attribs["proj"]);
    bindUniformData(modelGroups[TYPE_EXPLOSION].attribs["view"]);
-   bindUniformData(modelGroups[TYPE_EXPLOSION].attribs["model_mat"], value_ptr(model_mat));
 
    // the glBufferData calls need to stay here while the corresponding arrays
@@ -2060,4 +2058,6 @@
    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);
@@ -2187,5 +2187,17 @@
          glBindBuffer(GL_ARRAY_BUFFER, attrib->buffer);
          glBufferData(GL_ARRAY_BUFFER, smg->vboCapacity * GLsizeof(attrib->type) * attrib->size, NULL, GL_DYNAMIC_DRAW);
-      }
+      } else if (modelGroupIt->first == TYPE_EXPLOSION) {
+         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)
    }
 
@@ -2206,21 +2218,13 @@
    BufferInfo* bufferInfo = &shaderBufferInfo[modelGroups[obj.type].shaderProgram];
 
-   if (obj.type == TYPE_SHIP || obj.type == TYPE_ASTEROID || obj.type == TYPE_LASER) {
-      obj.vertex_vbo_offset = modelGroups[obj.type].numPoints;
-   } else {
-      obj.vertex_vbo_offset = bufferInfo->vbo_base + modelGroups[obj.type].numPoints;
-   }
+   obj.vertex_vbo_offset = modelGroups[obj.type].numPoints;
    obj.ubo_offset = bufferInfo->ubo_base + bufferInfo->ubo_offset;
 
-   if (obj.ubo_offset == 0) {
-      objFirst = &obj;
-   }
-
-   if (obj.type != TYPE_EXPLOSION) {
-      ShaderModelGroup& smg = modelGroups[obj.type];
-
-      for (map<string, AttribInfo>::iterator it = smg.attribs.begin(); it != smg.attribs.end(); it++) {
-         glBindBuffer(GL_ARRAY_BUFFER, it->second.buffer);
-
+   ShaderModelGroup& smg = modelGroups[obj.type];
+
+   for (map<string, AttribInfo>::iterator it = smg.attribs.begin(); it != smg.attribs.end(); it++) {
+      glBindBuffer(GL_ARRAY_BUFFER, it->second.buffer);
+
+      if (obj.type != TYPE_EXPLOSION || it->first == "ubo_index") {
          switch (it->second.attribType) {
             case ATTRIB_POINT_VARYING:
@@ -2238,16 +2242,19 @@
          }
       }
-
+   }
+
+   if (obj.type != TYPE_EXPLOSION) {
       obj.model_mat = obj.model_transform * obj.model_base;
-      glBindBuffer(GL_UNIFORM_BUFFER, ubo);
-      glBufferSubData(GL_UNIFORM_BUFFER, obj.ubo_offset * sizeof(mat4), sizeof(mat4), value_ptr(obj.model_mat));
-
-      if (obj.type == TYPE_ASTEROID) {
-         glUseProgram(modelGroups[TYPE_ASTEROID].shaderProgram);
-
-         ostringstream oss;
-         oss << "hp[" << obj.ubo_offset << "]";
-         glUniform1f(glGetUniformLocation(modelGroups[TYPE_ASTEROID].shaderProgram, oss.str().c_str()), ((Asteroid*)&obj)->hp);
-      }
+   }
+
+   glBindBuffer(GL_UNIFORM_BUFFER, ubo);
+   glBufferSubData(GL_UNIFORM_BUFFER, obj.ubo_offset * sizeof(mat4), sizeof(mat4), value_ptr(obj.model_mat));
+
+   if (obj.type == TYPE_ASTEROID) {
+      glUseProgram(modelGroups[TYPE_ASTEROID].shaderProgram);
+
+      ostringstream oss;
+      oss << "hp[" << obj.ubo_offset << "]";
+      glUniform1f(glGetUniformLocation(modelGroups[TYPE_ASTEROID].shaderProgram, oss.str().c_str()), ((Asteroid*)&obj)->hp);
    }
 
@@ -2443,11 +2450,5 @@
    glEnable(GL_PROGRAM_POINT_SIZE);
 
-   glBindBuffer(GL_UNIFORM_BUFFER, ubo);
-   glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(mat4), value_ptr(objExplosion->model_mat));
-
    glDrawArrays(GL_POINTS, 0, modelGroups[TYPE_EXPLOSION].numPoints);
-
-   glBindBuffer(GL_UNIFORM_BUFFER, ubo);
-   glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(mat4), value_ptr(objFirst->model_mat));
 
    glDisable(GL_PROGRAM_POINT_SIZE);
@@ -2721,6 +2722,6 @@
 }
 
-SceneObject* createExplosion() {
-   SceneObject* obj = new SceneObject();
+ParticleEffect* createExplosion() {
+   ParticleEffect* obj = new ParticleEffect();
    obj->type = TYPE_EXPLOSION;
 
