Index: sdl-game.cpp
===================================================================
--- sdl-game.cpp	(revision c1ec4f63156230af7a921799acc46120ccc4b110)
+++ sdl-game.cpp	(revision 6486ba8f42f8ee2b61912e3a96ac6921ee30292b)
@@ -111,6 +111,13 @@
 
    createBufferSet(sizeof(UBO_VP_mats),
-      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBuffers_modelPipeline);
+                   VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   uniformBuffers_modelPipeline);
+
+   createBufferSet(objects_modelPipeline.capacity * sizeof(SSBO_ModelObject),
+                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
+                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   storageBuffers_modelPipeline);
 
    modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
@@ -121,15 +128,15 @@
       VK_SHADER_STAGE_FRAGMENT_BIT, &floorTextureImageDescriptor);
 
-   SceneObject<ModelVertex, SSBO_ModelObject>* texturedSquare = nullptr;
+   SceneObject<ModelVertex>* texturedSquare = nullptr;
 
    texturedSquare = &addObject(modelObjects, modelPipeline,
       addObjectIndex<ModelVertex>(modelObjects.size(),
          addVertexNormals<ModelVertex>({
-            {{-0.5f, -0.5f,  0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, 0},
-            {{ 0.5f, -0.5f,  0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, 0},
-            {{ 0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, 0},
-            {{ 0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, 0},
-            {{-0.5f,  0.5f,  0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, 0},
-            {{-0.5f, -0.5f,  0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, 0}
+            {{-0.5f, -0.5f,  0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
+            {{ 0.5f, -0.5f,  0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
+            {{ 0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
+            {{ 0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
+            {{-0.5f,  0.5f,  0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
+            {{-0.5f, -0.5f,  0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}}
          })),
       {
@@ -264,10 +271,4 @@
       VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
       { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, 16, 24);
-
-   createBufferSet(objects_modelPipeline.capacity * sizeof(SSBO_ModelObject),
-                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
-                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
-                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-                   storageBuffers_modelPipeline);
 }
 
@@ -362,20 +363,19 @@
                   float zOffset = -2.0f + (0.5f * modelObjects.size());
 
-                  SceneObject<ModelVertex, SSBO_ModelObject>& texturedSquare =
-                     addObject(modelObjects, modelPipeline,
-                        addObjectIndex<ModelVertex>(modelObjects.size(),
-                           addVertexNormals<ModelVertex>({
-                              {{-0.5f, -0.5f,  0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, 0},
-                              {{ 0.5f, -0.5f,  0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, 0},
-                              {{ 0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, 0},
-                              {{ 0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, 0},
-                              {{-0.5f,  0.5f,  0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, 0},
-                              {{-0.5f, -0.5f,  0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, 0}
-                           })),
-                        {
-                           0, 1, 2, 3, 4, 5
-                        }, objects_modelPipeline, {
-                           mat4(1.0f)
-                        });
+                  SceneObject<ModelVertex>& texturedSquare = addObject(modelObjects, modelPipeline,
+                     addObjectIndex<ModelVertex>(modelObjects.size(),
+                        addVertexNormals<ModelVertex>({
+                           {{-0.5f, -0.5f,  0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
+                           {{ 0.5f, -0.5f,  0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
+                           {{ 0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
+                           {{ 0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
+                           {{-0.5f,  0.5f,  0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
+                           {{-0.5f, -0.5f,  0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}}
+                        })),
+                     {
+                        0, 1, 2, 3, 4, 5
+                     }, objects_modelPipeline, {
+                        mat4(1.0f)
+                     });
 
                   texturedSquare.model_base =
@@ -455,6 +455,6 @@
 
    for (size_t i = 0; i < modelObjects.size(); i++) {
-      SceneObject<ModelVertex, SSBO_ModelObject>& obj = modelObjects[i];
-      SSBO_ModelObject& objData = obj.ssbo;
+      SceneObject<ModelVertex>& obj = modelObjects[i];
+      SSBO_ModelObject& objData = objects_modelPipeline.get(i);
 
       // Rotate the textured squares
@@ -488,9 +488,4 @@
 
    modelPipeline.cleanupBuffers();
-
-   for (size_t i = 0; i < storageBuffers_modelPipeline.buffers.size(); i++) {
-      vkDestroyBuffer(device, storageBuffers_modelPipeline.buffers[i], nullptr);
-      vkFreeMemory(device, storageBuffers_modelPipeline.memory[i], nullptr);
-   }
 
    // END UNREVIEWED SECTION
@@ -1279,11 +1274,14 @@
    createSyncObjects();
 
-   // TODO: Move UBO creation/management into GraphicsPipeline_Vulkan, like I did with SSBOs
-   // TODO: Check if the shader stages and maybe some other properties of the pipeline can be re-used
-   // instead of recreated every time
-
    createBufferSet(sizeof(UBO_VP_mats),
-      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBuffers_modelPipeline);
+                   VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   uniformBuffers_modelPipeline);
+
+   createBufferSet(objects_modelPipeline.memorySize(),
+                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
+                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   storageBuffers_modelPipeline);
 
    modelPipeline.updateRenderPass(renderPass);
@@ -1312,4 +1310,9 @@
       vkDestroyBuffer(device, uniformBuffers_modelPipeline.buffers[i], nullptr);
       vkFreeMemory(device, uniformBuffers_modelPipeline.memory[i], nullptr);
+   }
+
+   for (size_t i = 0; i < storageBuffers_modelPipeline.buffers.size(); i++) {
+      vkDestroyBuffer(device, storageBuffers_modelPipeline.buffers[i], nullptr);
+      vkFreeMemory(device, storageBuffers_modelPipeline.memory[i], nullptr);
    }
 
Index: sdl-game.hpp
===================================================================
--- sdl-game.hpp	(revision c1ec4f63156230af7a921799acc46120ccc4b110)
+++ sdl-game.hpp	(revision 6486ba8f42f8ee2b61912e3a96ac6921ee30292b)
@@ -93,9 +93,8 @@
 // TODO: Create a typedef for index type so I can easily change uin16_t to something else later
 // TODO: Maybe create a typedef for each of the templated SceneObject types
-template<class VertexType, class SSBOType>
+template<class VertexType>
 struct SceneObject {
    vector<VertexType> vertices;
    vector<uint16_t> indices;
-   SSBOType ssbo;
 
    mat4 model_base;
@@ -109,8 +108,8 @@
    // Move the targetAsteroid stuff out of this class since it is very specific to lasers
    // and makes moving SceneObject into its own header file more problematic
-   SceneObject<ModelVertex, SSBO_Asteroid>* targetAsteroid; // currently only used for lasers
-};
-
-// TODO: Have to figure out how to include an optional ssbo parameter for each object
+   SceneObject<ModelVertex>* targetAsteroid; // currently only used for lasers
+};
+
+// TODO: Figure out how to include an optional ssbo parameter for each object
 // Could probably use the same approach to make indices optional
 // Figure out if there are sufficient use cases to make either of these optional or is it fine to make
@@ -238,5 +237,5 @@
       // if there is a need to add other uniform variables to one or more of the shaders
 
-      vector<SceneObject<ModelVertex, SSBO_ModelObject>> modelObjects;
+      vector<SceneObject<ModelVertex>> modelObjects;
 
       UBO_VP_mats object_VP_mats;
@@ -307,8 +306,8 @@
       // stop using objects.back() to access the object that was just created
       template<class VertexType, class SSBOType>
-      SceneObject<VertexType, SSBOType>& addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
-                                                   GraphicsPipeline_Vulkan<VertexType>& pipeline,
-                                                   const vector<VertexType>& vertices, vector<uint16_t> indices,
-                                                   VulkanBuffer<SSBOType>& objectBuffer, SSBOType ssbo);
+      SceneObject<VertexType>& addObject(vector<SceneObject<VertexType>>& objects,
+                                         GraphicsPipeline_Vulkan<VertexType>& pipeline,
+                                         const vector<VertexType>& vertices, vector<uint16_t> indices,
+                                         VulkanBuffer<SSBOType>& objectBuffer, SSBOType ssbo);
 
       template<class VertexType>
@@ -318,6 +317,6 @@
       vector<VertexType> addVertexNormals(vector<VertexType> vertices);
 
-      template<class VertexType, class SSBOType>
-      void centerObject(SceneObject<VertexType, SSBOType>& object);
+      template<class VertexType>
+      void centerObject(SceneObject<VertexType>& object);
 
       void renderFrame(ImDrawData* draw_data);
@@ -352,8 +351,8 @@
 // Figure out a better way to allow the model matrix to be set during object creation
 template<class VertexType, class SSBOType>
-SceneObject<VertexType, SSBOType>& VulkanGame::addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
-                                                         GraphicsPipeline_Vulkan<VertexType>& pipeline,
-                                                         const vector<VertexType>& vertices, vector<uint16_t> indices,
-                                                         VulkanBuffer<SSBOType>& objectBuffer, SSBOType ssbo) {
+SceneObject<VertexType>& VulkanGame::addObject(vector<SceneObject<VertexType>>& objects,
+                                               GraphicsPipeline_Vulkan<VertexType>& pipeline,
+                                               const vector<VertexType>& vertices, vector<uint16_t> indices,
+                                               VulkanBuffer<SSBOType>& objectBuffer, SSBOType ssbo) {
    // TODO: Use the model field of ssbo to set the object's model_base
    // currently, the passed-in model is useless since it gets overridden when ssbo.model is recalculated
@@ -364,8 +363,8 @@
    }
 
-   objects.push_back({ vertices, indices, ssbo, mat4(1.0f), mat4(1.0f) });
+   objects.push_back({ vertices, indices, mat4(1.0f), mat4(1.0f) });
    objectBuffer.add(ssbo);
 
-   SceneObject<VertexType, SSBOType>& obj = objects.back();
+   SceneObject<VertexType>& obj = objects.back();
 
    // TODO: Specify whether to center the object outside of this function or, worst case, maybe
@@ -410,6 +409,6 @@
 }
 
-template<class VertexType, class SSBOType>
-void VulkanGame::centerObject(SceneObject<VertexType, SSBOType>& object) {
+template<class VertexType>
+void VulkanGame::centerObject(SceneObject<VertexType>& object) {
    vector<VertexType>& vertices = object.vertices;
 
Index: vulkan-buffer.hpp
===================================================================
--- vulkan-buffer.hpp	(revision c1ec4f63156230af7a921799acc46120ccc4b110)
+++ vulkan-buffer.hpp	(revision 6486ba8f42f8ee2b61912e3a96ac6921ee30292b)
@@ -33,4 +33,5 @@
       void resize();
 
+      T& get(uint32_t index);
       void add(T obj);
 
@@ -41,14 +42,12 @@
       size_t alignment;
       size_t range;
+      //size_t capacity;
       size_t numObjects;
 
-      T* srcData; // TODO: Rename this to something else probably and rename rawData to data
-      vector<T>* vData;
-
-      // Remember that this is a pointer to the mapped video memory
-      // Maybe rename it to mappedData or something to make that clearer
-      void* rawData;
+      T* rawData;
+      vector<void*> mappedData;
 
       size_t memRequirement(size_t capacity);
+      size_t memOffset(uint32_t index);
 };
 
@@ -73,7 +72,6 @@
                               , numObjects(0)
                               , resized(false)
-                              , srcData(nullptr)
                               , rawData(nullptr)
-                              , vData(nullptr) {
+                              , mappedData() {
 }
 
@@ -85,12 +83,11 @@
                               , numObjects(0)
                               , resized(false)
-                              , srcData(nullptr)
                               , rawData(nullptr)
-                              , vData(nullptr) {
+                              , mappedData() {
    if (minOffsetAlignment > 0) {
       alignment = (alignment + minOffsetAlignment - 1) & ~(minOffsetAlignment - 1);
    }
 
-   srcData = (T*)malloc(memRequirement(capacity));
+   rawData = (T*)malloc(memRequirement(capacity));
 }
 
@@ -102,6 +99,6 @@
 template<class T>
 VulkanBuffer<T>::~VulkanBuffer() {
-   if (srcData != nullptr) {
-      free(srcData);
+   if (rawData != nullptr) {
+      free(rawData);
    }
 }
@@ -117,9 +114,9 @@
       range = other.range;
 
-      if (srcData != nullptr) {
-         free(srcData);
+      if (rawData != nullptr) {
+         free(rawData);
       }
 
-      srcData = other.srcData;
+      rawData = other.rawData;
 
       other.capacity = 0;
@@ -127,5 +124,5 @@
       other.range = 0;
 
-      other.srcData = nullptr;
+      other.rawData = nullptr;
    }
 
@@ -139,4 +136,12 @@
 
 template<class T>
+T& VulkanBuffer<T>::get(uint32_t index) {
+   // TODO: Check that index < numObjects
+
+   T* obj = (T*)((size_t)rawData + memOffset(index));
+   return *obj;
+}
+
+template<class T>
 void VulkanBuffer<T>::add(T obj) {
    if (numObjects == capacity) {
@@ -144,6 +149,21 @@
       resized = true;
 
+      size_t oldMemReq = memRequirement(capacity);
+
       capacity *= 2;
+
+      size_t newMemReq = memRequirement(capacity);
+
+      T* newData = (T*)malloc(newMemReq);
+      // TODO: Check for failure
+
+      memcpy(newData, rawData, oldMemReq);
+
+      free(rawData);
+      rawData = newData;
    }
+
+   T* ptr = (T*)((size_t)rawData + memOffset(numObjects));
+   *ptr = obj;
 
    numObjects++;
@@ -160,3 +180,8 @@
 }
 
+template<class T>
+size_t VulkanBuffer<T>::memOffset(uint32_t index) {
+   return (index / range) * alignment + (index % range) * sizeof(T);
+}
+
 #endif // _VULKAN_BUFFER_H
Index: vulkan-game.cpp
===================================================================
--- vulkan-game.cpp	(revision c1ec4f63156230af7a921799acc46120ccc4b110)
+++ vulkan-game.cpp	(revision 6486ba8f42f8ee2b61912e3a96ac6921ee30292b)
@@ -139,6 +139,13 @@
 
    createBufferSet(sizeof(UBO_VP_mats),
-      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBuffers_modelPipeline);
+                   VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   uniformBuffers_modelPipeline);
+
+   createBufferSet(objects_modelPipeline.memorySize(),
+                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
+                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   storageBuffers_modelPipeline);
 
    modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
@@ -149,5 +156,5 @@
       VK_SHADER_STAGE_FRAGMENT_BIT, &floorTextureImageDescriptor);
 
-   SceneObject<ModelVertex, SSBO_ModelObject>* texturedSquare = nullptr;
+   SceneObject<ModelVertex>* texturedSquare = nullptr;
 
    texturedSquare = &addObject(modelObjects, modelPipeline,
@@ -202,6 +209,13 @@
 
    createBufferSet(sizeof(UBO_VP_mats),
-      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBuffers_shipPipeline);
+                   VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   uniformBuffers_shipPipeline);
+
+   createBufferSet(objects_shipPipeline.memorySize(),
+                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
+                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   storageBuffers_shipPipeline);
 
    shipPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
@@ -212,5 +226,5 @@
    // TODO: With the normals, indexing basically becomes pointless since no vertices will have exactly
    // the same data. Add an option to make some pipelines not use indexing
-   SceneObject<ModelVertex, SSBO_ModelObject>& ship = addObject(shipObjects, shipPipeline,
+   SceneObject<ModelVertex>& ship = addObject(shipObjects, shipPipeline,
       addObjectIndex<ModelVertex>(shipObjects.size(),
          addVertexNormals<ModelVertex>({
@@ -462,6 +476,13 @@
 
    createBufferSet(sizeof(UBO_VP_mats),
-      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBuffers_asteroidPipeline);
+                   VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   uniformBuffers_asteroidPipeline);
+
+   createBufferSet(objects_asteroidPipeline.memorySize(),
+                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
+                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   storageBuffers_asteroidPipeline);
 
    asteroidPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
@@ -480,6 +501,13 @@
 
    createBufferSet(sizeof(UBO_VP_mats),
-      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBuffers_laserPipeline);
+                   VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   uniformBuffers_laserPipeline);
+
+   createBufferSet(objects_laserPipeline.memorySize(),
+                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
+                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   storageBuffers_laserPipeline);
 
    laserPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
@@ -500,6 +528,13 @@
 
    createBufferSet(sizeof(UBO_Explosion),
-      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBuffers_explosionPipeline);
+                   VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   uniformBuffers_explosionPipeline);
+
+   createBufferSet(objects_explosionPipeline.memorySize(),
+                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
+                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   storageBuffers_explosionPipeline);
 
    explosionPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
@@ -615,39 +650,15 @@
       { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, 24, 24);
 
-   createBufferSet(objects_modelPipeline.capacity * sizeof(SSBO_ModelObject),
-                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
-                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
-                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-                   storageBuffers_modelPipeline);
-
    shipPipeline = GraphicsPipeline_Vulkan<ModelVertex>(
       VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
       { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, 138, 138);
 
-   createBufferSet(objects_shipPipeline.capacity * sizeof(SSBO_ModelObject),
-                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
-                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
-                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-                   storageBuffers_shipPipeline);
-
    asteroidPipeline = GraphicsPipeline_Vulkan<ModelVertex>(
       VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
       { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, 24, 36);
 
-   createBufferSet(objects_asteroidPipeline.capacity * sizeof(SSBO_Asteroid),
-                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
-                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
-                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-                   storageBuffers_asteroidPipeline);
-
    laserPipeline = GraphicsPipeline_Vulkan<LaserVertex>(
       VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
       { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, 8, 18);
-
-   createBufferSet(objects_laserPipeline.capacity * sizeof(SSBO_Laser),
-                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
-                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
-                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-                   storageBuffers_laserPipeline);
 
    explosionPipeline = GraphicsPipeline_Vulkan<ExplosionVertex>(
@@ -655,10 +666,4 @@
       { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height },
       EXPLOSION_PARTICLE_COUNT, EXPLOSION_PARTICLE_COUNT);
-
-   createBufferSet(objects_explosionPipeline.capacity * sizeof(SSBO_Explosion),
-                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
-                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
-                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-                   storageBuffers_explosionPipeline);
 }
 
@@ -767,20 +772,19 @@
                   float zOffset = -2.0f + (0.5f * modelObjects.size());
 
-                  SceneObject<ModelVertex, SSBO_ModelObject>& texturedSquare =
-                     addObject(modelObjects, modelPipeline,
-                        addObjectIndex<ModelVertex>(modelObjects.size(),
-                           addVertexNormals<ModelVertex>({
-                              {{-0.5f, -0.5f,  0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
-                              {{ 0.5f, -0.5f,  0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
-                              {{ 0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
-                              {{ 0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
-                              {{-0.5f,  0.5f,  0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
-                              {{-0.5f, -0.5f,  0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}}
-                           })),
-                        {
-                           0, 1, 2, 3, 4, 5
-                        }, objects_modelPipeline, {
-                           mat4(1.0f)
-                        });
+                  SceneObject<ModelVertex>& texturedSquare = addObject(modelObjects, modelPipeline,
+                     addObjectIndex<ModelVertex>(modelObjects.size(),
+                        addVertexNormals<ModelVertex>({
+                           {{-0.5f, -0.5f,  0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
+                           {{ 0.5f, -0.5f,  0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
+                           {{ 0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
+                           {{ 0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
+                           {{-0.5f,  0.5f,  0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
+                           {{-0.5f, -0.5f,  0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}}
+                        })),
+                     {
+                        0, 1, 2, 3, 4, 5
+                     }, objects_modelPipeline, {
+                        mat4(1.0f)
+                     });
 
                   texturedSquare.model_base =
@@ -817,5 +821,5 @@
                // START UNREVIEWED SECTION
                if (e.key.keycode == SDL_SCANCODE_Z && leftLaserIdx != -1) {
-                  laserObjects[leftLaserIdx].ssbo.deleted = true;
+                  objects_laserPipeline.get(leftLaserIdx).deleted = true;
                   leftLaserIdx = -1;
 
@@ -825,5 +829,5 @@
                   }
                } else if (e.key.keycode == SDL_SCANCODE_X && rightLaserIdx != -1) {
-                  laserObjects[rightLaserIdx].ssbo.deleted = true;
+                  objects_laserPipeline.get(rightLaserIdx).deleted = true;
                   rightLaserIdx = -1;
 
@@ -857,5 +861,5 @@
       // Check which keys are held down
 
-      SceneObject<ModelVertex, SSBO_ModelObject>& ship = shipObjects[0];
+      SceneObject<ModelVertex>& ship = shipObjects[0];
 
       if (gui->keyPressed(SDL_SCANCODE_LEFT)) {
@@ -936,72 +940,71 @@
 // where it will run just once per frame
 void VulkanGame::updateScene() {
-   if (curTime - this->lastSpawn_asteroid > this->spawnRate_asteroid) {
-      this->lastSpawn_asteroid = curTime;
-
-      SceneObject<ModelVertex, SSBO_Asteroid>& asteroid =
-         addObject(asteroidObjects, asteroidPipeline,
-            addObjectIndex<ModelVertex>(asteroidObjects.size(),
-               addVertexNormals<ModelVertex>({
-
-                  // front
-                  {{ 1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{ 1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{ 1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-
-                  // top
-                  {{ 1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{ 1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{ 1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-
-                  // bottom
-                  {{ 1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{ 1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{ 1.0f, -1.0f, -1.0}, {0.4f, 0.4f, 0.4f}},
-
-                  // back
-                  {{ 1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{ 1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{ 1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-
-                  // right
-                  {{ 1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{ 1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{ 1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{ 1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{ 1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{ 1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-
-                  // left
-                  {{-1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
-                  {{-1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
-               })),
-            {
-                0,  1,  2,  3,  4,  5,
-                6,  7,  8,  9, 10, 11,
-               12, 13, 14, 15, 16, 17,
-               18, 19, 20, 21, 22, 23,
-               24, 25, 26, 27, 28, 29,
-               30, 31, 32, 33, 34, 35,
-            }, objects_asteroidPipeline, {
-               mat4(1.0f),
-               10.0f,
-               false
-            });
+   if (curTime - lastSpawn_asteroid > spawnRate_asteroid) {
+      lastSpawn_asteroid = curTime;
+
+      SceneObject<ModelVertex>& asteroid = addObject(asteroidObjects, asteroidPipeline,
+         addObjectIndex<ModelVertex>(asteroidObjects.size(),
+            addVertexNormals<ModelVertex>({
+
+               // front
+               {{ 1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{ 1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{ 1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+
+               // top
+               {{ 1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{ 1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{ 1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+
+               // bottom
+               {{ 1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{ 1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{ 1.0f, -1.0f, -1.0}, {0.4f, 0.4f, 0.4f}},
+
+               // back
+               {{ 1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{ 1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{ 1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+
+               // right
+               {{ 1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{ 1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{ 1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{ 1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{ 1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{ 1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+
+               // left
+               {{-1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f,  1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f,  1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
+               {{-1.0f, -1.0f,  1.0f}, {0.4f, 0.4f, 0.4f}},
+            })),
+         {
+             0,  1,  2,  3,  4,  5,
+             6,  7,  8,  9, 10, 11,
+            12, 13, 14, 15, 16, 17,
+            18, 19, 20, 21, 22, 23,
+            24, 25, 26, 27, 28, 29,
+            30, 31, 32, 33, 34, 35,
+         }, objects_asteroidPipeline, {
+            mat4(1.0f),
+            10.0f,
+            false
+         });
 
       // This accounts for the scaling in model_base.
@@ -1033,6 +1036,6 @@
 
    for (size_t i = 0; i < modelObjects.size(); i++) {
-      SceneObject<ModelVertex, SSBO_ModelObject>& obj = modelObjects[i];
-      SSBO_ModelObject& objData = obj.ssbo;
+      SceneObject<ModelVertex>& obj = modelObjects[i];
+      SSBO_ModelObject& objData = objects_modelPipeline.get(i);
 
       // Rotate the textured squares
@@ -1058,6 +1061,6 @@
    // TODO: Move ship position updates from the ui event handling code into this function
    for (size_t i = 0; i < shipObjects.size(); i++) {
-      SceneObject<ModelVertex, SSBO_ModelObject>& obj = shipObjects[i];
-      SSBO_ModelObject& objData = obj.ssbo;
+      SceneObject<ModelVertex>& obj = shipObjects[i];
+      SSBO_ModelObject& objData = objects_shipPipeline.get(i);
 
       objData.model = obj.model_transform * obj.model_base;
@@ -1077,6 +1080,6 @@
 
    for (size_t i = 0; i < asteroidObjects.size(); i++) {
-      SceneObject<ModelVertex, SSBO_Asteroid>& obj = asteroidObjects[i];
-      SSBO_Asteroid& objData = obj.ssbo;
+      SceneObject<ModelVertex>& obj = asteroidObjects[i];
+      SSBO_Asteroid& objData = objects_asteroidPipeline.get(i);
 
       if (!objData.deleted) {
@@ -1127,6 +1130,6 @@
 
    for (size_t i = 0; i < laserObjects.size(); i++) {
-      SceneObject<LaserVertex, SSBO_Laser>& obj = laserObjects[i];
-      SSBO_Laser& objData = obj.ssbo;
+      SceneObject<LaserVertex>& obj = laserObjects[i];
+      SSBO_Laser& objData = objects_laserPipeline.get(i);
 
       objData.model = obj.model_transform * obj.model_base;
@@ -1146,6 +1149,6 @@
 
    for (size_t i = 0; i < explosionObjects.size(); i++) {
-      SceneObject<ExplosionVertex, SSBO_Explosion>& obj = explosionObjects[i];
-      SSBO_Explosion& objData = obj.ssbo;
+      SceneObject<ExplosionVertex>& obj = explosionObjects[i];
+      SSBO_Explosion& objData = objects_explosionPipeline.get(i);
 
       if (!objData.deleted) {
@@ -1212,29 +1215,4 @@
    laserPipeline.cleanupBuffers();
    explosionPipeline.cleanupBuffers();
-
-   for (size_t i = 0; i < storageBuffers_modelPipeline.buffers.size(); i++) {
-      vkDestroyBuffer(device, storageBuffers_modelPipeline.buffers[i], nullptr);
-      vkFreeMemory(device, storageBuffers_modelPipeline.memory[i], nullptr);
-   }
-
-   for (size_t i = 0; i < storageBuffers_shipPipeline.buffers.size(); i++) {
-      vkDestroyBuffer(device, storageBuffers_shipPipeline.buffers[i], nullptr);
-      vkFreeMemory(device, storageBuffers_shipPipeline.memory[i], nullptr);
-   }
-
-   for (size_t i = 0; i < storageBuffers_asteroidPipeline.buffers.size(); i++) {
-      vkDestroyBuffer(device, storageBuffers_asteroidPipeline.buffers[i], nullptr);
-      vkFreeMemory(device, storageBuffers_asteroidPipeline.memory[i], nullptr);
-   }
-
-   for (size_t i = 0; i < storageBuffers_laserPipeline.buffers.size(); i++) {
-      vkDestroyBuffer(device, storageBuffers_laserPipeline.buffers[i], nullptr);
-      vkFreeMemory(device, storageBuffers_laserPipeline.memory[i], nullptr);
-   }
-
-   for (size_t i = 0; i < storageBuffers_explosionPipeline.buffers.size(); i++) {
-      vkDestroyBuffer(device, storageBuffers_explosionPipeline.buffers[i], nullptr);
-      vkFreeMemory(device, storageBuffers_explosionPipeline.memory[i], nullptr);
-   }
 
    // END UNREVIEWED SECTION
@@ -2004,6 +1982,5 @@
    float length = glm::length(ray);
 
-   SceneObject<LaserVertex, SSBO_Laser>& laser = addObject(
-      laserObjects, laserPipeline,
+   SceneObject<LaserVertex>& laser = addObject(laserObjects, laserPipeline,
       addObjectIndex<LaserVertex>(laserObjects.size(), {
          {{ width / 2, 0.0f, -width / 2         }, {1.0f, 0.5f }},
@@ -2051,5 +2028,5 @@
 
 void VulkanGame::translateLaser(size_t index, const vec3& translation) {
-   SceneObject<LaserVertex, SSBO_Laser>& laser = this->laserObjects[index];
+   SceneObject<LaserVertex>& laser = laserObjects[index];
 
    // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
@@ -2081,5 +2058,5 @@
 
 void VulkanGame::updateLaserTarget(size_t index) {
-   SceneObject<LaserVertex, SSBO_Laser>& laser = this->laserObjects[index];
+   SceneObject<LaserVertex>& laser = laserObjects[index];
 
    // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
@@ -2090,10 +2067,10 @@
 
    vec3 intersection(0.0f), closestIntersection(0.0f);
-   SceneObject<ModelVertex, SSBO_Asteroid>* closestAsteroid = nullptr;
+   SceneObject<ModelVertex>* closestAsteroid = nullptr;
    unsigned int closestAsteroidIndex = -1;
 
-   for (int i = 0; i < this->asteroidObjects.size(); i++) {
-      if (!this->asteroidObjects[i].ssbo.deleted &&
-            this->getLaserAndAsteroidIntersection(this->asteroidObjects[i], start, end, intersection)) {
+   for (int i = 0; i < asteroidObjects.size(); i++) {
+      if (!objects_asteroidPipeline.get(i).deleted &&
+          getLaserAndAsteroidIntersection(asteroidObjects[i], start, end, intersection)) {
          // TODO: Implement a more generic algorithm for testing the closest object by getting the distance between the points
          // TODO: Also check which intersection is close to the start of the laser. This would make the algorithm work
@@ -2119,10 +2096,10 @@
       }
 
-      EffectOverTime<ModelVertex, SSBO_Asteroid>* eot = nullptr;
+      EffectOverTime<SSBO_Asteroid>* eot = nullptr;
 
       if (closestAsteroid != nullptr) {
-         // TODO: Use some sort of smart pointer instead
-         eot = new EffectOverTime<ModelVertex, SSBO_Asteroid>(asteroidPipeline, asteroidObjects, closestAsteroidIndex,
-            offset_of(&SSBO_Asteroid::hp), curTime, -20.0f);
+         // TODO: Figure out if I should use a smart pointer instead
+         eot = new EffectOverTime(objects_asteroidPipeline, closestAsteroidIndex, offset_of(&SSBO_Asteroid::hp),
+                                  curTime, -20.0f);
          effects.push_back(eot);
       }
@@ -2146,11 +2123,11 @@
 
    // TODO: Consider if I want to set a flag and do this update in updateScene() instead
-   updateObjectVertices(this->laserPipeline, laser, index);
+   updateObjectVertices(laserPipeline, laser, index);
 }
 
 // TODO: Determine if I should pass start and end by reference or value since they don't get changed
 // Probably use const reference
-bool VulkanGame::getLaserAndAsteroidIntersection(SceneObject<ModelVertex, SSBO_Asteroid>& asteroid,
-      vec3& start, vec3& end, vec3& intersection) {
+bool VulkanGame::getLaserAndAsteroidIntersection(SceneObject<ModelVertex>& asteroid, vec3& start, vec3& end,
+                                                 vec3& intersection) {
    /*
    ### LINE EQUATIONS ###
@@ -2221,5 +2198,5 @@
    iota(indices.begin(), indices.end(), 0);
 
-   SceneObject<ExplosionVertex, SSBO_Explosion>& explosion = addObject(explosionObjects, explosionPipeline,
+   SceneObject<ExplosionVertex>& explosion = addObject(explosionObjects, explosionPipeline,
       addObjectIndex(explosionObjects.size(), vertices), indices, objects_explosionPipeline, {
          mat4(1.0f),
@@ -2254,11 +2231,14 @@
    createSyncObjects();
 
-   // TODO: Move UBO creation/management into GraphicsPipeline_Vulkan, like I did with SSBOs
-   // TODO: Check if the shader stages and maybe some other properties of the pipeline can be re-used
-   // instead of recreated every time
-
    createBufferSet(sizeof(UBO_VP_mats),
-      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBuffers_modelPipeline);
+                   VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   uniformBuffers_modelPipeline);
+
+   createBufferSet(objects_modelPipeline.memorySize(),
+                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
+                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   storageBuffers_modelPipeline);
 
    modelPipeline.updateRenderPass(renderPass);
@@ -2268,6 +2248,13 @@
 
    createBufferSet(sizeof(UBO_VP_mats),
-      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBuffers_shipPipeline);
+                   VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   uniformBuffers_shipPipeline);
+
+   createBufferSet(objects_shipPipeline.memorySize(),
+                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
+                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   storageBuffers_shipPipeline);
 
    shipPipeline.updateRenderPass(renderPass);
@@ -2277,6 +2264,13 @@
 
    createBufferSet(sizeof(UBO_VP_mats),
-      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBuffers_asteroidPipeline);
+                   VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   uniformBuffers_asteroidPipeline);
+
+   createBufferSet(objects_asteroidPipeline.memorySize(),
+                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
+                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   storageBuffers_asteroidPipeline);
 
    asteroidPipeline.updateRenderPass(renderPass);
@@ -2286,6 +2280,13 @@
 
    createBufferSet(sizeof(UBO_VP_mats),
-      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBuffers_laserPipeline);
+                   VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   uniformBuffers_laserPipeline);
+
+   createBufferSet(objects_laserPipeline.memorySize(),
+                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
+                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   storageBuffers_laserPipeline);
 
    laserPipeline.updateRenderPass(renderPass);
@@ -2295,6 +2296,13 @@
 
    createBufferSet(sizeof(UBO_Explosion),
-      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBuffers_explosionPipeline);
+                   VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   uniformBuffers_explosionPipeline);
+
+   createBufferSet(objects_explosionPipeline.memorySize(),
+                   VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
+                   | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+                   VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+                   storageBuffers_explosionPipeline);
 
    explosionPipeline.updateRenderPass(renderPass);
@@ -2329,4 +2337,9 @@
    }
 
+   for (size_t i = 0; i < storageBuffers_modelPipeline.buffers.size(); i++) {
+      vkDestroyBuffer(device, storageBuffers_modelPipeline.buffers[i], nullptr);
+      vkFreeMemory(device, storageBuffers_modelPipeline.memory[i], nullptr);
+   }
+
    for (size_t i = 0; i < uniformBuffers_shipPipeline.buffers.size(); i++) {
       vkDestroyBuffer(device, uniformBuffers_shipPipeline.buffers[i], nullptr);
@@ -2334,4 +2347,9 @@
    }
 
+   for (size_t i = 0; i < storageBuffers_shipPipeline.buffers.size(); i++) {
+      vkDestroyBuffer(device, storageBuffers_shipPipeline.buffers[i], nullptr);
+      vkFreeMemory(device, storageBuffers_shipPipeline.memory[i], nullptr);
+   }
+
    for (size_t i = 0; i < uniformBuffers_asteroidPipeline.buffers.size(); i++) {
       vkDestroyBuffer(device, uniformBuffers_asteroidPipeline.buffers[i], nullptr);
@@ -2339,4 +2357,9 @@
    }
 
+   for (size_t i = 0; i < storageBuffers_asteroidPipeline.buffers.size(); i++) {
+      vkDestroyBuffer(device, storageBuffers_asteroidPipeline.buffers[i], nullptr);
+      vkFreeMemory(device, storageBuffers_asteroidPipeline.memory[i], nullptr);
+   }
+
    for (size_t i = 0; i < uniformBuffers_laserPipeline.buffers.size(); i++) {
       vkDestroyBuffer(device, uniformBuffers_laserPipeline.buffers[i], nullptr);
@@ -2344,7 +2367,17 @@
    }
 
+   for (size_t i = 0; i < storageBuffers_laserPipeline.buffers.size(); i++) {
+      vkDestroyBuffer(device, storageBuffers_laserPipeline.buffers[i], nullptr);
+      vkFreeMemory(device, storageBuffers_laserPipeline.memory[i], nullptr);
+   }
+
    for (size_t i = 0; i < uniformBuffers_explosionPipeline.buffers.size(); i++) {
       vkDestroyBuffer(device, uniformBuffers_explosionPipeline.buffers[i], nullptr);
       vkFreeMemory(device, uniformBuffers_explosionPipeline.memory[i], nullptr);
+   }
+
+   for (size_t i = 0; i < storageBuffers_explosionPipeline.buffers.size(); i++) {
+      vkDestroyBuffer(device, storageBuffers_explosionPipeline.buffers[i], nullptr);
+      vkFreeMemory(device, storageBuffers_explosionPipeline.memory[i], nullptr);
    }
 
Index: vulkan-game.hpp
===================================================================
--- vulkan-game.hpp	(revision c1ec4f63156230af7a921799acc46120ccc4b110)
+++ vulkan-game.hpp	(revision 6486ba8f42f8ee2b61912e3a96ac6921ee30292b)
@@ -109,9 +109,8 @@
 // TODO: Create a typedef for index type so I can easily change uin16_t to something else later
 // TODO: Maybe create a typedef for each of the templated SceneObject types
-template<class VertexType, class SSBOType>
+template<class VertexType>
 struct SceneObject {
    vector<VertexType> vertices;
    vector<uint16_t> indices;
-   SSBOType ssbo;
 
    mat4 model_base;
@@ -125,8 +124,8 @@
    // Move the targetAsteroid stuff out of this class since it is very specific to lasers
    // and makes moving SceneObject into its own header file more problematic
-   SceneObject<ModelVertex, SSBO_Asteroid>* targetAsteroid; // currently only used for lasers
-};
-
-// TODO: Have to figure out how to include an optional ssbo parameter for each object
+   SceneObject<ModelVertex>* targetAsteroid; // currently only used for lasers
+};
+
+// TODO: Figure out how to include an optional ssbo parameter for each object
 // Could probably use the same approach to make indices optional
 // Figure out if there are sufficient use cases to make either of these optional or is it fine to make
@@ -149,9 +148,15 @@
 };
 
-template<class VertexType, class SSBOType>
+// TODO: Move this into its own hpp and cpp files
+// TODO: Actually, since this is only used to make lasers deal damage to asteroids, review the logic
+// and see if there is a more straightforward way of implementing this.
+// If there is a simple and straightforward way to implement this in updateScene(), I should just do that instead of
+// using this class. A general feature like this is useful, but, depending on the actual game, it might not be used
+// that often, and using this class might actually make things more complicated if it doesn't quite implement the
+// desired features
+template<class SSBOType>
 struct EffectOverTime : public BaseEffectOverTime {
-   GraphicsPipeline_Vulkan<VertexType>& pipeline;
-   vector<SceneObject<VertexType, SSBOType>>& objects;
-   unsigned int objectIndex;
+   VulkanBuffer<SSBOType> &buffer;
+   uint32_t objectIndex;
    size_t effectedFieldOffset;
    float startValue;
@@ -159,32 +164,25 @@
    float changePerSecond;
 
-   EffectOverTime(GraphicsPipeline_Vulkan<VertexType>& pipeline, vector<SceneObject<VertexType, SSBOType>>& objects,
-                  unsigned int objectIndex, size_t effectedFieldOffset, float startTime, float changePerSecond)
-               : pipeline(pipeline)
-               , objects(objects)
+   EffectOverTime(VulkanBuffer<SSBOType> &buffer, uint32_t objectIndex, size_t effectedFieldOffset, float startTime,
+                  float changePerSecond)
+               : buffer(buffer)
                , objectIndex(objectIndex)
                , effectedFieldOffset(effectedFieldOffset)
                , startTime(startTime)
                , changePerSecond(changePerSecond) {
-      size_t ssboOffset = offset_of(&SceneObject<VertexType, SSBOType>::ssbo);
-
-      unsigned char* effectedFieldPtr = reinterpret_cast<unsigned char*>(&objects[objectIndex]) +
-         ssboOffset + effectedFieldOffset;
-
-      startValue = *reinterpret_cast<float*>(effectedFieldPtr);
+      startValue = *reinterpret_cast<float*>(getEffectedFieldPtr());
+   }
+
+   unsigned char* getEffectedFieldPtr() {
+      return reinterpret_cast<unsigned char*>(&buffer.get(objectIndex)) + effectedFieldOffset;
    }
 
    void applyEffect(float curTime) {
-      if (objects[objectIndex].ssbo.deleted) {
-         this->deleted = true;
+      if (buffer.get(objectIndex).deleted) {
+         deleted = true;
          return;
       }
 
-      size_t ssboOffset = offset_of(&SceneObject<VertexType, SSBOType>::ssbo);
-
-      unsigned char* effectedFieldPtr = reinterpret_cast<unsigned char*>(&objects[objectIndex]) +
-         ssboOffset + effectedFieldOffset;
-
-      *reinterpret_cast<float*>(effectedFieldPtr) = startValue + (curTime - startTime) * changePerSecond;
+      *reinterpret_cast<float*>(getEffectedFieldPtr()) = startValue + (curTime - startTime) * changePerSecond;
    }
 };
@@ -341,21 +339,21 @@
       // if there is a need to add other uniform variables to one or more of the shaders
 
-      vector<SceneObject<ModelVertex, SSBO_ModelObject>> modelObjects;
+      vector<SceneObject<ModelVertex>> modelObjects;
 
       UBO_VP_mats object_VP_mats;
 
-      vector<SceneObject<ModelVertex, SSBO_ModelObject>> shipObjects;
+      vector<SceneObject<ModelVertex>> shipObjects;
 
       UBO_VP_mats ship_VP_mats;
 
-      vector<SceneObject<ModelVertex, SSBO_Asteroid>> asteroidObjects;
+      vector<SceneObject<ModelVertex>> asteroidObjects;
 
       UBO_VP_mats asteroid_VP_mats;
 
-      vector<SceneObject<LaserVertex, SSBO_Laser>> laserObjects;
+      vector<SceneObject<LaserVertex>> laserObjects;
 
       UBO_VP_mats laser_VP_mats;
 
-      vector<SceneObject<ExplosionVertex, SSBO_Explosion>> explosionObjects;
+      vector<SceneObject<ExplosionVertex>> explosionObjects;
 
       UBO_Explosion explosion_UBO;
@@ -370,8 +368,8 @@
 
       unsigned int leftLaserIdx = -1;
-      EffectOverTime<ModelVertex, SSBO_Asteroid>* leftLaserEffect = nullptr;
+      EffectOverTime<SSBO_Asteroid>* leftLaserEffect = nullptr;
 
       unsigned int rightLaserIdx = -1;
-      EffectOverTime<ModelVertex, SSBO_Asteroid>* rightLaserEffect = nullptr;
+      EffectOverTime<SSBO_Asteroid>* rightLaserEffect = nullptr;
 
       /*** High-level vars ***/
@@ -440,8 +438,8 @@
       // stop using objects.back() to access the object that was just created
       template<class VertexType, class SSBOType>
-      SceneObject<VertexType, SSBOType>& addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
-                                                   GraphicsPipeline_Vulkan<VertexType>& pipeline,
-                                                   const vector<VertexType>& vertices, vector<uint16_t> indices,
-                                                   VulkanBuffer<SSBOType>& objectBuffer, SSBOType ssbo);
+      SceneObject<VertexType>& addObject(vector<SceneObject<VertexType>>& objects,
+                                         GraphicsPipeline_Vulkan<VertexType>& pipeline,
+                                         const vector<VertexType>& vertices, vector<uint16_t> indices,
+                                         VulkanBuffer<SSBOType>& objectBuffer, SSBOType ssbo);
 
       template<class VertexType>
@@ -451,16 +449,16 @@
       vector<VertexType> addVertexNormals(vector<VertexType> vertices);
 
-      template<class VertexType, class SSBOType>
-      void centerObject(SceneObject<VertexType, SSBOType>& object);
-
-      template<class VertexType, class SSBOType>
-      void updateObjectVertices(GraphicsPipeline_Vulkan<VertexType>& pipeline,
-            SceneObject<VertexType, SSBOType>& obj, size_t index);
+      template<class VertexType>
+      void centerObject(SceneObject<VertexType>& object);
+
+      template<class VertexType>
+      void updateObjectVertices(GraphicsPipeline_Vulkan<VertexType>& pipeline, SceneObject<VertexType>& obj,
+                                size_t index);
 
       void addLaser(vec3 start, vec3 end, vec3 color, float width);
       void translateLaser(size_t index, const vec3& translation);
       void updateLaserTarget(size_t index);
-      bool getLaserAndAsteroidIntersection(SceneObject<ModelVertex, SSBO_Asteroid>& asteroid,
-            vec3& start, vec3& end, vec3& intersection);
+      bool getLaserAndAsteroidIntersection(SceneObject<ModelVertex>& asteroid, vec3& start, vec3& end,
+                                           vec3& intersection);
 
       void addExplosion(mat4 model_mat, float duration, float cur_time);
@@ -488,5 +486,5 @@
 
 template<>
-inline void VulkanGame::centerObject(SceneObject<ExplosionVertex, SSBO_Explosion>& object) {
+inline void VulkanGame::centerObject(SceneObject<ExplosionVertex>& object) {
 }
 
@@ -505,8 +503,8 @@
 // Figure out a better way to allow the model matrix to be set during object creation
 template<class VertexType, class SSBOType>
-SceneObject<VertexType, SSBOType>& VulkanGame::addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
-                                                         GraphicsPipeline_Vulkan<VertexType>& pipeline,
-                                                         const vector<VertexType>& vertices, vector<uint16_t> indices,
-                                                         VulkanBuffer<SSBOType>& objectBuffer, SSBOType ssbo) {
+SceneObject<VertexType>& VulkanGame::addObject(vector<SceneObject<VertexType>>& objects,
+                                               GraphicsPipeline_Vulkan<VertexType>& pipeline,
+                                               const vector<VertexType>& vertices, vector<uint16_t> indices,
+                                               VulkanBuffer<SSBOType>& objectBuffer, SSBOType ssbo) {
    // TODO: Use the model field of ssbo to set the object's model_base
    // currently, the passed-in model is useless since it gets overridden when ssbo.model is recalculated
@@ -517,8 +515,8 @@
    }
 
-   objects.push_back({ vertices, indices, ssbo, mat4(1.0f), mat4(1.0f) });
+   objects.push_back({ vertices, indices, mat4(1.0f), mat4(1.0f) });
    objectBuffer.add(ssbo);
 
-   SceneObject<VertexType, SSBOType>& obj = objects.back();
+   SceneObject<VertexType>& obj = objects.back();
 
    // TODO: Specify whether to center the object outside of this function or, worst case, maybe
@@ -571,6 +569,6 @@
 }
 
-template<class VertexType, class SSBOType>
-void VulkanGame::centerObject(SceneObject<VertexType, SSBOType>& object) {
+template<class VertexType>
+void VulkanGame::centerObject(SceneObject<VertexType>& object) {
    vector<VertexType>& vertices = object.vertices;
 
@@ -617,7 +615,7 @@
 }
 
-template<class VertexType, class SSBOType>
-void VulkanGame::updateObjectVertices(GraphicsPipeline_Vulkan<VertexType>& pipeline,
-      SceneObject<VertexType, SSBOType>& obj, size_t index) {
+template<class VertexType>
+void VulkanGame::updateObjectVertices(GraphicsPipeline_Vulkan<VertexType>& pipeline, SceneObject<VertexType>& obj,
+                                      size_t index) {
    pipeline.updateObjectVertices(index, obj.vertices, resourceCommandPool, graphicsQueue);
 }
