Index: sdl-game.hpp
===================================================================
--- sdl-game.hpp	(revision 914bb995364b78b9b9cef8950e8062ab7384a491)
+++ sdl-game.hpp	(revision e8445f0578a1239a58166837998c25fe3ab0ecc3)
@@ -10,11 +10,19 @@
 #include <SDL2/SDL.h>
 
+#define GLM_FORCE_RADIANS
+#define GLM_FORCE_DEPTH_ZERO_TO_ONE // Since, in Vulkan, the depth range is 0 to 1 instead of -1 to 1
+#define GLM_FORCE_RIGHT_HANDED
+
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+
 #include "IMGUI/imgui_impl_vulkan.h"
 
 #include "consts.hpp"
 #include "vulkan-utils.hpp"
-
+#include "graphics-pipeline_vulkan.hpp"
 #include "game-gui-sdl.hpp"
 
+using namespace glm;
 using namespace std;
 using namespace std::chrono;
@@ -28,4 +36,71 @@
 #endif
 
+// TODO: Consider if there is a better way of dealing with all the vertex types and ssbo types, maybe
+// by consolidating some and trying to keep new ones to a minimum
+
+struct ModelVertex {
+   vec3 pos;
+   vec3 color;
+   vec2 texCoord;
+   vec3 normal;
+   unsigned int objIndex;
+};
+
+struct LaserVertex {
+   vec3 pos;
+   vec2 texCoord;
+   unsigned int objIndex;
+};
+
+struct ExplosionVertex {
+   vec3 particleStartVelocity;
+   float particleStartTime;
+   unsigned int objIndex;
+};
+
+struct SSBO_ModelObject {
+   alignas(16) mat4 model;
+};
+
+struct SSBO_Asteroid {
+   alignas(16) mat4 model;
+   alignas(4) float hp;
+   alignas(4) unsigned int deleted;
+};
+
+struct UBO_VP_mats {
+   alignas(16) mat4 view;
+   alignas(16) mat4 proj;
+};
+
+// TODO: Change the index type to uint32_t and check the Vulkan Tutorial loading model section as a reference
+// 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>
+struct SceneObject {
+   vector<VertexType> vertices;
+   vector<uint16_t> indices;
+   SSBOType ssbo;
+
+   mat4 model_base;
+   mat4 model_transform;
+
+   bool modified;
+
+   // TODO: Figure out if I should make child classes that have these fields instead of putting them in the
+   // parent class
+   vec3 center; // currently only matters for asteroids
+   float radius; // currently only matters for asteroids
+   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
+// 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
+// them mamdatory
+
+
+// TODO: Look into using dynamic_cast to check types of SceneObject and EffectOverTime
+
 // TODO: Maybe move this to a different header
 
@@ -60,5 +135,12 @@
          void* pUserData);
 
+      // TODO: Maybe pass these in as parameters to some Camera class
+      const float NEAR_CLIP = 0.1f;
+      const float FAR_CLIP = 100.0f;
+      const float FOV_ANGLE = 67.0f; // means the camera lens goes from -33 deg to 33 deg
+
       bool done;
+
+      vec3 cam_pos;
 
       // TODO: Good place to start using smart pointers
@@ -110,6 +192,36 @@
       bool shouldRecreateSwapChain;
 
+      VkSampler textureSampler;
+
+      VulkanImage floorTextureImage;
+      VkDescriptorImageInfo floorTextureImageDescriptor;
+
+      mat4 viewMat, projMat;
+
       // Maybe at some point create an imgui pipeline class, but I don't think it makes sense right now
       VkDescriptorPool imguiDescriptorPool;
+
+      // TODO: Probably restructure the GraphicsPipeline_Vulkan class based on what I learned about descriptors and textures
+      // while working on graphics-library. Double-check exactly what this was and note it down here.
+      // Basically, I think the point was that if I have several models that all use the same shaders and, therefore,
+      // 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;
+
+      // TODO: Maybe make the ubo objects part of the pipeline class since there's only one ubo
+      // per pipeline.
+      // Or maybe create a higher level wrapper around GraphicsPipeline_Vulkan to hold things like
+      // the objects vector, the ubo, and the ssbo
+
+      // TODO: Rename *_VP_mats to *_uniforms and possibly use different types for each one
+      // if there is a need to add other uniform variables to one or more of the shaders
+
+      vector<SceneObject<ModelVertex, SSBO_ModelObject>> modelObjects;
+
+      vector<VkBuffer> uniformBuffers_modelPipeline;
+      vector<VkDeviceMemory> uniformBuffersMemory_modelPipeline;
+      vector<VkDescriptorBufferInfo> uniformBufferInfoList_modelPipeline;
+
+      UBO_VP_mats object_VP_mats;
 
       /*** High-level vars ***/
@@ -133,5 +245,8 @@
       bool initUI(int width, int height, unsigned char guiFlags);
       void initVulkan();
+      void initGraphicsPipelines();
+      void initMatrices();
       void renderLoop();
+      void updateScene();
       void cleanup();
 
@@ -156,7 +271,35 @@
       void createSyncObjects();
 
+      void createTextureSampler();
+
       void initImGuiOverlay();
       void cleanupImGuiOverlay();
 
+      void createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags flags,
+         vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory,
+         vector<VkDescriptorBufferInfo>& bufferInfoList);
+
+      // 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);
+
+      template<class VertexType>
+      vector<VertexType> addObjectIndex(unsigned int objIndex, vector<VertexType> vertices);
+
+      template<class VertexType>
+      vector<VertexType> addVertexNormals(vector<VertexType> vertices);
+
+      template<class VertexType, class SSBOType>
+      void centerObject(SceneObject<VertexType, SSBOType>& object);
+
+      template<class VertexType, class SSBOType>
+      void updateObject(vector<SceneObject<VertexType, SSBOType>>& objects,
+         GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline, size_t index);
+
       void renderFrame(ImDrawData* draw_data);
       void presentFrame();
@@ -178,3 +321,152 @@
 };
 
+// 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
+
+// TODO: Maybe return a reference to the object from this method if I decide that updating it
+// immediately after creation is a good idea (such as setting model_base)
+// Currently, model_base is set like this in a few places and the radius is set for asteroids
+// 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) {
+   // 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
+   size_t numVertices = pipeline.getNumVertices();
+
+   for (uint16_t& idx : indices) {
+      idx += numVertices;
+   }
+
+   objects.push_back({ vertices, indices, ssbo, mat4(1.0f), mat4(1.0f), false });
+
+   SceneObject<VertexType, SSBOType>& obj = objects.back();
+
+   // TODO: Specify whether to center the object outside of this function or, worst case, maybe
+   // with a boolean being passed in here, so that I don't have to rely on checking the specific object
+   // type
+   if (!is_same_v<VertexType, LaserVertex> && !is_same_v<VertexType, ExplosionVertex>) {
+      centerObject(obj);
+   }
+
+   bool storageBufferResized = pipeline.addObject(obj.vertices, obj.indices, obj.ssbo,
+      resourceCommandPool, graphicsQueue);
+
+   if (pipelinesCreated) {
+      vkDeviceWaitIdle(device);
+
+      for (uint32_t i = 0; i < swapChainImageCount; i++) {
+         vkFreeCommandBuffers(device, commandPools[i], 1, &commandBuffers[i]);
+      }
+
+      // TODO: The pipeline recreation only has to be done once per frame where at least
+      // one SSBO is resized.
+      // 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) {
+         pipeline.createPipeline(pipeline.vertShaderFile, pipeline.fragShaderFile);
+         pipeline.createDescriptorPool(swapChainImages);
+         pipeline.createDescriptorSets(swapChainImages);
+      }
+
+      createCommandBuffers();
+   }
+
+   return obj;
+}
+
+template<class VertexType>
+vector<VertexType> VulkanGame::addObjectIndex(unsigned int objIndex, vector<VertexType> vertices) {
+   for (VertexType& vertex : vertices) {
+      vertex.objIndex = objIndex;
+   }
+
+   return vertices;
+}
+
+template<class VertexType>
+vector<VertexType> VulkanGame::addVertexNormals(vector<VertexType> vertices) {
+   for (unsigned int i = 0; i < vertices.size(); i += 3) {
+      vec3 p1 = vertices[i].pos;
+      vec3 p2 = vertices[i + 1].pos;
+      vec3 p3 = vertices[i + 2].pos;
+
+      vec3 normal = normalize(cross(p2 - p1, p3 - p1));
+
+      // Add the same normal for all 3 vertices
+      vertices[i].normal = normal;
+      vertices[i + 1].normal = normal;
+      vertices[i + 2].normal = normal;
+   }
+
+   return vertices;
+}
+
+template<class VertexType, class SSBOType>
+void VulkanGame::centerObject(SceneObject<VertexType, SSBOType>& object) {
+   vector<VertexType>& vertices = object.vertices;
+
+   float min_x = vertices[0].pos.x;
+   float max_x = vertices[0].pos.x;
+   float min_y = vertices[0].pos.y;
+   float max_y = vertices[0].pos.y;
+   float min_z = vertices[0].pos.z;
+   float max_z = vertices[0].pos.z;
+
+   // start from the second point
+   for (unsigned int i = 1; i < vertices.size(); i++) {
+      vec3& pos = vertices[i].pos;
+
+      if (min_x > pos.x) {
+         min_x = pos.x;
+      }
+      else if (max_x < pos.x) {
+         max_x = pos.x;
+      }
+
+      if (min_y > pos.y) {
+         min_y = pos.y;
+      }
+      else if (max_y < pos.y) {
+         max_y = pos.y;
+      }
+
+      if (min_z > pos.z) {
+         min_z = pos.z;
+      }
+      else if (max_z < pos.z) {
+         max_z = pos.z;
+      }
+   }
+
+   vec3 center = vec3(min_x + max_x, min_y + max_y, min_z + max_z) / 2.0f;
+
+   for (unsigned int i = 0; i < vertices.size(); i++) {
+      vertices[i].pos -= center;
+   }
+
+   object.radius = std::max(max_x - center.x, max_y - center.y);
+   object.radius = std::max(object.radius, max_z - center.z);
+
+   object.center = vec3(0.0f, 0.0f, 0.0f);
+}
+
+// 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) {
+   SceneObject<VertexType, SSBOType>& obj = objects[index];
+
+   obj.ssbo.model = obj.model_transform * obj.model_base;
+   obj.center = vec3(obj.ssbo.model * vec4(0.0f, 0.0f, 0.0f, 1.0f));
+
+   pipeline.updateObject(index, obj.ssbo);
+
+   obj.modified = false;
+}
+
 #endif // _SDL_GAME_H
