Index: graphics-pipeline_vulkan.hpp
===================================================================
--- graphics-pipeline_vulkan.hpp	(revision 756162f65401bd12de652b25a35ce6e01efb144a)
+++ graphics-pipeline_vulkan.hpp	(revision 9d21aac42227b516c5df135b6ff2266349c321e4)
@@ -21,14 +21,4 @@
 
 using namespace glm;
-
-// TODO: Maybe change the name of this struct so I can call the list something other than descriptorInfoList
-struct DescriptorInfo {
-   VkDescriptorType type;
-   VkShaderStageFlags stageFlags;
-
-   // Only one of the below properties should be set
-   vector<VkDescriptorBufferInfo>* bufferDataList;
-   VkDescriptorImageInfo* imageData;
-};
 
 // TODO: Use this struct for uniform buffers as well and rename it to VulkanBuffer (maybe move it to VulkanUtils)
@@ -41,8 +31,25 @@
 };
 
-template<class VertexType, class SSBOType>
+// TODO: Maybe change the name of this struct so I can call the list something other than descriptorInfoList
+struct DescriptorInfo {
+   VkDescriptorType type;
+   VkShaderStageFlags stageFlags;
+
+   // Only one of the below properties should be set
+   vector<VkDescriptorBufferInfo>* bufferDataList;
+   VkDescriptorImageInfo* imageData;
+};
+
+template<class VertexType>
 class GraphicsPipeline_Vulkan : public GraphicsPipeline {
    public:
       string vertShaderFile, fragShaderFile;
+
+      // TODO: Move this outside this classs, since it is no longer used here
+      StorageBufferSet storageBufferSet;
+
+      // Both of these are only used for managing the SSBO, so move them out as well
+      size_t objectCapacity;
+      size_t numObjects;
 
       GraphicsPipeline_Vulkan();
@@ -64,6 +71,4 @@
       void addAttribute(VkFormat format, size_t offset);
 
-      void addStorageDescriptor(VkShaderStageFlags stageFlags);
-
       // TODO: I might be able to use a single VkDescriptorBufferInfo here and reuse it when creating the descriptor sets
       void addDescriptorInfo(VkDescriptorType type, VkShaderStageFlags stageFlags,
@@ -71,4 +76,7 @@
       void addDescriptorInfo(VkDescriptorType type, VkShaderStageFlags stageFlags, VkDescriptorImageInfo* imageData);
 
+      void updateDescriptorInfo(uint32_t index, vector<VkDescriptorBufferInfo>* bufferData);
+      // TODO: Maybe make an analogous one for updating image info
+
       void createPipeline(string vertShaderFile, string fragShaderFile);
       void createDescriptorSetLayout();
@@ -78,8 +86,6 @@
       void createRenderCommands(VkCommandBuffer& commandBuffer, uint32_t currentImage);
 
-      bool addObject(const vector<VertexType>& vertices, vector<uint16_t> indices, SSBOType& ssbo,
-                     VkCommandPool commandPool, VkQueue graphicsQueue);
-
-      void updateObject(size_t objIndex, SSBOType& ssbo);
+      void addObject(const vector<VertexType>& vertices, vector<uint16_t> indices, VkCommandPool commandPool,
+                     VkQueue graphicsQueue);
 
       void updateObjectVertices(size_t objIndex, const vector<VertexType>& vertices, VkCommandPool commandPool,
@@ -117,9 +123,4 @@
       VkDeviceMemory indexBufferMemory;
 
-      size_t numObjects;
-      size_t objectCapacity;
-
-      StorageBufferSet storageBufferSet;
-
       VkShaderModule createShaderModule(const vector<char>& code);
       vector<char> readFile(const string& filename);
@@ -127,11 +128,10 @@
       void resizeVertexBuffer(VkCommandPool commandPool, VkQueue graphicsQueue);
       void resizeIndexBuffer(VkCommandPool commandPool, VkQueue graphicsQueue);
-      void resizeStorageBufferSet(StorageBufferSet& set, VkCommandPool commandPool, VkQueue graphicsQueue);
 };
 
 /*** PUBLIC METHODS ***/
 
-template<class VertexType, class SSBOType>
-GraphicsPipeline_Vulkan<VertexType, SSBOType>::GraphicsPipeline_Vulkan() {
+template<class VertexType>
+GraphicsPipeline_Vulkan<VertexType>::GraphicsPipeline_Vulkan() {
 }
 
@@ -139,6 +139,6 @@
 // TODO: See if it would be feasible to move code in the createPipeline method
 // into the constructor. That way, I can also put relevant cleanup code into the destructor
-template<class VertexType, class SSBOType>
-GraphicsPipeline_Vulkan<VertexType, SSBOType>::GraphicsPipeline_Vulkan(
+template<class VertexType>
+GraphicsPipeline_Vulkan<VertexType>::GraphicsPipeline_Vulkan(
       VkPrimitiveTopology topology, VkPhysicalDevice physicalDevice, VkDevice device,
       VkRenderPass renderPass, Viewport viewport, vector<VkImage>& swapChainImages,
@@ -173,45 +173,23 @@
    this->numObjects = 0;
    this->objectCapacity = objectCapacity;
-
-   // Hacky way to allow an SSBO to be optional
-   // Specifying void* as the SSBOType will skip allocating the related buffers
-   if (!is_same_v<SSBOType, void*>) {
-      VkDeviceSize bufferSize = objectCapacity * sizeof(SSBOType);
-      cout << "NUM SWAP CHAIN IMAGES: " << swapChainImages.size() << endl;
-
-      storageBufferSet.buffers.resize(swapChainImages.size());
-      storageBufferSet.memory.resize(swapChainImages.size());
-      storageBufferSet.infoSet.resize(swapChainImages.size());
-
-      for (size_t i = 0; i < swapChainImages.size(); i++) {
-         VulkanUtils::createBuffer(this->device, this->physicalDevice, bufferSize,
-            VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
-            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-            storageBufferSet.buffers[i], storageBufferSet.memory[i]);
-
-         storageBufferSet.infoSet[i].buffer = storageBufferSet.buffers[i];
-         storageBufferSet.infoSet[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
-         storageBufferSet.infoSet[i].range = bufferSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
-      }
-   }
 }
 
 // TODO: Move as much cleanup as I can into the destructor
-template<class VertexType, class SSBOType>
-GraphicsPipeline_Vulkan<VertexType, SSBOType>::~GraphicsPipeline_Vulkan() {
-}
-
-template<class VertexType, class SSBOType>
-size_t GraphicsPipeline_Vulkan<VertexType, SSBOType>::getNumVertices() {
+template<class VertexType>
+GraphicsPipeline_Vulkan<VertexType>::~GraphicsPipeline_Vulkan() {
+}
+
+template<class VertexType>
+size_t GraphicsPipeline_Vulkan<VertexType>::getNumVertices() {
    return numVertices;
 }
 
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::updateRenderPass(VkRenderPass renderPass) {
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::updateRenderPass(VkRenderPass renderPass) {
    this->renderPass = renderPass;
 }
 
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::addAttribute(VkFormat format, size_t offset) {
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::addAttribute(VkFormat format, size_t offset) {
    VkVertexInputAttributeDescription attributeDesc = {};
 
@@ -224,27 +202,24 @@
 }
 
-// TODO: The SSBOType check isn't really needed since I call this function in VulkanGame explicitly
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::addStorageDescriptor(VkShaderStageFlags stageFlags) {
-   if (!is_same_v<SSBOType, void*>) {
-      addDescriptorInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
-         stageFlags, &storageBufferSet.infoSet);
-   }
-}
-
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::addDescriptorInfo(VkDescriptorType type,
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::addDescriptorInfo(VkDescriptorType type,
       VkShaderStageFlags stageFlags, vector<VkDescriptorBufferInfo>* bufferData) {
    this->descriptorInfoList.push_back({ type, stageFlags, bufferData, nullptr });
 }
 
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::addDescriptorInfo(VkDescriptorType type,
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::addDescriptorInfo(VkDescriptorType type,
       VkShaderStageFlags stageFlags, VkDescriptorImageInfo* imageData) {
    this->descriptorInfoList.push_back({ type, stageFlags, nullptr, imageData });
 }
 
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::createPipeline(string vertShaderFile, string fragShaderFile) {
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::updateDescriptorInfo(uint32_t index,
+                                                                         vector<VkDescriptorBufferInfo>* bufferData) {
+   this->descriptorInfoList[index].bufferDataList = bufferData;
+}
+
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::createPipeline(string vertShaderFile, string fragShaderFile) {
    this->vertShaderFile = vertShaderFile;
    this->fragShaderFile = fragShaderFile;
@@ -386,6 +361,6 @@
 }
 
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::createDescriptorSetLayout() {
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::createDescriptorSetLayout() {
    vector<VkDescriptorSetLayoutBinding> bindings(this->descriptorInfoList.size());
 
@@ -408,6 +383,6 @@
 }
 
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::createDescriptorPool(vector<VkImage>& swapChainImages) {
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::createDescriptorPool(vector<VkImage>& swapChainImages) {
    vector<VkDescriptorPoolSize> poolSizes(this->descriptorInfoList.size());
 
@@ -429,6 +404,6 @@
 
 // TODO: Since I only need the size of the swapChainImages array, I should just pass that in instead of the whole array
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::createDescriptorSets(vector<VkImage>& swapChainImages) {
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::createDescriptorSets(vector<VkImage>& swapChainImages) {
    vector<VkDescriptorSetLayout> layouts(swapChainImages.size(), this->descriptorSetLayout);
 
@@ -476,8 +451,6 @@
 }
 
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::createRenderCommands(VkCommandBuffer& commandBuffer,
-      uint32_t currentImage) {
-
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::createRenderCommands(VkCommandBuffer& commandBuffer, uint32_t currentImage) {
    vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
 
@@ -494,9 +467,7 @@
 }
 
-template<class VertexType, class SSBOType>
-bool GraphicsPipeline_Vulkan<VertexType, SSBOType>::addObject(
-      const vector<VertexType>& vertices, vector<uint16_t> indices,
-      SSBOType& ssbo, VkCommandPool commandPool, VkQueue graphicsQueue) {
-
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::addObject(const vector<VertexType>& vertices, vector<uint16_t> indices,
+                                                    VkCommandPool commandPool, VkQueue graphicsQueue) {
    // TODO: When resizing the vertex or index buffer, take deleted objects into account.
    // Remove their data from the buffer and determine the new size of the bufer based on # of remining objects
@@ -522,46 +493,9 @@
       this->indexBuffer, this->numIndices, graphicsQueue);
    this->numIndices += indices.size();
-
-   bool resizedStorageBuffer = false;
-
-   if (!is_same_v<SSBOType, void*>) {
-      if (this->numObjects == this->objectCapacity) {
-         resizeStorageBufferSet(storageBufferSet, commandPool, graphicsQueue);
-         cleanup();
-
-         // Assume the SSBO is always the 2nd binding
-         this->descriptorInfoList[1].bufferDataList = &storageBufferSet.infoSet;
-         resizedStorageBuffer = true;
-
-         cout << "SSBO resized, New object capacity: " << this->objectCapacity << endl;
-
-         // TODO: I'll need to correctly update the descriptor set array instead of appending to it
-         // Then, I'll have to call createDescriptorSets() and finally createCommandBuffers() (from vulkan-game)
-         // This isn't too bad actually, since I have to call createCommandBuffers() every time I add a newobject
-         // anyway. So, in this function, I'll just have to call createDescriptorSets()
-      }
-
-      updateObject(this->numObjects, ssbo);
-   }
-
-   this->numObjects++;
-
-   return resizedStorageBuffer;
-}
-
-// TODO: Allow a swapchain index to be passed in instead of updating all of them
-// Actually, since I'm in the process of replacing SSBOs with dynamic UBOs, I can ignore that for this function
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::updateObject(size_t objIndex, SSBOType& ssbo) {
-   if (!is_same_v<SSBOType, void*>) {
-      for (size_t i = 0; i < storageBufferSet.memory.size(); i++) {
-         VulkanUtils::copyDataToMemory(this->device, ssbo, storageBufferSet.memory[i], objIndex * sizeof(SSBOType));
-      }
-   }
 }
 
 // Should only be used if the number of vertices has not changed
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::updateObjectVertices(size_t objIndex,
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::updateObjectVertices(size_t objIndex,
       const vector<VertexType>& vertices, VkCommandPool commandPool, VkQueue graphicsQueue) {
    VulkanUtils::copyDataToBuffer(this->device, this->physicalDevice, commandPool, vertices,
@@ -569,6 +503,6 @@
 }
 
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::cleanup() {
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::cleanup() {
    vkDestroyPipeline(device, pipeline, nullptr);
    vkDestroyDescriptorPool(device, descriptorPool, nullptr);
@@ -579,6 +513,6 @@
 }
 
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::cleanupBuffers() {
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::cleanupBuffers() {
    vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
 
@@ -587,17 +521,10 @@
    vkDestroyBuffer(device, indexBuffer, nullptr);
    vkFreeMemory(device, indexBufferMemory, nullptr);
-
-   if (!is_same_v<SSBOType, void*>) {
-      for (size_t i = 0; i < storageBufferSet.buffers.size(); i++) {
-         vkDestroyBuffer(device, storageBufferSet.buffers[i], nullptr);
-         vkFreeMemory(device, storageBufferSet.memory[i], nullptr);
-      }
-   }
 }
 
 /*** PRIVATE METHODS ***/
 
-template<class VertexType, class SSBOType>
-VkShaderModule GraphicsPipeline_Vulkan<VertexType, SSBOType>::createShaderModule(const vector<char>& code) {
+template<class VertexType>
+VkShaderModule GraphicsPipeline_Vulkan<VertexType>::createShaderModule(const vector<char>& code) {
    VkShaderModuleCreateInfo createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
@@ -613,6 +540,6 @@
 }
 
-template<class VertexType, class SSBOType>
-vector<char> GraphicsPipeline_Vulkan<VertexType, SSBOType>::readFile(const string& filename) {
+template<class VertexType>
+vector<char> GraphicsPipeline_Vulkan<VertexType>::readFile(const string& filename) {
    ifstream file(filename, ios::ate | ios::binary);
 
@@ -632,7 +559,6 @@
 }
 
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::resizeVertexBuffer(VkCommandPool commandPool,
-      VkQueue graphicsQueue) {
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::resizeVertexBuffer(VkCommandPool commandPool, VkQueue graphicsQueue) {
    VkBuffer newVertexBuffer;
    VkDeviceMemory newVertexBufferMemory;
@@ -652,7 +578,6 @@
 }
 
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::resizeIndexBuffer(VkCommandPool commandPool,
-      VkQueue graphicsQueue) {
+template<class VertexType>
+void GraphicsPipeline_Vulkan<VertexType>::resizeIndexBuffer(VkCommandPool commandPool, VkQueue graphicsQueue) {
    VkBuffer newIndexBuffer;
    VkDeviceMemory newIndexBufferMemory;
@@ -672,33 +597,3 @@
 }
 
-template<class VertexType, class SSBOType>
-void GraphicsPipeline_Vulkan<VertexType, SSBOType>::resizeStorageBufferSet(StorageBufferSet& set,
-      VkCommandPool commandPool, VkQueue graphicsQueue) {
-   this->objectCapacity *= 2;
-   VkDeviceSize bufferSize = objectCapacity * sizeof(SSBOType);
-
-   for (size_t i = 0; i < set.buffers.size(); i++) {
-      VkBuffer newStorageBuffer;
-      VkDeviceMemory newStorageBufferMemory;
-
-      VulkanUtils::createBuffer(this->device, this->physicalDevice, bufferSize,
-         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,
-         newStorageBuffer, newStorageBufferMemory);
-
-      VulkanUtils::copyBuffer(this->device, commandPool, set.buffers[i], newStorageBuffer,
-         0, 0, this->numObjects * sizeof(SSBOType), graphicsQueue);
-
-      vkDestroyBuffer(this->device, set.buffers[i], nullptr);
-      vkFreeMemory(this->device, set.memory[i], nullptr);
-
-      set.buffers[i] = newStorageBuffer;
-      set.memory[i] = newStorageBufferMemory;
-
-      set.infoSet[i].buffer = set.buffers[i];
-      set.infoSet[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
-      set.infoSet[i].range = bufferSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
-   }
-}
-
 #endif // _GRAPHICS_PIPELINE_VULKAN_H
Index: sdl-game.cpp
===================================================================
--- sdl-game.cpp	(revision 756162f65401bd12de652b25a35ce6e01efb144a)
+++ sdl-game.cpp	(revision 9d21aac42227b516c5df135b6ff2266349c321e4)
@@ -103,11 +103,12 @@
    modelPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ModelVertex::objIndex));
 
-   createBufferSet(uniformBuffers_modelPipeline, uniformBuffersMemory_modelPipeline, sizeof(UBO_VP_mats),
+   createBufferSet(sizeof(UBO_VP_mats),
       VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBufferInfoList_modelPipeline);
+      uniformBuffers_modelPipeline, uniformBuffersMemory_modelPipeline, uniformBufferInfoList_modelPipeline);
 
    modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
       VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_modelPipeline);
-   modelPipeline.addStorageDescriptor(VK_SHADER_STAGE_VERTEX_BIT);
+   modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+      VK_SHADER_STAGE_VERTEX_BIT, &modelPipeline.storageBufferSet.infoSet);
    modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
       VK_SHADER_STAGE_FRAGMENT_BIT, &floorTextureImageDescriptor);
@@ -256,7 +257,13 @@
 
 void VulkanGame::initGraphicsPipelines() {
-   modelPipeline = GraphicsPipeline_Vulkan<ModelVertex, SSBO_ModelObject>(
+   modelPipeline = GraphicsPipeline_Vulkan<ModelVertex>(
       VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
       { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 16, 24, 10);
+
+   createBufferSet(modelPipeline.objectCapacity * sizeof(SSBO_ModelObject),
+      VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+      modelPipeline.storageBufferSet.buffers, modelPipeline.storageBufferSet.memory,
+      modelPipeline.storageBufferSet.infoSet);
 }
 
@@ -468,4 +475,9 @@
    modelPipeline.cleanupBuffers();
 
+   for (size_t i = 0; i < modelPipeline.storageBufferSet.buffers.size(); i++) {
+      vkDestroyBuffer(device, modelPipeline.storageBufferSet.buffers[i], nullptr);
+      vkFreeMemory(device, modelPipeline.storageBufferSet.memory[i], nullptr);
+   }
+
    // END UNREVIEWED SECTION
 
@@ -1083,6 +1095,6 @@
 }
 
-void VulkanGame::createBufferSet(vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory,
-                                 VkDeviceSize bufferSize, VkBufferUsageFlags flags, VkMemoryPropertyFlags properties,
+void VulkanGame::createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags flags, VkMemoryPropertyFlags properties,
+                                 vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory,
                                  vector<VkDescriptorBufferInfo>& bufferInfoList) {
    buffers.resize(swapChainImageCount);
@@ -1230,7 +1242,7 @@
    // instead of recreated every time
 
-   createBufferSet(uniformBuffers_modelPipeline, uniformBuffersMemory_modelPipeline, sizeof(UBO_VP_mats),
+   createBufferSet(sizeof(UBO_VP_mats),
       VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-      uniformBufferInfoList_modelPipeline);
+      uniformBuffers_modelPipeline, uniformBuffersMemory_modelPipeline, uniformBufferInfoList_modelPipeline);
 
    modelPipeline.updateRenderPass(renderPass);
Index: sdl-game.hpp
===================================================================
--- sdl-game.hpp	(revision 756162f65401bd12de652b25a35ce6e01efb144a)
+++ sdl-game.hpp	(revision 9d21aac42227b516c5df135b6ff2266349c321e4)
@@ -207,5 +207,5 @@
       // the same pipeline, but use different textures, the approach I took when initially creating GraphicsPipeline_Vulkan
       // wouldn't work since the whole pipeline couldn't have a common set of descriptors for the textures
-      GraphicsPipeline_Vulkan<ModelVertex, SSBO_ModelObject> modelPipeline;
+      GraphicsPipeline_Vulkan<ModelVertex> modelPipeline;
 
       // TODO: Maybe make the ubo objects part of the pipeline class since there's only one ubo
@@ -276,7 +276,18 @@
       void cleanupImGuiOverlay();
 
-      void createBufferSet(vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory, VkDeviceSize bufferSize,
-                           VkBufferUsageFlags flags, VkMemoryPropertyFlags properties,
+      // TODO: Maybe move these to a different class, possibly VulkanBuffer or some new related class
+
+      void createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags flags, VkMemoryPropertyFlags properties,
+                           vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory,
                            vector<VkDescriptorBufferInfo>& bufferInfoList);
+
+      // TODO: See if it makes sense to rename this to resizeBufferSet() and use it to resize other types of buffers as well
+      // TODO: Remove the need for templating, which is only there so a GraphicsPupeline_Vulkan can be passed in
+      template<class VertexType, class SSBOType>
+      void resizeStorageBufferSet(StorageBufferSet& set, VkCommandPool commandPool, VkQueue graphicsQueue,
+                                  GraphicsPipeline_Vulkan<VertexType>& pipeline);
+
+      template<class SSBOType>
+      void updateStorageBuffer(StorageBufferSet& storageBufferSet, size_t objIndex, SSBOType& ssbo);
 
       // TODO: Since addObject() returns a reference to the new object now,
@@ -284,5 +295,5 @@
       template<class VertexType, class SSBOType>
       SceneObject<VertexType, SSBOType>& addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
-                                                   GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline,
+                                                   GraphicsPipeline_Vulkan<VertexType>& pipeline,
                                                    const vector<VertexType>& vertices, vector<uint16_t> indices,
                                                    SSBOType ssbo, bool pipelinesCreated);
@@ -298,6 +309,6 @@
 
       template<class VertexType, class SSBOType>
-      void updateObject(vector<SceneObject<VertexType, SSBOType>>& objects, GraphicsPipeline_Vulkan<VertexType,
-                        SSBOType>& pipeline, size_t index);
+      void updateObject(vector<SceneObject<VertexType, SSBOType>>& objects,
+                        GraphicsPipeline_Vulkan<VertexType>& pipeline, size_t index);
 
       void renderFrame(ImDrawData* draw_data);
@@ -320,7 +331,45 @@
 };
 
+template<class VertexType, class SSBOType>
+void VulkanGame::resizeStorageBufferSet(StorageBufferSet& set, VkCommandPool commandPool, VkQueue graphicsQueue,
+                                        GraphicsPipeline_Vulkan<VertexType>& pipeline) {
+   pipeline.objectCapacity *= 2;
+   VkDeviceSize bufferSize = pipeline.objectCapacity * sizeof(SSBOType);
+
+   for (size_t i = 0; i < set.buffers.size(); i++) {
+      VkBuffer newStorageBuffer;
+      VkDeviceMemory newStorageBufferMemory;
+
+      VulkanUtils::createBuffer(device, physicalDevice, bufferSize,
+         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,
+         newStorageBuffer, newStorageBufferMemory);
+
+      VulkanUtils::copyBuffer(device, commandPool, set.buffers[i], newStorageBuffer,
+         0, 0, pipeline.numObjects * sizeof(SSBOType), graphicsQueue);
+
+      vkDestroyBuffer(device, set.buffers[i], nullptr);
+      vkFreeMemory(device, set.memory[i], nullptr);
+
+      set.buffers[i] = newStorageBuffer;
+      set.memory[i] = newStorageBufferMemory;
+
+      set.infoSet[i].buffer = set.buffers[i];
+      set.infoSet[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
+      set.infoSet[i].range = bufferSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
+   }
+}
+
+// TODO: See if it makes sense to pass in the current swapchain index instead of updating all of them
+template<class SSBOType>
+void VulkanGame::updateStorageBuffer(StorageBufferSet& storageBufferSet, size_t objIndex, SSBOType& ssbo) {
+   for (size_t i = 0; i < storageBufferSet.memory.size(); i++) {
+      VulkanUtils::copyDataToMemory(device, ssbo, storageBufferSet.memory[i], objIndex * sizeof(SSBOType));
+   }
+}
+
 // TODO: Right now, it's basically necessary to pass the identity matrix in for ssbo.model
 // and to change the model matrix later by setting model_transform and then calling updateObject()
-// Figure out a better way to allow the model matrix to be set during objecting creation
+// Figure out a better way to allow the model matrix to be set during object creation
 
 // TODO: Maybe return a reference to the object from this method if I decide that updating it
@@ -329,9 +378,8 @@
 // to account for scaling
 template<class VertexType, class SSBOType>
-SceneObject<VertexType, SSBOType>& VulkanGame::addObject(
-   vector<SceneObject<VertexType, SSBOType>>& objects,
-   GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline,
-   const vector<VertexType>& vertices, vector<uint16_t> indices, SSBOType ssbo,
-   bool pipelinesCreated) {
+SceneObject<VertexType, SSBOType>& VulkanGame::addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
+                                                         GraphicsPipeline_Vulkan<VertexType>& pipeline,
+                                                         const vector<VertexType>& vertices, vector<uint16_t> indices,
+                                                         SSBOType ssbo, bool pipelinesCreated) {
    // 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 in updateObject() anyway
@@ -353,6 +401,22 @@
    }
 
-   bool storageBufferResized = pipeline.addObject(obj.vertices, obj.indices, obj.ssbo,
-      resourceCommandPool, graphicsQueue);
+   pipeline.addObject(obj.vertices, obj.indices, resourceCommandPool, graphicsQueue);
+
+   bool resizeStorageBuffer = pipeline.numObjects == pipeline.objectCapacity;
+
+   if (resizeStorageBuffer) {
+      resizeStorageBufferSet<VertexType, SSBOType>(pipeline.storageBufferSet, resourceCommandPool, graphicsQueue, pipeline);
+      pipeline.cleanup();
+
+      // Assume the SSBO is always the 2nd binding
+      pipeline.updateDescriptorInfo(1, &pipeline.storageBufferSet.infoSet);
+   }
+
+   pipeline.numObjects++;
+
+   updateStorageBuffer(pipeline.storageBufferSet, pipeline.numObjects - 1, obj.ssbo);
+
+   // TODO: Figure out why I am destroying and recreating the ubos when the swap chain is recreated,
+   // but am reusing the same ssbos. Maybe I don't need to recreate the ubos.
 
    if (pipelinesCreated) {
@@ -367,5 +431,8 @@
       // Refactor the logic to check for any resized SSBOs after all objects for the frame
       // are created and then recreate each of the corresponding pipelines only once per frame
-      if (storageBufferResized) {
+
+      // TODO: Also, verify if I actually need to recreate all of these, or maybe just the descriptor sets, for instance
+
+      if (resizeStorageBuffer) {
          pipeline.createPipeline(pipeline.vertShaderFile, pipeline.fragShaderFile);
          pipeline.createDescriptorPool(swapChainImages);
@@ -457,6 +524,6 @@
 // TODO: Just pass in the single object instead of a list of all of them
 template<class VertexType, class SSBOType>
-void VulkanGame::updateObject(vector<SceneObject<VertexType, SSBOType>>& objects, GraphicsPipeline_Vulkan<VertexType,
-                              SSBOType>& pipeline, size_t index) {
+void VulkanGame::updateObject(vector<SceneObject<VertexType, SSBOType>>& objects,
+                              GraphicsPipeline_Vulkan<VertexType>& pipeline, size_t index) {
    SceneObject<VertexType, SSBOType>& obj = objects[index];
 
@@ -464,5 +531,5 @@
    obj.center = vec3(obj.ssbo.model * vec4(0.0f, 0.0f, 0.0f, 1.0f));
 
-   pipeline.updateObject(index, obj.ssbo);
+   updateStorageBuffer(pipeline.storageBufferSet, index, obj.ssbo);
 
    obj.modified = false;
Index: vulkan-game.cpp
===================================================================
--- vulkan-game.cpp	(revision 756162f65401bd12de652b25a35ce6e01efb144a)
+++ vulkan-game.cpp	(revision 9d21aac42227b516c5df135b6ff2266349c321e4)
@@ -119,10 +119,12 @@
    modelPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ModelVertex::objIndex));
 
-   createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+   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, uniformBuffersMemory_modelPipeline, uniformBufferInfoList_modelPipeline);
 
    modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
       VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_modelPipeline);
-   modelPipeline.addStorageDescriptor(VK_SHADER_STAGE_VERTEX_BIT);
+   modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+      VK_SHADER_STAGE_VERTEX_BIT, &modelPipeline.storageBufferSet.infoSet);
    modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
       VK_SHADER_STAGE_FRAGMENT_BIT, &floorTextureImageDescriptor);
@@ -180,10 +182,12 @@
    shipPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ModelVertex::objIndex));
 
-   createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+   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, uniformBuffersMemory_shipPipeline, uniformBufferInfoList_shipPipeline);
 
    shipPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
       VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_shipPipeline);
-   shipPipeline.addStorageDescriptor(VK_SHADER_STAGE_VERTEX_BIT);
+   shipPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+      VK_SHADER_STAGE_VERTEX_BIT, &shipPipeline.storageBufferSet.infoSet);
 
    // TODO: With the normals, indexing basically becomes pointless since no vertices will have exactly
@@ -438,10 +442,12 @@
    asteroidPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ModelVertex::objIndex));
 
-   createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+   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, uniformBuffersMemory_asteroidPipeline, uniformBufferInfoList_asteroidPipeline);
 
    asteroidPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
       VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_asteroidPipeline);
-   asteroidPipeline.addStorageDescriptor(VK_SHADER_STAGE_VERTEX_BIT);
+   asteroidPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+      VK_SHADER_STAGE_VERTEX_BIT, &asteroidPipeline.storageBufferSet.infoSet);
 
    asteroidPipeline.createDescriptorSetLayout();
@@ -454,10 +460,12 @@
    laserPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&LaserVertex::objIndex));
 
-   createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+   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, uniformBuffersMemory_laserPipeline, uniformBufferInfoList_laserPipeline);
 
    laserPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
       VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_laserPipeline);
-   laserPipeline.addStorageDescriptor(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
+   laserPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+      VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, &laserPipeline.storageBufferSet.infoSet);
    laserPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
       VK_SHADER_STAGE_FRAGMENT_BIT, &laserTextureImageDescriptor);
@@ -472,10 +480,12 @@
    explosionPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ExplosionVertex::objIndex));
 
-   createBufferSet(sizeof(UBO_Explosion), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+   createBufferSet(sizeof(UBO_Explosion),
+      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
       uniformBuffers_explosionPipeline, uniformBuffersMemory_explosionPipeline, uniformBufferInfoList_explosionPipeline);
 
    explosionPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
       VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_explosionPipeline);
-   explosionPipeline.addStorageDescriptor(VK_SHADER_STAGE_VERTEX_BIT);
+   explosionPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+      VK_SHADER_STAGE_VERTEX_BIT, &explosionPipeline.storageBufferSet.infoSet);
 
    explosionPipeline.createDescriptorSetLayout();
@@ -582,24 +592,54 @@
 
 void VulkanGame::initGraphicsPipelines() {
-   modelPipeline = GraphicsPipeline_Vulkan<ModelVertex, SSBO_ModelObject>(
+   modelPipeline = GraphicsPipeline_Vulkan<ModelVertex>(
       VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
       { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 24, 24, 10);
 
-   shipPipeline = GraphicsPipeline_Vulkan<ModelVertex, SSBO_ModelObject>(
+   createBufferSet(modelPipeline.objectCapacity * sizeof(SSBO_ModelObject),
+      VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+      modelPipeline.storageBufferSet.buffers, modelPipeline.storageBufferSet.memory,
+      modelPipeline.storageBufferSet.infoSet);
+
+   shipPipeline = GraphicsPipeline_Vulkan<ModelVertex>(
       VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
       { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 138, 138, 10);
 
-   asteroidPipeline = GraphicsPipeline_Vulkan<ModelVertex, SSBO_Asteroid>(
+   createBufferSet(modelPipeline.objectCapacity * sizeof(SSBO_ModelObject),
+      VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+      shipPipeline.storageBufferSet.buffers, shipPipeline.storageBufferSet.memory,
+      shipPipeline.storageBufferSet.infoSet);
+
+   asteroidPipeline = GraphicsPipeline_Vulkan<ModelVertex>(
       VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
       { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 24, 36, 10);
 
-   laserPipeline = GraphicsPipeline_Vulkan<LaserVertex, SSBO_Laser>(
+   createBufferSet(modelPipeline.objectCapacity * sizeof(SSBO_Asteroid),
+      VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+      asteroidPipeline.storageBufferSet.buffers, asteroidPipeline.storageBufferSet.memory,
+      asteroidPipeline.storageBufferSet.infoSet);
+
+   laserPipeline = GraphicsPipeline_Vulkan<LaserVertex>(
       VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
       { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 8, 18, 2);
 
-   explosionPipeline = GraphicsPipeline_Vulkan<ExplosionVertex, SSBO_Explosion>(
+   createBufferSet(modelPipeline.objectCapacity * sizeof(SSBO_Laser),
+      VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+      laserPipeline.storageBufferSet.buffers, laserPipeline.storageBufferSet.memory,
+      laserPipeline.storageBufferSet.infoSet);
+
+   explosionPipeline = GraphicsPipeline_Vulkan<ExplosionVertex>(
       VK_PRIMITIVE_TOPOLOGY_POINT_LIST, physicalDevice, device, renderPass,
       { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height },
       swapChainImages, EXPLOSION_PARTICLE_COUNT, EXPLOSION_PARTICLE_COUNT, 2);
+
+   createBufferSet(modelPipeline.objectCapacity * sizeof(SSBO_Explosion),
+      VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
+      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+      explosionPipeline.storageBufferSet.buffers, explosionPipeline.storageBufferSet.memory,
+      explosionPipeline.storageBufferSet.infoSet);
 }
 
@@ -1091,4 +1131,29 @@
    explosionPipeline.cleanupBuffers();
 
+   for (size_t i = 0; i < modelPipeline.storageBufferSet.buffers.size(); i++) {
+      vkDestroyBuffer(device, modelPipeline.storageBufferSet.buffers[i], nullptr);
+      vkFreeMemory(device, modelPipeline.storageBufferSet.memory[i], nullptr);
+   }
+
+   for (size_t i = 0; i < shipPipeline.storageBufferSet.buffers.size(); i++) {
+      vkDestroyBuffer(device, shipPipeline.storageBufferSet.buffers[i], nullptr);
+      vkFreeMemory(device, shipPipeline.storageBufferSet.memory[i], nullptr);
+   }
+
+   for (size_t i = 0; i < asteroidPipeline.storageBufferSet.buffers.size(); i++) {
+      vkDestroyBuffer(device, asteroidPipeline.storageBufferSet.buffers[i], nullptr);
+      vkFreeMemory(device, asteroidPipeline.storageBufferSet.memory[i], nullptr);
+   }
+
+   for (size_t i = 0; i < laserPipeline.storageBufferSet.buffers.size(); i++) {
+      vkDestroyBuffer(device, laserPipeline.storageBufferSet.buffers[i], nullptr);
+      vkFreeMemory(device, laserPipeline.storageBufferSet.memory[i], nullptr);
+   }
+
+   for (size_t i = 0; i < explosionPipeline.storageBufferSet.buffers.size(); i++) {
+      vkDestroyBuffer(device, explosionPipeline.storageBufferSet.buffers[i], nullptr);
+      vkFreeMemory(device, explosionPipeline.storageBufferSet.memory[i], nullptr);
+   }
+
    // END UNREVIEWED SECTION
 
@@ -1810,6 +1875,7 @@
 }
 
-void VulkanGame::createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags flags,
-   vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory, vector<VkDescriptorBufferInfo>& bufferInfoList) {
+void VulkanGame::createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags flags, VkMemoryPropertyFlags properties,
+                                 vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory,
+                                 vector<VkDescriptorBufferInfo>& bufferInfoList) {
    buffers.resize(swapChainImageCount);
    buffersMemory.resize(swapChainImageCount);
@@ -1817,7 +1883,5 @@
 
    for (size_t i = 0; i < swapChainImageCount; i++) {
-      VulkanUtils::createBuffer(device, physicalDevice, bufferSize, flags,
-         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
-         buffers[i], buffersMemory[i]);
+      VulkanUtils::createBuffer(device, physicalDevice, bufferSize, flags, properties, buffers[i], buffersMemory[i]);
 
       bufferInfoList[i].buffer = buffers[i];
@@ -2093,5 +2157,6 @@
    // instead of recreated every time
 
-   createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+   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, uniformBuffersMemory_modelPipeline, uniformBufferInfoList_modelPipeline);
 
@@ -2101,5 +2166,6 @@
    modelPipeline.createDescriptorSets(swapChainImages);
 
-   createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+   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, uniformBuffersMemory_shipPipeline, uniformBufferInfoList_shipPipeline);
 
@@ -2109,5 +2175,6 @@
    shipPipeline.createDescriptorSets(swapChainImages);
 
-   createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+   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, uniformBuffersMemory_asteroidPipeline, uniformBufferInfoList_asteroidPipeline);
 
@@ -2117,5 +2184,6 @@
    asteroidPipeline.createDescriptorSets(swapChainImages);
 
-   createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+   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, uniformBuffersMemory_laserPipeline, uniformBufferInfoList_laserPipeline);
 
@@ -2125,5 +2193,6 @@
    laserPipeline.createDescriptorSets(swapChainImages);
 
-   createBufferSet(sizeof(UBO_Explosion), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+   createBufferSet(sizeof(UBO_Explosion),
+      VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
       uniformBuffers_explosionPipeline, uniformBuffersMemory_explosionPipeline, uniformBufferInfoList_explosionPipeline);
 
Index: vulkan-game.hpp
===================================================================
--- vulkan-game.hpp	(revision 756162f65401bd12de652b25a35ce6e01efb144a)
+++ vulkan-game.hpp	(revision 9d21aac42227b516c5df135b6ff2266349c321e4)
@@ -142,5 +142,5 @@
 template<class VertexType, class SSBOType>
 struct EffectOverTime : public BaseEffectOverTime {
-   GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline;
+   GraphicsPipeline_Vulkan<VertexType>& pipeline;
    vector<SceneObject<VertexType, SSBOType>>& objects;
    unsigned int objectIndex;
@@ -150,13 +150,12 @@
    float changePerSecond;
 
-   EffectOverTime(GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline,
-         vector<SceneObject<VertexType, SSBOType>>& objects, unsigned int objectIndex,
-         size_t effectedFieldOffset, float startTime, float changePerSecond) :
-         pipeline(pipeline),
-         objects(objects),
-         objectIndex(objectIndex),
-         effectedFieldOffset(effectedFieldOffset),
-         startTime(startTime),
-         changePerSecond(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)
+               , objectIndex(objectIndex)
+               , effectedFieldOffset(effectedFieldOffset)
+               , startTime(startTime)
+               , changePerSecond(changePerSecond) {
       size_t ssboOffset = offset_of(&SceneObject<VertexType, SSBOType>::ssbo);
 
@@ -301,9 +300,9 @@
       // the same pipeline, but use different textures, the approach I took when initially creating GraphicsPipeline_Vulkan
       // wouldn't work since the whole pipeline couldn't have a common set of descriptors for the textures
-      GraphicsPipeline_Vulkan<ModelVertex, SSBO_ModelObject> modelPipeline;
-      GraphicsPipeline_Vulkan<ModelVertex, SSBO_ModelObject> shipPipeline;
-      GraphicsPipeline_Vulkan<ModelVertex, SSBO_Asteroid> asteroidPipeline;
-      GraphicsPipeline_Vulkan<LaserVertex, SSBO_Laser> laserPipeline;
-      GraphicsPipeline_Vulkan<ExplosionVertex, SSBO_Explosion> explosionPipeline;
+      GraphicsPipeline_Vulkan<ModelVertex> modelPipeline;
+      GraphicsPipeline_Vulkan<ModelVertex> shipPipeline;
+      GraphicsPipeline_Vulkan<ModelVertex> asteroidPipeline;
+      GraphicsPipeline_Vulkan<LaserVertex> laserPipeline;
+      GraphicsPipeline_Vulkan<ExplosionVertex> explosionPipeline;
 
       // TODO: Maybe make the ubo objects part of the pipeline class since there's only one ubo
@@ -420,16 +419,26 @@
       void cleanupImGuiOverlay();
 
-      void createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags flags,
-         vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory,
-         vector<VkDescriptorBufferInfo>& bufferInfoList);
+      // TODO: Maybe move these to a different class, possibly VulkanBuffer or some new related class
+
+      void createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags flags, VkMemoryPropertyFlags properties,
+                           vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory,
+                           vector<VkDescriptorBufferInfo>& bufferInfoList);
+
+      // TODO: See if it makes sense to rename this to resizeBufferSet() and use it to resize other types of buffers as well
+      // TODO: Remove the need for templating, which is only there so a GraphicsPupeline_Vulkan can be passed in
+      template<class VertexType, class SSBOType>
+      void resizeStorageBufferSet(StorageBufferSet& set, VkCommandPool commandPool, VkQueue graphicsQueue,
+                                  GraphicsPipeline_Vulkan<VertexType>& pipeline);
+
+      template<class SSBOType>
+      void updateStorageBuffer(StorageBufferSet& storageBufferSet, size_t objIndex, SSBOType& ssbo);
 
       // TODO: Since addObject() returns a reference to the new object now,
       // 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, SSBOType>& pipeline,
-            const vector<VertexType>& vertices, vector<uint16_t> indices, SSBOType ssbo,
-            bool pipelinesCreated);
+      SceneObject<VertexType, SSBOType>& addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
+                                                   GraphicsPipeline_Vulkan<VertexType>& pipeline,
+                                                   const vector<VertexType>& vertices, vector<uint16_t> indices,
+                                                   SSBOType ssbo, bool pipelinesCreated);
 
       template<class VertexType>
@@ -444,8 +453,8 @@
       template<class VertexType, class SSBOType>
       void updateObject(vector<SceneObject<VertexType, SSBOType>>& objects,
-            GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline, size_t index);
+            GraphicsPipeline_Vulkan<VertexType>& pipeline, size_t index);
 
       template<class VertexType, class SSBOType>
-      void updateObjectVertices(GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline,
+      void updateObjectVertices(GraphicsPipeline_Vulkan<VertexType>& pipeline,
             SceneObject<VertexType, SSBOType>& obj, size_t index);
 
@@ -485,7 +494,45 @@
 // End of specialized no-op functions
 
+template<class VertexType, class SSBOType>
+void VulkanGame::resizeStorageBufferSet(StorageBufferSet& set, VkCommandPool commandPool, VkQueue graphicsQueue,
+                                        GraphicsPipeline_Vulkan<VertexType>& pipeline) {
+   pipeline.objectCapacity *= 2;
+   VkDeviceSize bufferSize = pipeline.objectCapacity * sizeof(SSBOType);
+
+   for (size_t i = 0; i < set.buffers.size(); i++) {
+      VkBuffer newStorageBuffer;
+      VkDeviceMemory newStorageBufferMemory;
+
+      VulkanUtils::createBuffer(device, physicalDevice, bufferSize,
+         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,
+         newStorageBuffer, newStorageBufferMemory);
+
+      VulkanUtils::copyBuffer(device, commandPool, set.buffers[i], newStorageBuffer,
+         0, 0, pipeline.numObjects * sizeof(SSBOType), graphicsQueue);
+
+      vkDestroyBuffer(device, set.buffers[i], nullptr);
+      vkFreeMemory(device, set.memory[i], nullptr);
+
+      set.buffers[i] = newStorageBuffer;
+      set.memory[i] = newStorageBufferMemory;
+
+      set.infoSet[i].buffer = set.buffers[i];
+      set.infoSet[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
+      set.infoSet[i].range = bufferSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
+   }
+}
+
+// TODO: See if it makes sense to pass in the current swapchain index instead of updating all of them
+template<class SSBOType>
+void VulkanGame::updateStorageBuffer(StorageBufferSet& storageBufferSet, size_t objIndex, SSBOType& ssbo) {
+   for (size_t i = 0; i < storageBufferSet.memory.size(); i++) {
+      VulkanUtils::copyDataToMemory(device, ssbo, storageBufferSet.memory[i], objIndex * sizeof(SSBOType));
+   }
+}
+
 // TODO: Right now, it's basically necessary to pass the identity matrix in for ssbo.model
 // and to change the model matrix later by setting model_transform and then calling updateObject()
-// Figure out a better way to allow the model matrix to be set during objecting creation
+// Figure out a better way to allow the model matrix to be set during object creation
 
 // TODO: Maybe return a reference to the object from this method if I decide that updating it
@@ -494,9 +541,8 @@
 // to account for scaling
 template<class VertexType, class SSBOType>
-SceneObject<VertexType, SSBOType>& VulkanGame::addObject(
-      vector<SceneObject<VertexType, SSBOType>>& objects,
-      GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline,
-      const vector<VertexType>& vertices, vector<uint16_t> indices, SSBOType ssbo,
-      bool pipelinesCreated) {
+SceneObject<VertexType, SSBOType>& VulkanGame::addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
+                                                         GraphicsPipeline_Vulkan<VertexType>& pipeline,
+                                                         const vector<VertexType>& vertices, vector<uint16_t> indices,
+                                                         SSBOType ssbo, bool pipelinesCreated) {
    // 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 in updateObject() anyway
@@ -518,6 +564,22 @@
    }
 
-   bool storageBufferResized = pipeline.addObject(obj.vertices, obj.indices, obj.ssbo,
-      resourceCommandPool, graphicsQueue);
+   pipeline.addObject(obj.vertices, obj.indices, resourceCommandPool, graphicsQueue);
+
+   bool resizeStorageBuffer = pipeline.numObjects == pipeline.objectCapacity;
+
+   if (resizeStorageBuffer) {
+      resizeStorageBufferSet<VertexType, SSBOType>(pipeline.storageBufferSet, resourceCommandPool, graphicsQueue, pipeline);
+      pipeline.cleanup();
+
+      // Assume the SSBO is always the 2nd binding
+      pipeline.updateDescriptorInfo(1, &pipeline.storageBufferSet.infoSet);
+   }
+
+   pipeline.numObjects++;
+
+   updateStorageBuffer(pipeline.storageBufferSet, pipeline.numObjects - 1, obj.ssbo);
+
+   // TODO: Figure out why I am destroying and recreating the ubos when the swap chain is recreated,
+   // but am reusing the same ssbos. Maybe I don't need to recreate the ubos.
 
    if (pipelinesCreated) {
@@ -532,5 +594,8 @@
       // Refactor the logic to check for any resized SSBOs after all objects for the frame
       // are created and then recreate each of the corresponding pipelines only once per frame
-      if (storageBufferResized) {
+
+      // TODO: Also, verify if I actually need to recreate all of these, or maybe just the descriptor sets, for instance
+
+      if (resizeStorageBuffer) {
          pipeline.createPipeline(pipeline.vertShaderFile, pipeline.fragShaderFile);
          pipeline.createDescriptorPool(swapChainImages);
@@ -628,5 +693,5 @@
 template<class VertexType, class SSBOType>
 void VulkanGame::updateObject(vector<SceneObject<VertexType, SSBOType>>& objects,
-      GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline, size_t index) {
+      GraphicsPipeline_Vulkan<VertexType>& pipeline, size_t index) {
    SceneObject<VertexType, SSBOType>& obj = objects[index];
 
@@ -634,5 +699,5 @@
    obj.center = vec3(obj.ssbo.model * vec4(0.0f, 0.0f, 0.0f, 1.0f));
 
-   pipeline.updateObject(index, obj.ssbo);
+   updateStorageBuffer(pipeline.storageBufferSet, index, obj.ssbo);
 
    obj.modified = false;
@@ -640,5 +705,5 @@
 
 template<class VertexType, class SSBOType>
-void VulkanGame::updateObjectVertices(GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline,
+void VulkanGame::updateObjectVertices(GraphicsPipeline_Vulkan<VertexType>& pipeline,
       SceneObject<VertexType, SSBOType>& obj, size_t index) {
    pipeline.updateObjectVertices(index, obj.vertices, resourceCommandPool, graphicsQueue);
