Index: NewOpenGLGame.vcxproj
===================================================================
--- NewOpenGLGame.vcxproj	(revision 2b0214ca505392d9242f491cd493243bda726bf6)
+++ NewOpenGLGame.vcxproj	(revision 0e0f851a9c036c86fd7761ec28ab1b72e823161f)
@@ -161,6 +161,8 @@
   </ItemGroup>
   <ItemGroup>
-    <None Include="color.frag" />
-    <None Include="color.vert" />
+    <None Include="asteroid.frag" />
+    <None Include="asteroid.vert" />
+    <None Include="ship.frag" />
+    <None Include="ship.vert" />
     <None Include="laser.frag" />
     <None Include="laser.vert" />
Index: asteroid.frag
===================================================================
--- asteroid.frag	(revision 0e0f851a9c036c86fd7761ec28ab1b72e823161f)
+++ asteroid.frag	(revision 0e0f851a9c036c86fd7761ec28ab1b72e823161f)
@@ -0,0 +1,52 @@
+#version 410
+
+in vec3 position_eye, normal_eye, color, light_position_eye, light2_position_eye;
+
+out vec4 frag_color;
+
+// fixed point light properties
+vec3 Ls = vec3(1.0, 1.0, 1.0);
+vec3 Ld = vec3(1.0, 1.0, 1.0);
+vec3 La = vec3(0.2, 0.2, 0.2);
+
+// surface reflectance
+vec3 Ks = vec3(1.0, 1.0, 1.0);
+vec3 Kd = vec3(1.0, 1.5, 1.0);
+vec3 Ka = vec3(0.2, 0.2, 0.2);
+float specular_exponent = 100.0; // specular 'power'
+
+void main() {
+  // ambient intensity
+  vec3 Ia = La * Ka;
+
+  // ambient intensity
+  vec3 Ia2 = La * Ka;
+
+  vec3 direction_to_light_eye = normalize(light_position_eye - position_eye);
+  float dot_prod = max(dot(direction_to_light_eye, normal_eye), 0.0);
+
+  // diffuse intensity
+  vec3 Id = Ld * color * dot_prod;
+
+  vec3 direction_to_light2_eye = normalize(light2_position_eye - position_eye);
+  float dot_prod2 = max(dot(direction_to_light2_eye, normal_eye), 0.0);
+
+  // diffuse intensity
+  vec3 Id2 = Ld * color * dot_prod2;
+
+  vec3 surface_to_viewer_eye = normalize(-position_eye);
+
+  vec3 reflection_eye = reflect(-direction_to_light_eye, normal_eye);
+  float dot_prod_specular = max(dot(reflection_eye, surface_to_viewer_eye), 0.0);
+  float specular_factor = pow(dot_prod_specular, specular_exponent);
+
+  vec3 reflection_eye2 = reflect(-direction_to_light2_eye, normal_eye);
+  float dot_prod_specular2 = max(dot(reflection_eye2, surface_to_viewer_eye), 0.0);
+  float specular_factor2 = pow(dot_prod_specular2, specular_exponent);
+
+  // specular intensity
+  vec3 Is = Ls * Ks * specular_factor;
+  vec3 Is2 = Ls * Ks * specular_factor2;
+
+  frag_color = vec4((Is + Id + Ia + Is2 + Id2 + Ia2)/2, 1.0);
+}
Index: asteroid.vert
===================================================================
--- asteroid.vert	(revision 0e0f851a9c036c86fd7761ec28ab1b72e823161f)
+++ asteroid.vert	(revision 0e0f851a9c036c86fd7761ec28ab1b72e823161f)
@@ -0,0 +1,37 @@
+#version 410
+
+#define MAX_NUM_OBJECTS 1024
+
+uniform mat4 view, proj, hp;
+
+layout (std140) uniform models {
+  mat4 model_mats[MAX_NUM_OBJECTS];
+};
+
+/*
+layout (std140) uniform hp_uniform {
+  mat4 hp[MAX_NUM_OBJECTS];
+};
+*/
+
+layout(location = 0) in vec3 vertex_position;
+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, light2_position_eye;
+
+// fixed point light position
+vec3 light_position_world = vec3(0.0, 0.0, 2.0);
+vec3 light2_position_world = vec3(0.0, 1.5, -0.1);
+
+void main() {
+  position_eye = vec3(view * model_mats[ubo_index] * vec4(vertex_position, 1.0));
+  normal_eye = normalize(vec3(view * model_mats[ubo_index] * vec4(vertex_normal, 0.0)));
+  //color = vertex_color * (hp[ubo_index][0][0] * 10.0); //(hp[ubo_index*4] / 10.0);
+  color = vertex_color * (hp[0][0] * 2.0 / 10.0);
+  light_position_eye = vec3(view * vec4(light_position_world, 1.0));
+  light2_position_eye = vec3(view * vec4(light2_position_world, 1.0));
+
+  gl_Position = proj * vec4(position_eye, 1.0);
+}
Index: lor.frag
===================================================================
--- color.frag	(revision 2b0214ca505392d9242f491cd493243bda726bf6)
+++ 	(revision )
@@ -1,52 +1,0 @@
-#version 410
-
-in vec3 position_eye, normal_eye, color, light_position_eye, light2_position_eye;
-
-out vec4 frag_color;
-
-// fixed point light properties
-vec3 Ls = vec3(1.0, 1.0, 1.0);
-vec3 Ld = vec3(1.0, 1.0, 1.0);
-vec3 La = vec3(0.2, 0.2, 0.2);
-
-// surface reflectance
-vec3 Ks = vec3(1.0, 1.0, 1.0);
-vec3 Kd = vec3(1.0, 1.5, 1.0);
-vec3 Ka = vec3(0.2, 0.2, 0.2);
-float specular_exponent = 100.0; // specular 'power'
-
-void main() {
-  // ambient intensity
-  vec3 Ia = La * Ka;
-
-  // ambient intensity
-  vec3 Ia2 = La * Ka;
-
-  vec3 direction_to_light_eye = normalize(light_position_eye - position_eye);
-  float dot_prod = max(dot(direction_to_light_eye, normal_eye), 0.0);
-
-  // diffuse intensity
-  vec3 Id = Ld * color * dot_prod;
-
-  vec3 direction_to_light2_eye = normalize(light2_position_eye - position_eye);
-  float dot_prod2 = max(dot(direction_to_light2_eye, normal_eye), 0.0);
-
-  // diffuse intensity
-  vec3 Id2 = Ld * color * dot_prod2;
-
-  vec3 surface_to_viewer_eye = normalize(-position_eye);
-
-  vec3 reflection_eye = reflect(-direction_to_light_eye, normal_eye);
-  float dot_prod_specular = max(dot(reflection_eye, surface_to_viewer_eye), 0.0);
-  float specular_factor = pow(dot_prod_specular, specular_exponent);
-
-  vec3 reflection_eye2 = reflect(-direction_to_light2_eye, normal_eye);
-  float dot_prod_specular2 = max(dot(reflection_eye2, surface_to_viewer_eye), 0.0);
-  float specular_factor2 = pow(dot_prod_specular2, specular_exponent);
-
-  // specular intensity
-  vec3 Is = Ls * Ks * specular_factor;
-  vec3 Is2 = Ls * Ks * specular_factor2;
-
-  frag_color = vec4((Is + Id + Ia + Is2 + Id2 + Ia2)/2, 1.0);
-}
Index: lor.vert
===================================================================
--- color.vert	(revision 2b0214ca505392d9242f491cd493243bda726bf6)
+++ 	(revision )
@@ -1,30 +1,0 @@
-#version 410
-
-#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 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, light2_position_eye;
-
-// fixed point light position
-vec3 light_position_world = vec3(0.0, 0.0, 2.0);
-vec3 light2_position_world = vec3(0.0, 1.5, -0.1);
-
-void main() {
-  position_eye = vec3(view * model_mats[ubo_index] * vec4(vertex_position, 1.0));
-  normal_eye = normalize(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));
-  light2_position_eye = vec3(view * vec4(light2_position_world, 1.0));
-
-  gl_Position = proj * vec4(position_eye, 1.0);
-}
Index: new-game.cpp
===================================================================
--- new-game.cpp	(revision 2b0214ca505392d9242f491cd493243bda726bf6)
+++ new-game.cpp	(revision 0e0f851a9c036c86fd7761ec28ab1b72e823161f)
@@ -76,5 +76,5 @@
 
 struct Asteroid : SceneObject {
-   double hp;
+   float hp;
 };
 
@@ -84,16 +84,15 @@
 
 struct EffectOverTime {
-   double& effectedValue;
-   double startValue;
+   float& effectedValue;
+   float startValue;
    double startTime;
-   double changePerSecond;
+   float changePerSecond;
    bool deleted;
    SceneObject* effectedObject;
 
-   EffectOverTime(double& effectedValue, double changePerSecond, SceneObject* object)
-      : effectedValue(effectedValue), effectedObject(object) {
+   EffectOverTime(float& effectedValue, float changePerSecond, SceneObject* object)
+      : effectedValue(effectedValue), changePerSecond(changePerSecond), effectedObject(object) {
       startValue = effectedValue;
       startTime = glfwGetTime();
-      this->changePerSecond = changePerSecond;
       deleted = false;
    }
@@ -113,4 +112,14 @@
 void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
 void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
+
+void APIENTRY debugGlCallback(
+   GLenum source,
+   GLenum type,
+   GLuint id,
+   GLenum severity,
+   GLsizei length,
+   const GLchar* message,
+   const void* userParam
+);
 
 bool faceClicked(array<vec3, 3> points, SceneObject* obj, vec4 world_ray, vec4 cam, vec4& click_point);
@@ -133,4 +142,5 @@
                   GLuint normals_vbo,
                   GLuint ubo,
+                  GLuint asteroid_hp_ubo,
                   GLuint model_mat_idx_vbo);
 void removeObjectFromScene(SceneObject& obj, GLuint ubo);
@@ -145,4 +155,5 @@
                   GLuint* normals_vbo,
                   GLuint* ubo,
+                  GLuint* asteroid_hp_ubo,
                   GLuint* model_mat_idx_vbo);
 
@@ -155,4 +166,5 @@
                   GLuint normals_vbo,
                   GLuint ubo,
+                  GLuint asteroid_hp_ubo,
                   GLuint model_mat_idx_vbo);
 
@@ -165,4 +177,5 @@
                   GLuint normals_vbo,
                   GLuint ubo,
+                  GLuint asteroid_hp_ubo,
                   GLuint model_mat_idx_vbo);
 
@@ -174,5 +187,5 @@
 
 void translateLaser(Laser* laser, const vec3& translation, GLuint ubo);
-void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, GLuint points_vbo);
+void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, GLuint points_vbo, GLuint asteroid_sp, GLuint asteroid_hp_loc);
 bool getLaserAndAsteroidIntersection(vec3& start, vec3& end, SceneObject& asteroid, vec3& intersection);
 
@@ -181,6 +194,6 @@
 
 void renderScene(map<GLuint, BufferInfo>& shaderBufferInfo,
-                  GLuint color_sp, GLuint texture_sp, GLuint laser_sp,
-                  GLuint color_vao, GLuint texture_vao, GLuint laser_vao,
+                  GLuint color_sp, GLuint asteroid_sp, GLuint texture_sp, GLuint laser_sp,
+                  GLuint color_vao, GLuint asteroid_vao, GLuint texture_vao, GLuint laser_vao,
                   GLuint colors_vbo, GLuint selected_colors_vbo,
                   SceneObject* selectedObject);
@@ -254,8 +267,11 @@
 #endif
 
-   glfwWindowHint(GLFW_SAMPLES, 16);
-
    GLFWwindow* window = NULL;
    GLFWmonitor* mon = NULL;
+
+   glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+   glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+   glfwWindowHint(GLFW_SAMPLES, 16);
+   glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true);
 
    if (FULLSCREEN) {
@@ -278,4 +294,11 @@
    glewExperimental = GL_TRUE;
    glewInit();
+
+   if (GLEW_KHR_debug) {
+      cout << "FOUND GLEW debug extension" << endl;
+      glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+      glDebugMessageCallback((GLDEBUGPROC)debugGlCallback, nullptr);
+      cout << "Bound debug callback" << endl;
+   }
 
    srand(time(0));
@@ -322,6 +345,6 @@
    const GLubyte* renderer = glGetString(GL_RENDERER);
    const GLubyte* version = glGetString(GL_VERSION);
-   printf("Renderer: %s\n", renderer);
-   printf("OpenGL version supported %s\n", version);
+   cout << "Renderer: " << renderer << endl;
+   cout << "OpenGL version supported " << version << endl;
 
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -406,9 +429,13 @@
    map<GLuint, BufferInfo> shaderBufferInfo;
 
-   GLuint color_sp = loadShaderProgram("./color.vert", "./color.frag");
+   // TODO: Rename color_sp to ship_sp and comment out texture_sp)
+
+   GLuint color_sp = loadShaderProgram("./ship.vert", "./ship.frag");
+   GLuint asteroid_sp = loadShaderProgram("./asteroid.vert", "./asteroid.frag");
    GLuint texture_sp = loadShaderProgram("./texture.vert", "./texture.frag");
    GLuint laser_sp = loadShaderProgram("./laser.vert", "./laser.frag");
 
    shaderBufferInfo[color_sp] = BufferInfo();
+   shaderBufferInfo[asteroid_sp] = BufferInfo();
    shaderBufferInfo[texture_sp] = BufferInfo();
    shaderBufferInfo[laser_sp] = BufferInfo();
@@ -425,6 +452,7 @@
 
    GLuint points_vbo, colors_vbo, selected_colors_vbo, texcoords_vbo,
-      normals_vbo, ubo, model_mat_idx_vbo;
-
+      normals_vbo, ubo, asteroid_hp_ubo, model_mat_idx_vbo;
+
+   cout << "Initializing buffers" << endl;
    initializeBuffers(
       &points_vbo,
@@ -434,6 +462,8 @@
       &normals_vbo,
       &ubo,
+      &asteroid_hp_ubo,
       &model_mat_idx_vbo);
 
+   cout << "Populating buffers" << endl;
    populateBuffers(objects,
       shaderBufferInfo,
@@ -444,5 +474,8 @@
       normals_vbo,
       ubo,
+      asteroid_hp_ubo,
       model_mat_idx_vbo);
+
+   cout << "Done" << endl;
 
    GLuint color_vao = 0;
@@ -459,4 +492,25 @@
 
    // 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, 0);
+
+   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 asteroid_vao = 0;
+   glGenVertexArrays(1, &asteroid_vao);
+   glBindVertexArray(asteroid_vao);
+
+   glEnableVertexAttribArray(0);
+   glEnableVertexAttribArray(1);
+   glEnableVertexAttribArray(2);
+   glEnableVertexAttribArray(3);
+
+   glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
+   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
+
    glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
@@ -543,14 +597,20 @@
    GLuint view_test_loc = glGetUniformLocation(color_sp, "view");
    GLuint proj_test_loc = glGetUniformLocation(color_sp, "proj");
-   GLuint color_sp_ub_index = glGetUniformBlockIndex(color_sp, "models");
+   GLuint color_sp_models_ub_index = glGetUniformBlockIndex(color_sp, "models");
+
+   GLuint asteroid_view_mat_loc = glGetUniformLocation(asteroid_sp, "view");
+   GLuint asteroid_proj_mat_loc = glGetUniformLocation(asteroid_sp, "proj");
+   GLuint asteroid_hp_loc = glGetUniformLocation(asteroid_sp, "hp");
+   GLuint asteroid_sp_models_ub_index = glGetUniformBlockIndex(asteroid_sp, "models");
+   //GLuint asteroid_sp_hp_ub_index = glGetUniformBlockIndex(asteroid_sp, "hp_uniform");
 
    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");
+   GLuint texture_sp_models_ub_index = glGetUniformBlockIndex(texture_sp, "models");
 
    GLuint laser_view_mat_loc = glGetUniformLocation(laser_sp, "view");
    GLuint laser_proj_mat_loc = glGetUniformLocation(laser_sp, "proj");
    GLuint laser_color_loc = glGetUniformLocation(laser_sp, "laser_color");
-   GLuint laser_sp_ub_index = glGetUniformBlockIndex(laser_sp, "models");
+   GLuint laser_sp_models_ub_index = glGetUniformBlockIndex(laser_sp, "models");
 
 
@@ -559,7 +619,23 @@
    glUniformMatrix4fv(proj_test_loc, 1, GL_FALSE, value_ptr(proj_mat));
 
-   glUniformBlockBinding(color_sp, color_sp_ub_index, ub_binding_point);
+   glUniformBlockBinding(color_sp, color_sp_models_ub_index, ub_binding_point);
    glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
 
+
+   GLfloat tempHp = 10.0f;
+   mat4 hp(tempHp);
+
+   glUseProgram(asteroid_sp);
+   glUniformMatrix4fv(asteroid_view_mat_loc, 1, GL_FALSE, value_ptr(view_mat));
+   glUniformMatrix4fv(asteroid_proj_mat_loc, 1, GL_FALSE, value_ptr(proj_mat));
+   glUniformMatrix4fv(asteroid_hp_loc, 1, GL_FALSE, value_ptr(hp));
+
+   glUniformBlockBinding(asteroid_sp, asteroid_sp_models_ub_index, ub_binding_point);
+   glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
+
+   /*
+   glUniformBlockBinding(asteroid_sp, asteroid_sp_hp_ub_index, ub_binding_point);
+   glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, asteroid_hp_ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
+   */
 
    glUseProgram(texture_sp);
@@ -567,5 +643,5 @@
    glUniformMatrix4fv(proj_mat_loc, 1, GL_FALSE, value_ptr(proj_mat));
 
-   glUniformBlockBinding(texture_sp, texture_sp_ub_index, ub_binding_point);
+   glUniformBlockBinding(texture_sp, texture_sp_models_ub_index, ub_binding_point);
    glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
 
@@ -576,5 +652,5 @@
    glUniform3f(laser_color_loc, 0.2f, 1.0f, 0.2f);
 
-   glUniformBlockBinding(laser_sp, laser_sp_ub_index, ub_binding_point);
+   glUniformBlockBinding(laser_sp, laser_sp_models_ub_index, ub_binding_point);
    glBindBufferRange(GL_UNIFORM_BUFFER, ub_binding_point, ubo, 0, GL_MAX_UNIFORM_BLOCK_SIZE);
 
@@ -641,5 +717,5 @@
          elapsed_seconds_spawn += elapsed_seconds;
          if (elapsed_seconds_spawn > 0.5f) {
-            SceneObject* obj = createAsteroid(vec3(getRandomNum(-1.3f, 1.3f), -1.2f, getRandomNum(-5.5f, -4.5f)), color_sp);
+            SceneObject* obj = createAsteroid(vec3(getRandomNum(-1.3f, 1.3f), -1.2f, getRandomNum(-5.5f, -4.5f)), asteroid_sp);
             addObjectToScene(obj, shaderBufferInfo,
                points_vbo,
@@ -649,4 +725,5 @@
                normals_vbo,
                ubo,
+               asteroid_hp_ubo,
                model_mat_idx_vbo);
 
@@ -708,4 +785,5 @@
                normals_vbo,
                ubo,
+               asteroid_hp_ubo,
                model_mat_idx_vbo);
          } else if (key_state[GLFW_KEY_Z] == GLFW_RELEASE) {
@@ -725,4 +803,5 @@
                normals_vbo,
                ubo,
+               asteroid_hp_ubo,
                model_mat_idx_vbo);
          } else if (key_state[GLFW_KEY_X] == GLFW_RELEASE) {
@@ -747,8 +826,8 @@
 
          if (leftLaser != NULL && !leftLaser->deleted) {
-            updateLaserTarget(leftLaser, objects, points_vbo);
+            updateLaserTarget(leftLaser, objects, points_vbo, asteroid_sp, asteroid_hp_loc);
          }
          if (rightLaser != NULL && !rightLaser->deleted) {
-            updateLaserTarget(rightLaser, objects, points_vbo);
+            updateLaserTarget(rightLaser, objects, points_vbo, asteroid_sp, asteroid_hp_loc);
          }
       }
@@ -847,6 +926,6 @@
          case STATE_GAME:
             renderScene(shaderBufferInfo,
-               color_sp, texture_sp, laser_sp,
-               color_vao, texture_vao, laser_vao,
+               color_sp, asteroid_sp, texture_sp, laser_sp,
+               color_vao, asteroid_vao, texture_vao, laser_vao,
                colors_vbo, selected_colors_vbo,
                selectedObject);
@@ -936,4 +1015,103 @@
 }
 
+void APIENTRY debugGlCallback(
+   GLenum source,
+   GLenum type,
+   GLuint id,
+   GLenum severity,
+   GLsizei length,
+   const GLchar* message,
+   const void* userParam
+) {
+   string strMessage(message);
+
+   // TODO: Use C++ strings directly
+   char source_str[2048];
+   char type_str[2048];
+   char severity_str[2048];
+
+   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;
+   }
+
+   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;
+   }
+   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;
+   }
+
+   if (string(severity_str) != "NOTIFICATION") {
+      cout << "OpenGL Error!!!" << endl;
+      cout << "Source: " << string(source_str) << endl;
+      cout << "Type: " << string(type_str) << endl;
+      cout << "Severity: " << string(severity_str) << endl;
+      cout << strMessage << endl;
+   }
+}
+
 
 GLuint loadShader(GLenum type, string file) {
@@ -1109,4 +1287,5 @@
    GLuint normals_vbo,
    GLuint ubo,
+   GLuint asteroid_hp_ubo,
    GLuint model_mat_idx_vbo) {
    objects.push_back(obj);
@@ -1133,4 +1312,5 @@
          normals_vbo,
          ubo,
+         asteroid_hp_ubo,
          model_mat_idx_vbo);
    } else {
@@ -1142,4 +1322,5 @@
          normals_vbo,
          ubo,
+         asteroid_hp_ubo,
          model_mat_idx_vbo);
    }
@@ -1692,4 +1873,5 @@
                   GLuint* normals_vbo,
                   GLuint* ubo,
+                  GLuint* asteroid_hp_ubo,
                   GLuint* model_mat_idx_vbo) {
    *points_vbo = 0;
@@ -1710,4 +1892,7 @@
    *ubo = 0;
    glGenBuffers(1, ubo);
+
+   *asteroid_hp_ubo = 0;
+   glGenBuffers(1, asteroid_hp_ubo);
 
    *model_mat_idx_vbo = 0;
@@ -1723,9 +1908,8 @@
                   GLuint normals_vbo,
                   GLuint ubo,
-                  GLuint model_mat_idx_vbo) {
-   GLsizeiptr points_buffer_size = 0;
-   GLsizeiptr textures_buffer_size = 0;
-   GLsizeiptr ubo_buffer_size = 0;
-   GLsizeiptr model_mat_idx_buffer_size = 0;
+                  GLuint asteroid_hp_ubo,
+                  GLuint ubo_idx_vbo) {
+   GLsizeiptr num_points = 0;
+   GLsizeiptr num_objects = 0;
 
    map<GLuint, unsigned int> shaderCounts;
@@ -1745,8 +1929,6 @@
          it = objects.erase(it);
       } else {
-         points_buffer_size += (*it)->num_points * sizeof(GLfloat) * 3;
-         textures_buffer_size += (*it)->num_points * sizeof(GLfloat) * 2;
-         ubo_buffer_size += 16 * sizeof(GLfloat);
-         model_mat_idx_buffer_size += (*it)->num_points * sizeof(GLuint);
+         num_points += (*it)->num_points;
+         num_objects++;
 
          if (shaderCounts.count((*it)->shader_program) == 0) {
@@ -1763,8 +1945,6 @@
 
    // double the buffer sizes to leave room for new objects
-   points_buffer_size *= 2;
-   textures_buffer_size *= 2;
-   ubo_buffer_size *= 2;
-   model_mat_idx_buffer_size *= 2;
+   num_points *= 2;
+   num_objects *= 2;
 
    map<GLuint, unsigned int>::iterator shaderIt;
@@ -1780,4 +1960,6 @@
    */
    for (shaderIt = shaderCounts.begin(); shaderIt != shaderCounts.end(); shaderIt++) {
+      // 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[shaderIt->first].vbo_base = lastShaderCount * 2;
       shaderBufferInfo[shaderIt->first].ubo_base = lastShaderUboCount * 2;
@@ -1804,23 +1986,28 @@
 
    glBindBuffer(GL_ARRAY_BUFFER, points_vbo);
-   glBufferData(GL_ARRAY_BUFFER, points_buffer_size, NULL, GL_DYNAMIC_DRAW);
+   glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 3, NULL, GL_DYNAMIC_DRAW);
 
    glBindBuffer(GL_ARRAY_BUFFER, colors_vbo);
-   glBufferData(GL_ARRAY_BUFFER, points_buffer_size, NULL, GL_DYNAMIC_DRAW);
+   glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 3, NULL, GL_DYNAMIC_DRAW);
 
    glBindBuffer(GL_ARRAY_BUFFER, selected_colors_vbo);
-   glBufferData(GL_ARRAY_BUFFER, points_buffer_size, NULL, GL_DYNAMIC_DRAW);
+   glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 3, NULL, GL_DYNAMIC_DRAW);
 
    glBindBuffer(GL_ARRAY_BUFFER, texcoords_vbo);
-   glBufferData(GL_ARRAY_BUFFER, textures_buffer_size, NULL, GL_DYNAMIC_DRAW);
+   glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 2, NULL, GL_DYNAMIC_DRAW);
 
    glBindBuffer(GL_ARRAY_BUFFER, normals_vbo);
-   glBufferData(GL_ARRAY_BUFFER, points_buffer_size, NULL, GL_DYNAMIC_DRAW);
+   glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLfloat) * 3, NULL, GL_DYNAMIC_DRAW);
 
    glBindBuffer(GL_UNIFORM_BUFFER, ubo);
-   glBufferData(GL_UNIFORM_BUFFER, ubo_buffer_size, NULL, GL_DYNAMIC_DRAW);
-
-   glBindBuffer(GL_ARRAY_BUFFER, model_mat_idx_vbo);
-   glBufferData(GL_ARRAY_BUFFER, model_mat_idx_buffer_size, NULL, GL_DYNAMIC_DRAW);
+   glBufferData(GL_UNIFORM_BUFFER, num_objects * sizeof(mat4), NULL, GL_DYNAMIC_DRAW);
+
+   /*
+   glBindBuffer(GL_UNIFORM_BUFFER, asteroid_hp_ubo);
+   glBufferData(GL_UNIFORM_BUFFER, num_objects * sizeof(mat4), NULL, GL_DYNAMIC_DRAW);
+   */
+
+   glBindBuffer(GL_ARRAY_BUFFER, ubo_idx_vbo);
+   glBufferData(GL_ARRAY_BUFFER, num_points * sizeof(GLuint), NULL, GL_DYNAMIC_DRAW);
 
    for (it = objects.begin(); it != objects.end(); it++) {
@@ -1832,5 +2019,6 @@
          normals_vbo,
          ubo,
-         model_mat_idx_vbo);
+         asteroid_hp_ubo,
+         ubo_idx_vbo);
    }
 }
@@ -1844,4 +2032,5 @@
                   GLuint normals_vbo,
                   GLuint ubo,
+                  GLuint asteroid_hp_ubo,
                   GLuint model_mat_idx_vbo) {
    BufferInfo* bufferInfo = &shaderBufferInfo[obj.shader_program];
@@ -1875,4 +2064,13 @@
    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) {
+      glBindBuffer(GL_UNIFORM_BUFFER, asteroid_hp_ubo);
+      GLfloat tempHp = 0.1f; // this value has no impact on the actual color, wtf
+      mat4 tempMat(tempHp);
+      glBufferSubData(GL_UNIFORM_BUFFER, obj.ubo_offset * sizeof(mat4), sizeof(mat4), value_ptr(tempMat));
+   //}
+   */
 
    bufferInfo->vbo_offset += obj.num_points;
@@ -1923,5 +2121,5 @@
 }
 
-void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, GLuint points_vbo) {
+void updateLaserTarget(Laser* laser, vector<SceneObject*>& objects, GLuint points_vbo, GLuint asteroid_sp, GLuint asteroid_hp_loc) {
    // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
    // and then re-used here
@@ -1958,5 +2156,5 @@
 
       if (closestAsteroid != NULL) {
-         eot = new EffectOverTime(closestAsteroid->hp, -10, closestAsteroid);
+         eot = new EffectOverTime(closestAsteroid->hp, -10.0f, closestAsteroid);
          effects.push_back(eot);
       }
@@ -1973,4 +2171,12 @@
    if (closestAsteroid != NULL) {
       length = glm::length(closestIntersection - start);
+
+      // TODO: Find a better way of doing this
+
+      GLfloat tempHp = closestAsteroid->hp;
+      mat4 hp(tempHp);
+
+      glUseProgram(asteroid_sp);
+      glUniformMatrix4fv(asteroid_hp_loc, 1, GL_FALSE, value_ptr(hp));
    }
 
@@ -2035,6 +2241,6 @@
 
 void renderScene(map<GLuint, BufferInfo>& shaderBufferInfo,
-                  GLuint color_sp, GLuint texture_sp, GLuint laser_sp,
-                  GLuint color_vao, GLuint texture_vao, GLuint laser_vao,
+                  GLuint color_sp, GLuint asteroid_sp, GLuint texture_sp, GLuint laser_sp,
+                  GLuint color_vao, GLuint asteroid_vao, GLuint texture_vao, GLuint laser_vao,
                   GLuint colors_vbo, GLuint selected_colors_vbo,
                   SceneObject* selectedObject) {
@@ -2057,4 +2263,9 @@
 
    glDrawArrays(GL_TRIANGLES, shaderBufferInfo[color_sp].vbo_base, shaderBufferInfo[color_sp].vbo_offset);
+
+   glUseProgram(asteroid_sp);
+   glBindVertexArray(asteroid_vao);
+
+   glDrawArrays(GL_TRIANGLES, shaderBufferInfo[asteroid_sp].vbo_base, shaderBufferInfo[asteroid_sp].vbo_offset);
 
    glUseProgram(texture_sp);
Index: ship.frag
===================================================================
--- ship.frag	(revision 0e0f851a9c036c86fd7761ec28ab1b72e823161f)
+++ ship.frag	(revision 0e0f851a9c036c86fd7761ec28ab1b72e823161f)
@@ -0,0 +1,52 @@
+#version 410
+
+in vec3 position_eye, normal_eye, color, light_position_eye, light2_position_eye;
+
+out vec4 frag_color;
+
+// fixed point light properties
+vec3 Ls = vec3(1.0, 1.0, 1.0);
+vec3 Ld = vec3(1.0, 1.0, 1.0);
+vec3 La = vec3(0.2, 0.2, 0.2);
+
+// surface reflectance
+vec3 Ks = vec3(1.0, 1.0, 1.0);
+vec3 Kd = vec3(1.0, 1.5, 1.0);
+vec3 Ka = vec3(0.2, 0.2, 0.2);
+float specular_exponent = 100.0; // specular 'power'
+
+void main() {
+  // ambient intensity
+  vec3 Ia = La * Ka;
+
+  // ambient intensity
+  vec3 Ia2 = La * Ka;
+
+  vec3 direction_to_light_eye = normalize(light_position_eye - position_eye);
+  float dot_prod = max(dot(direction_to_light_eye, normal_eye), 0.0);
+
+  // diffuse intensity
+  vec3 Id = Ld * color * dot_prod;
+
+  vec3 direction_to_light2_eye = normalize(light2_position_eye - position_eye);
+  float dot_prod2 = max(dot(direction_to_light2_eye, normal_eye), 0.0);
+
+  // diffuse intensity
+  vec3 Id2 = Ld * color * dot_prod2;
+
+  vec3 surface_to_viewer_eye = normalize(-position_eye);
+
+  vec3 reflection_eye = reflect(-direction_to_light_eye, normal_eye);
+  float dot_prod_specular = max(dot(reflection_eye, surface_to_viewer_eye), 0.0);
+  float specular_factor = pow(dot_prod_specular, specular_exponent);
+
+  vec3 reflection_eye2 = reflect(-direction_to_light2_eye, normal_eye);
+  float dot_prod_specular2 = max(dot(reflection_eye2, surface_to_viewer_eye), 0.0);
+  float specular_factor2 = pow(dot_prod_specular2, specular_exponent);
+
+  // specular intensity
+  vec3 Is = Ls * Ks * specular_factor;
+  vec3 Is2 = Ls * Ks * specular_factor2;
+
+  frag_color = vec4((Is + Id + Ia + Is2 + Id2 + Ia2)/2, 1.0);
+}
Index: ship.vert
===================================================================
--- ship.vert	(revision 0e0f851a9c036c86fd7761ec28ab1b72e823161f)
+++ ship.vert	(revision 0e0f851a9c036c86fd7761ec28ab1b72e823161f)
@@ -0,0 +1,30 @@
+#version 410
+
+#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 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, light2_position_eye;
+
+// fixed point light position
+vec3 light_position_world = vec3(0.0, 0.0, 2.0);
+vec3 light2_position_world = vec3(0.0, 1.5, -0.1);
+
+void main() {
+  position_eye = vec3(view * model_mats[ubo_index] * vec4(vertex_position, 1.0));
+  normal_eye = normalize(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));
+  light2_position_eye = vec3(view * vec4(light2_position_world, 1.0));
+
+  gl_Position = proj * vec4(position_eye, 1.0);
+}
