Index: NewOpenGLGame.vcxproj
===================================================================
--- NewOpenGLGame.vcxproj	(revision f71d87dc18c06463ff1d4281ff63beb34f582bf7)
+++ NewOpenGLGame.vcxproj	(revision db06984dd8b4a063db3a92fd593585c1fa2d7f26)
@@ -163,4 +163,6 @@
     <None Include="asteroid.frag" />
     <None Include="asteroid.vert" />
+    <None Include="explosion.frag" />
+    <None Include="explosion.vert" />
     <None Include="ship.frag" />
     <None Include="ship.vert" />
Index: explosion.frag
===================================================================
--- explosion.frag	(revision db06984dd8b4a063db3a92fd593585c1fa2d7f26)
+++ explosion.frag	(revision db06984dd8b4a063db3a92fd593585c1fa2d7f26)
@@ -0,0 +1,12 @@
+#version 410 core
+
+in float opacity;
+
+out vec4 frag_color;
+
+vec4 particle_color = vec4 (0.5, 0.5, 0.8, 0.8);
+
+void main() {
+   frag_color.a = opacity;
+   frag_color.rgb = particle_color.rgb;
+}
Index: explosion.vert
===================================================================
--- explosion.vert	(revision db06984dd8b4a063db3a92fd593585c1fa2d7f26)
+++ explosion.vert	(revision db06984dd8b4a063db3a92fd593585c1fa2d7f26)
@@ -0,0 +1,22 @@
+#version 410 core
+
+uniform float elapsed_system_time;
+
+layout (location = 0) in vec3 v_i; // initial velocity
+layout (location = 1) in float start_time;
+
+out float opacity;
+
+void main() {
+   float t = elapsed_system_time - start_time;
+   t = mod(t, 3.0); // allow time to loop around so particle emitter keeps going
+
+   vec3 p = vec3(0.0, 0.0, 0.0);
+   vec3 a = vec3(0.0, -1.0, 0.0); // gravity
+   p += v_i * t + 0.5 * a * t * t;
+
+   opacity = 1.0 - (t / 3.0);
+
+   gl_Position = vec4(p, 1.0);
+   gl_PointSize = 15.0; // size in pixels
+}
Index: new-game.cpp
===================================================================
--- new-game.cpp	(revision f71d87dc18c06463ff1d4281ff63beb34f582bf7)
+++ new-game.cpp	(revision db06984dd8b4a063db3a92fd593585c1fa2d7f26)
@@ -157,4 +157,6 @@
                   GLuint* model_mat_idx_vbo);
 
+GLuint initializeParticleEffectBuffers();
+
 void populateBuffers(vector<SceneObject*>& objects,
                   map<GLuint, BufferInfo>& shaderBufferInfo,
@@ -193,8 +195,10 @@
 
 void renderScene(map<GLuint, BufferInfo>& shaderBufferInfo,
-                  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 color_sp, GLuint asteroid_sp, GLuint texture_sp, GLuint laser_sp, GLuint explosion_sp,
+                  GLuint color_vao, GLuint asteroid_vao, GLuint texture_vao, GLuint laser_vao, GLuint explosion_vao,
                   GLuint colors_vbo, GLuint selected_colors_vbo,
                   SceneObject* selectedObject);
+void renderExplosion(GLuint explosion_sp, GLuint explosion_vao, GLuint tex);
+
 void renderSceneGui();
 
@@ -206,4 +210,5 @@
 const int KEY_STATE_UNCHANGED = -1;
 const bool FULLSCREEN = false;
+const int EXPLOSION_PARTICLE_COUNT = 300;
 unsigned int MAX_UNIFORMS = 0; // Requires OpenGL constants only available at runtime, so it can't be const
 
@@ -438,4 +443,5 @@
    GLuint texture_sp = loadShaderProgram("./texture.vert", "./texture.frag");
    GLuint laser_sp = loadShaderProgram("./laser.vert", "./laser.frag");
+   GLuint explosion_sp = loadShaderProgram("./explosion.vert", "./explosion.frag");
 
    shaderBufferInfo[color_sp] = BufferInfo();
@@ -558,4 +564,6 @@
    glVertexAttribIPointer(2, 1, GL_UNSIGNED_INT, 0, 0);
 
+   GLuint explosion_vao = initializeParticleEffectBuffers();
+
    float cam_speed = 1.0f;
    float cam_yaw_speed = 60.0f*ONE_DEG_IN_RAD;
@@ -600,5 +608,4 @@
    GLuint asteroid_proj_mat_loc = glGetUniformLocation(asteroid_sp, "proj");
    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");
@@ -610,4 +617,6 @@
    GLuint laser_color_loc = glGetUniformLocation(laser_sp, "laser_color");
    GLuint laser_sp_models_ub_index = glGetUniformBlockIndex(laser_sp, "models");
+
+   GLuint elapsed_system_time_loc = glGetUniformLocation(explosion_sp, "elapsed_system_time");
 
 
@@ -905,4 +914,7 @@
       }
 
+      glUseProgram(explosion_sp);
+      glUniform1f(elapsed_system_time_loc, (GLfloat)current_seconds);
+
       // Render scene
 
@@ -916,6 +928,6 @@
          case STATE_GAME:
             renderScene(shaderBufferInfo,
-               color_sp, asteroid_sp, texture_sp, laser_sp,
-               color_vao, asteroid_vao, texture_vao, laser_vao,
+               color_sp, asteroid_sp, texture_sp, laser_sp, explosion_sp,
+               color_vao, asteroid_vao, texture_vao, laser_vao, explosion_vao,
                colors_vbo, selected_colors_vbo,
                selectedObject);
@@ -1886,4 +1898,46 @@
 }
 
+GLuint initializeParticleEffectBuffers() {
+   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) * 1.0f - 0.5f;
+      float randz = ((float)rand() / (float)RAND_MAX) * 1.0f - 0.5f;
+      vv[i*3] = randx;
+      vv[i*3 + 1] = 1.0f;
+      vv[i*3 + 2] = randz;
+   }
+
+   GLuint velocity_vbo;
+   glGenBuffers(1, &velocity_vbo);
+   glBindBuffer(GL_ARRAY_BUFFER, velocity_vbo);
+   glBufferData(GL_ARRAY_BUFFER, sizeof(vv), vv, GL_STATIC_DRAW);
+
+   GLuint time_vbo;
+   glGenBuffers(1, &time_vbo);
+   glBindBuffer(GL_ARRAY_BUFFER, time_vbo);
+   glBufferData(GL_ARRAY_BUFFER, sizeof(vt), vt, GL_STATIC_DRAW);
+
+   GLuint vao;
+   glGenVertexArrays(1, &vao);
+   glBindVertexArray(vao);
+
+   glBindBuffer(GL_ARRAY_BUFFER, velocity_vbo);
+   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
+
+   glBindBuffer(GL_ARRAY_BUFFER, time_vbo);
+   glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, NULL);
+
+   glEnableVertexAttribArray(0);
+   glEnableVertexAttribArray(1);
+
+   return vao;
+}
+
 void populateBuffers(vector<SceneObject*>& objects,
                   map<GLuint, BufferInfo>& shaderBufferInfo,
@@ -2219,6 +2273,6 @@
 
 void renderScene(map<GLuint, BufferInfo>& shaderBufferInfo,
-                  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 color_sp, GLuint asteroid_sp, GLuint texture_sp, GLuint laser_sp, GLuint explosion_sp,
+                  GLuint color_vao, GLuint asteroid_vao, GLuint texture_vao, GLuint laser_vao, GLuint explosion_vao,
                   GLuint colors_vbo, GLuint selected_colors_vbo,
                   SceneObject* selectedObject) {
@@ -2259,4 +2313,22 @@
    glDrawArrays(GL_TRIANGLES, shaderBufferInfo[laser_sp].vbo_base, shaderBufferInfo[laser_sp].vbo_offset);
 
+   glDisable(GL_BLEND);
+
+   renderExplosion(explosion_sp, explosion_vao, 0);
+}
+
+void renderExplosion(GLuint explosion_sp, GLuint explosion_vao, GLuint tex) {
+   glEnable(GL_BLEND);
+   glEnable(GL_PROGRAM_POINT_SIZE);
+
+   //glActiveTexture(GL_TEXTURE1);
+   //glBindTexture(GL_TEXTURE_2D, tex);
+   glUseProgram(explosion_sp);
+
+   glBindVertexArray(explosion_vao);
+
+   glDrawArrays(GL_POINTS, 0, EXPLOSION_PARTICLE_COUNT);
+
+   glDisable(GL_PROGRAM_POINT_SIZE);
    glDisable(GL_BLEND);
 }
