Index: new-game.cpp
===================================================================
--- new-game.cpp	(revision dd9771cac122d0ed1b2280c086e6bed85e6fe504)
+++ new-game.cpp	(revision a0eb547a52292720b00f8756cacd84e336ffd2f5)
@@ -53,6 +53,10 @@
 };
 
-// TODO: Once all object types use ShaderModelGroup objects,, I should be able to remove the
-// shader_program field since it would be available via modelGroups[type].shaderProgram
+enum AttribType {
+   ATTRIB_UNIFORM,
+   ATTRIB_OBJECT_VARYING,
+   ATTRIB_POINT_VARYING,
+};
+
 struct SceneObject {
    unsigned int id;
@@ -105,5 +109,4 @@
 struct BufferInfo {
    unsigned int vbo_base;
-   unsigned int vbo_capacity;
    unsigned int ubo_base;
    unsigned int ubo_offset;
@@ -111,8 +114,19 @@
 };
 
+struct AttribInfo {
+   AttribType attribType;
+   GLuint index;
+   GLint size;
+   GLenum type;
+   GLuint buffer;
+   size_t fieldOffset;
+};
+
 struct ShaderModelGroup {
    GLuint shaderProgram;
    GLuint vao;
+   map<string, AttribInfo> attribs;
    unsigned int numPoints;
+   unsigned int vboCapacity;
 };
 
@@ -158,4 +172,11 @@
 void addModelToGroup(ShaderModelGroup& modelGroup, SceneObject& model);
 
+void defineModelGroupAttrib(ShaderModelGroup& modelGroup, string name, AttribType attribType, GLint size, GLenum type, size_t fieldOffset);
+void initModelGroupAttribs(ShaderModelGroup& modelGroup);
+
+size_t GLsizeof(GLenum);
+GLvoid* getVectorAttribPtr(SceneObject& obj, size_t attribOffset);
+GLvoid* getScalarAttribPtr(SceneObject& obj, size_t attribOffset);
+
 void calculateObjectBoundingBox(SceneObject* obj);
 
@@ -207,5 +228,5 @@
 
 void translateLaser(Laser* laser, const vec3& translation, GLuint ubo);
-void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, GLuint points_vbo, GLuint asteroid_sp);
+void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, ShaderModelGroup& laserSmg, GLuint asteroid_sp);
 bool getLaserAndAsteroidIntersection(vec3& start, vec3& end, SceneObject& asteroid, vec3& intersection);
 
@@ -441,9 +462,4 @@
     * of any given object.
     *
-    * There will be two shader programs for now, one for draing colored objects, and another for
-    * drawing textured ones. The points, normals, and model mat ubo indices will be passed to both
-    * shaders, while the colors vbo will only be passed to the colors shader, and the texcoords vbo
-    * only to the texture shader.
-    *
     * Right now, the currently selected object is drawn using one color (specified in the selected
     * colors vbo) regardless of whether it is normally rendering using colors or a texture. The selected
@@ -454,33 +470,4 @@
     */
 
-   map<GLuint, BufferInfo> shaderBufferInfo;
-   map<ObjectType, ShaderModelGroup> modelGroups;
-
-   modelGroups[TYPE_SHIP] = createModelGroup(
-      loadShaderProgram("./ship.vert", "./ship.frag"));
-   shaderBufferInfo[modelGroups[TYPE_SHIP].shaderProgram] = BufferInfo(); // temporary
-
-   modelGroups[TYPE_ASTEROID] = createModelGroup(
-      loadShaderProgram("./asteroid.vert", "./asteroid.frag"));
-   shaderBufferInfo[modelGroups[TYPE_ASTEROID].shaderProgram] = BufferInfo(); // temporary
-
-   modelGroups[TYPE_LASER] = createModelGroup(
-      loadShaderProgram("./laser.vert", "./laser.frag"));
-   shaderBufferInfo[modelGroups[TYPE_LASER].shaderProgram] = BufferInfo(); // temporary
-
-   modelGroups[TYPE_EXPLOSION] = createModelGroup(
-      loadShaderProgram("./explosion.vert", "./explosion.frag"));
-   shaderBufferInfo[modelGroups[TYPE_EXPLOSION].shaderProgram] = BufferInfo(); // temporary
-
-   cam_pos = vec3(0.0f, 0.0f, 2.0f);
-   float cam_yaw = 0.0f * 2.0f * 3.14159f / 360.0f;
-   float cam_pitch = -50.0f * 2.0f * 3.14159f / 360.0f;
-
-   // player ship
-   SceneObject* ship = createShip();
-   objects.push_back(ship);
-
-   vector<SceneObject>::iterator obj_it;
-
    GLuint points_vbo, colors_vbo, texcoords_vbo, normals_vbo, ubo, model_mat_idx_vbo;
 
@@ -492,4 +479,108 @@
       &ubo,
       &model_mat_idx_vbo);
+
+   map<GLuint, BufferInfo> shaderBufferInfo;
+   map<ObjectType, ShaderModelGroup> modelGroups;
+
+   modelGroups[TYPE_SHIP] = createModelGroup(
+      loadShaderProgram("./ship.vert", "./ship.frag"));
+   shaderBufferInfo[modelGroups[TYPE_SHIP].shaderProgram] = BufferInfo(); // temporary
+
+   defineModelGroupAttrib(modelGroups[TYPE_SHIP], "vertex_position", ATTRIB_POINT_VARYING,
+      3, GL_FLOAT, offsetof(SceneObject, points));
+   defineModelGroupAttrib(modelGroups[TYPE_SHIP], "vertex_color", ATTRIB_POINT_VARYING,
+      3, GL_FLOAT, offsetof(SceneObject, colors));
+   defineModelGroupAttrib(modelGroups[TYPE_SHIP], "vertex_normal", ATTRIB_POINT_VARYING,
+      3, GL_FLOAT, offsetof(SceneObject, normals));
+   defineModelGroupAttrib(modelGroups[TYPE_SHIP], "ubo_index", ATTRIB_OBJECT_VARYING,
+      1, GL_UNSIGNED_INT, offsetof(SceneObject, ubo_offset));
+
+   initModelGroupAttribs(modelGroups[TYPE_SHIP]);
+
+   glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
+   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
+   modelGroups[TYPE_SHIP].attribs["vertex_position"].buffer = points_vbo;
+
+   glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
+   glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
+   modelGroups[TYPE_SHIP].attribs["vertex_color"].buffer = colors_vbo;
+
+   glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
+   glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, NULL);
+   modelGroups[TYPE_SHIP].attribs["vertex_normal"].buffer = normals_vbo;
+
+   glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
+   glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, 0, NULL);
+   modelGroups[TYPE_SHIP].attribs["ubo_index"].buffer = model_mat_idx_vbo;
+
+   modelGroups[TYPE_ASTEROID] = createModelGroup(
+      loadShaderProgram("./asteroid.vert", "./asteroid.frag"));
+   shaderBufferInfo[modelGroups[TYPE_ASTEROID].shaderProgram] = BufferInfo(); // temporary
+
+   defineModelGroupAttrib(modelGroups[TYPE_ASTEROID], "vertex_position", ATTRIB_POINT_VARYING,
+      3, GL_FLOAT, offsetof(SceneObject, points));
+   defineModelGroupAttrib(modelGroups[TYPE_ASTEROID], "vertex_color", ATTRIB_POINT_VARYING,
+      3, GL_FLOAT, offsetof(SceneObject, colors));
+   defineModelGroupAttrib(modelGroups[TYPE_ASTEROID], "vertex_normal", ATTRIB_POINT_VARYING,
+      3, GL_FLOAT, offsetof(SceneObject, normals));
+   defineModelGroupAttrib(modelGroups[TYPE_ASTEROID], "ubo_index", ATTRIB_OBJECT_VARYING,
+      1, GL_UNSIGNED_INT, offsetof(SceneObject, ubo_offset));
+
+   initModelGroupAttribs(modelGroups[TYPE_ASTEROID]);
+
+   glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
+   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
+   modelGroups[TYPE_ASTEROID].attribs["vertex_position"].buffer = points_vbo;
+
+   glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
+   glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
+   modelGroups[TYPE_ASTEROID].attribs["vertex_color"].buffer = colors_vbo;
+
+   glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
+   glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, NULL);
+   modelGroups[TYPE_ASTEROID].attribs["vertex_normal"].buffer = normals_vbo;
+
+   glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
+   glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, 0, NULL);
+   modelGroups[TYPE_ASTEROID].attribs["ubo_index"].buffer = model_mat_idx_vbo;
+
+   modelGroups[TYPE_LASER] = createModelGroup(
+      loadShaderProgram("./laser.vert", "./laser.frag"));
+   shaderBufferInfo[modelGroups[TYPE_LASER].shaderProgram] = BufferInfo(); // temporary
+
+   defineModelGroupAttrib(modelGroups[TYPE_LASER], "vertex_position", ATTRIB_POINT_VARYING,
+      3, GL_FLOAT, offsetof(SceneObject, points));
+   defineModelGroupAttrib(modelGroups[TYPE_LASER], "vt", ATTRIB_POINT_VARYING,
+      2, GL_FLOAT, offsetof(SceneObject, texcoords));
+   defineModelGroupAttrib(modelGroups[TYPE_LASER], "ubo_index", ATTRIB_OBJECT_VARYING,
+      1, GL_UNSIGNED_INT, offsetof(SceneObject, ubo_offset));
+
+   initModelGroupAttribs(modelGroups[TYPE_LASER]);
+
+   glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
+   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
+   modelGroups[TYPE_LASER].attribs["vertex_position"].buffer = points_vbo;
+
+   glBindBuffer(GL_ARRAY_BUFFER, texcoords_vbo);
+   glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, NULL);
+   modelGroups[TYPE_LASER].attribs["vt"].buffer = texcoords_vbo;
+
+   glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
+   glVertexAttribIPointer(2, 1, GL_UNSIGNED_INT, 0, NULL);
+   modelGroups[TYPE_LASER].attribs["ubo_index"].buffer = model_mat_idx_vbo;
+
+   modelGroups[TYPE_EXPLOSION] = createModelGroup(
+      loadShaderProgram("./explosion.vert", "./explosion.frag"));
+   shaderBufferInfo[modelGroups[TYPE_EXPLOSION].shaderProgram] = BufferInfo(); // temporary
+
+   cam_pos = vec3(0.0f, 0.0f, 2.0f);
+   float cam_yaw = 0.0f * 2.0f * 3.14159f / 360.0f;
+   float cam_pitch = -50.0f * 2.0f * 3.14159f / 360.0f;
+
+   // player ship
+   SceneObject* ship = createShip();
+   objects.push_back(ship);
+
+   vector<SceneObject>::iterator obj_it;
 
    populateBuffers(objects,
@@ -501,58 +592,4 @@
       ubo,
       model_mat_idx_vbo);
-
-   glBindVertexArray(modelGroups[TYPE_SHIP].vao);
-
-   glEnableVertexAttribArray(0);
-   glEnableVertexAttribArray(1);
-   glEnableVertexAttribArray(2);
-   glEnableVertexAttribArray(3);
-
-   glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
-   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
-
-   // Comment these two lines out when I want to use selected colors
-   glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
-   glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
-
-   glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
-   glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, NULL);
-
-   glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
-   glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, 0, NULL);
-
-   glBindVertexArray(modelGroups[TYPE_ASTEROID].vao);
-
-   glEnableVertexAttribArray(0);
-   glEnableVertexAttribArray(1);
-   glEnableVertexAttribArray(2);
-   glEnableVertexAttribArray(3);
-
-   glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
-   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
-
-   glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
-   glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
-
-   glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
-   glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, NULL);
-
-   glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
-   glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, 0, NULL);
-
-   glBindVertexArray(modelGroups[TYPE_LASER].vao);
-
-   glEnableVertexAttribArray(0);
-   glEnableVertexAttribArray(1);
-   glEnableVertexAttribArray(2);
-
-   glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
-   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
-
-   glBindBuffer(GL_ARRAY_BUFFER, texcoords_vbo);
-   glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, NULL);
-
-   glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
-   glVertexAttribIPointer(2, 1, GL_UNSIGNED_INT, 0, NULL);
 
    float cam_speed = 1.0f;
@@ -642,4 +679,7 @@
    GLuint asteroid_sp_models_ub_index = glGetUniformBlockIndex(modelGroups[TYPE_ASTEROID].shaderProgram, "models");
 
+   // may want to do initialization for basic_texture uniform here too
+   // Right now, I think I'm getting away without getting that uniform location because I'm only
+   // using one texture, so setting it to GL_TEXTURE0 once works
    GLuint laser_view_mat_loc = glGetUniformLocation(modelGroups[TYPE_LASER].shaderProgram, "view");
    GLuint laser_proj_mat_loc = glGetUniformLocation(modelGroups[TYPE_LASER].shaderProgram, "proj");
@@ -872,8 +912,8 @@
 
          if (leftLaser != NULL && !leftLaser->deleted) {
-            updateLaserTarget(leftLaser, objects, points_vbo, modelGroups[TYPE_ASTEROID].shaderProgram);
+            updateLaserTarget(leftLaser, objects, modelGroups[TYPE_LASER], modelGroups[TYPE_ASTEROID].shaderProgram);
          }
          if (rightLaser != NULL && !rightLaser->deleted) {
-            updateLaserTarget(rightLaser, objects, points_vbo, modelGroups[TYPE_ASTEROID].shaderProgram);
+            updateLaserTarget(rightLaser, objects, modelGroups[TYPE_LASER], modelGroups[TYPE_ASTEROID].shaderProgram);
          }
       }
@@ -946,6 +986,4 @@
 
          view_mat = R * T;
-
-         //printVector("cam pos", cam_pos);
 
          glUseProgram(modelGroups[TYPE_SHIP].shaderProgram);
@@ -1074,75 +1112,75 @@
 
    switch (source) {
-   case 0x8246:
-      strcpy(source_str, "API");
-      break;
-   case 0x8247:
-      strcpy(source_str, "WINDOW_SYSTEM");
-      break;
-   case 0x8248:
-      strcpy(source_str, "SHADER_COMPILER");
-      break;
-   case 0x8249:
-      strcpy(source_str, "THIRD_PARTY");
-      break;
-   case 0x824A:
-      strcpy(source_str, "APPLICATION");
-      break;
-   case 0x824B:
-      strcpy(source_str, "OTHER");
-      break;
-   default:
-      strcpy(source_str, "undefined");
-      break;
+      case 0x8246:
+         strcpy(source_str, "API");
+         break;
+      case 0x8247:
+         strcpy(source_str, "WINDOW_SYSTEM");
+         break;
+      case 0x8248:
+         strcpy(source_str, "SHADER_COMPILER");
+         break;
+      case 0x8249:
+         strcpy(source_str, "THIRD_PARTY");
+         break;
+      case 0x824A:
+         strcpy(source_str, "APPLICATION");
+         break;
+      case 0x824B:
+         strcpy(source_str, "OTHER");
+         break;
+      default:
+         strcpy(source_str, "undefined");
+         break;
    }
 
    switch (type) {
-   case 0x824C:
-      strcpy(type_str, "ERROR");
-      break;
-   case 0x824D:
-      strcpy(type_str, "DEPRECATED_BEHAVIOR");
-      break;
-   case 0x824E:
-      strcpy(type_str, "UNDEFINED_BEHAVIOR");
-      break;
-   case 0x824F:
-      strcpy(type_str, "PORTABILITY");
-      break;
-   case 0x8250:
-      strcpy(type_str, "PERFORMANCE");
-      break;
-   case 0x8251:
-      strcpy(type_str, "OTHER");
-      break;
-   case 0x8268:
-      strcpy(type_str, "MARKER");
-      break;
-   case 0x8269:
-      strcpy(type_str, "PUSH_GROUP");
-      break;
-   case 0x826A:
-      strcpy(type_str, "POP_GROUP");
-      break;
-   default:
-      strcpy(type_str, "undefined");
-      break;
+      case 0x824C:
+         strcpy(type_str, "ERROR");
+         break;
+      case 0x824D:
+         strcpy(type_str, "DEPRECATED_BEHAVIOR");
+         break;
+      case 0x824E:
+         strcpy(type_str, "UNDEFINED_BEHAVIOR");
+         break;
+      case 0x824F:
+         strcpy(type_str, "PORTABILITY");
+         break;
+      case 0x8250:
+         strcpy(type_str, "PERFORMANCE");
+         break;
+      case 0x8251:
+         strcpy(type_str, "OTHER");
+         break;
+      case 0x8268:
+         strcpy(type_str, "MARKER");
+         break;
+      case 0x8269:
+         strcpy(type_str, "PUSH_GROUP");
+         break;
+      case 0x826A:
+         strcpy(type_str, "POP_GROUP");
+         break;
+      default:
+         strcpy(type_str, "undefined");
+         break;
    }
    switch (severity) {
-   case 0x9146:
-      strcpy(severity_str, "HIGH");
-      break;
-   case 0x9147:
-      strcpy(severity_str, "MEDIUM");
-      break;
-   case 0x9148:
-      strcpy(severity_str, "LOW");
-      break;
-   case 0x826B:
-      strcpy(severity_str, "NOTIFICATION");
-      break;
-   default:
-      strcpy(severity_str, "undefined");
-      break;
+      case 0x9146:
+         strcpy(severity_str, "HIGH");
+         break;
+      case 0x9147:
+         strcpy(severity_str, "MEDIUM");
+         break;
+      case 0x9148:
+         strcpy(severity_str, "LOW");
+         break;
+      case 0x826B:
+         strcpy(severity_str, "NOTIFICATION");
+         break;
+      default:
+         strcpy(severity_str, "undefined");
+         break;
    }
 
@@ -1336,5 +1374,5 @@
    // Check if the buffers aren't large enough to fit the new object and, if so, call
    // populateBuffers() to resize and repopupulate them
-   if (bufferInfo->vbo_capacity < (modelGroups[obj->type].numPoints + obj->num_points) ||
+   if (modelGroups[obj->type].vboCapacity < (modelGroups[obj->type].numPoints + obj->num_points) ||
       bufferInfo->ubo_capacity < (bufferInfo->ubo_offset + 1)) {
 
@@ -1917,4 +1955,76 @@
 }
 
+void defineModelGroupAttrib(ShaderModelGroup& modelGroup, string name, AttribType attribType,
+                  GLint size, GLenum type, size_t fieldOffset) {
+   if (type != GL_FLOAT && type != GL_UNSIGNED_INT) {
+      cout << "Unknown shader program attribute type: " << type << endl;
+      return;
+   }
+
+   AttribInfo attribInfo;
+
+   attribInfo.attribType = attribType;
+   attribInfo.index = modelGroup.attribs.size();
+   attribInfo.size = size;
+   attribInfo.type = type;
+   attribInfo.fieldOffset = fieldOffset;
+
+   modelGroup.attribs[name] = attribInfo;
+}
+
+void initModelGroupAttribs(ShaderModelGroup& modelGroup) {
+   glBindVertexArray(modelGroup.vao);
+
+   map<string, AttribInfo>::iterator it;
+   for (it = modelGroup.attribs.begin(); it != modelGroup.attribs.end(); it++) {
+      glEnableVertexAttribArray(it->second.index);
+
+      glGenBuffers(1, &it->second.buffer);
+      glBindBuffer(GL_ARRAY_BUFFER, it->second.buffer);
+
+      switch (it->second.type) {
+         case GL_FLOAT: {
+            glVertexAttribPointer(it->second.index, it->second.size, it->second.type, GL_FALSE, 0, NULL);
+            break;
+         }
+         case GL_UNSIGNED_INT: {
+            glVertexAttribIPointer(it->second.index, it->second.size, it->second.type, 0, NULL);
+            break;
+         }
+      }
+   }
+}
+
+/* The purpose of this function is to replace the use of sizeof() when calling
+ * function like glBufferSubData and using AttribInfo to get offsets and types
+ * I need instead of hardcoding them. I can't save a type like GLfloat, but I cam
+ * save GL_FLOAT and use this function to return sizeof(GLfloat) when GL_FLOAT is
+ * passed.
+ */
+size_t GLsizeof(GLenum type) {
+   switch (type) {
+      case GL_FLOAT:
+         return sizeof(GLfloat);
+      case GL_UNSIGNED_INT:
+         return sizeof(GLuint);
+      default:
+         cout << "Uknown GL type passed to GLsizeof: " << type << endl;
+         return 0;
+   }
+}
+
+/* This function returns a reference to the first element of a given vector
+ * attribute in obj. The vector is assumed to hold GLfloats. If the same thing
+ * needs to be done later for vectors of other types, we could pass in a GLenum,
+ * and do something similar to GLsizeof
+ */
+GLvoid* getVectorAttribPtr(SceneObject& obj, size_t attribOffset) {
+   return (GLvoid*)(&(*(vector<GLfloat>*)((size_t)&obj + attribOffset))[0]);
+}
+
+GLvoid* getScalarAttribPtr(SceneObject& obj, size_t attribOffset) {
+   return (GLvoid*)((size_t)&obj + attribOffset);
+}
+
 void initializeBuffers(
                   GLuint* points_vbo,
@@ -2080,5 +2190,4 @@
       shaderBufferInfo[shaderCountIt->first].ubo_offset = 0;
 
-      shaderBufferInfo[shaderCountIt->first].vbo_capacity = shaderCounts[shaderCountIt->first] * 2;
       shaderBufferInfo[shaderCountIt->first].ubo_capacity = shaderUboCounts[shaderCountIt->first] * 2;
 
@@ -2087,7 +2196,26 @@
    }
 
+   /* Since we just want to start with lasers, make a loop that goes through all the laser model group attributes
+    * and allocates data for them. Determine how to go from GLenum (e.g. GL_FLOAT) to typedef (e.g. GLfloat)
+    */
    map<ObjectType, ShaderModelGroup>::iterator modelGroupIt;
+   ShaderModelGroup* smg;
    for (modelGroupIt = modelGroups.begin(); modelGroupIt != modelGroups.end(); modelGroupIt++) {
-      modelGroups[modelGroupIt->first].numPoints = 0;
+      smg = &modelGroups[modelGroupIt->first];
+
+      smg->numPoints = 0;
+      smg->vboCapacity = shaderCounts[smg->shaderProgram] * 2;
+      /*
+      if (modelGroupIt->first == TYPE_LASER) {
+         smg = &modelGroups[modelGroupIt->first];
+         smg->numPoints = shaderCounts[smg->shaderProgram];
+
+         // Here, I could add glBufferData calls to allocate space for laser buffers
+         if (modelGroupIt->first == TYPE_LASER) {
+            glBindBuffer(GL_ARRAY_BUFFER, smg->attribs["vertex_position"].buffer);
+            glBufferData(GL_ARRAY_BUFFER, smg->numPoints * sizeof(GLfloat) * smg->attribs["vertex_position"].size, NULL, GL_DYNAMIC_DRAW);
+         }
+      }
+      */
    }
 
@@ -2142,21 +2270,23 @@
 
    if (obj.type != TYPE_EXPLOSION) {
-      glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
-      for (unsigned int i = 0; i < obj.num_points; i++) {
-         glBufferSubData(GL_ARRAY_BUFFER, (obj.vertex_vbo_offset + i) * sizeof(GLuint), sizeof(GLuint), &obj.ubo_offset);
-      }
-
-      glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
-      glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * sizeof(GLfloat) * 3, obj.points.size() * sizeof(GLfloat), &obj.points[0]);
-
-      glBindBuffer(GL_ARRAY_BUFFER, texcoords_vbo);
-      glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * sizeof(GLfloat) * 2, obj.texcoords.size() * sizeof(GLfloat), &obj.texcoords[0]);
-
-      if (obj.type != TYPE_LASER) {
-         glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
-         glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * sizeof(GLfloat) * 3, obj.colors.size() * sizeof(GLfloat), &obj.colors[0]);
-
-         glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
-         glBufferSubData(GL_ARRAY_BUFFER, obj.vertex_vbo_offset * sizeof(GLfloat) * 3, obj.normals.size() * sizeof(GLfloat), &obj.normals[0]);
+      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);
+
+         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;
+         }
       }
 
@@ -2219,5 +2349,5 @@
 }
 
-void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, GLuint points_vbo, GLuint asteroid_sp) {
+void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, ShaderModelGroup& laserSmg, GLuint asteroid_sp) {
    // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
    // and then re-used here
@@ -2270,5 +2400,5 @@
       length = glm::length(closestIntersection - start);
 
-      // TODO: Find a more generic way of updating the laser hp than in updateLaserTarget
+      // TODO: Find a more generic way of updating the asteroid hp than in updateLaserTarget
 
       glUseProgram(asteroid_sp);
@@ -2289,6 +2419,8 @@
    laser->points[53] = -length + width / 2;
 
-   glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
-   glBufferSubData(GL_ARRAY_BUFFER, laser->vertex_vbo_offset * sizeof(GLfloat) * 3, laser->points.size() * sizeof(GLfloat), &laser->points[0]);
+   AttribInfo* attrib = &laserSmg.attribs["vertex_position"];
+   glBindBuffer(GL_ARRAY_BUFFER, attrib->buffer);
+   glBufferSubData(GL_ARRAY_BUFFER, laser->vertex_vbo_offset * GLsizeof(attrib->type) * attrib->size,
+      laser->num_points * GLsizeof(attrib->type) * attrib->size, getVectorAttribPtr(*laser, attrib->fieldOffset));
 }
 
