source: opengl-game/vulkan-game.hpp@ 2ba5617

feature/imgui-sdl points-test
Last change on this file since 2ba5617 was 2ba5617, checked in by Dmitry Portnoy <dmitry.portnoy@…>, 4 years ago

Center each object before, rather than after, it is copied to the GPU so that the centered vertices are copied, update an object's center when its model matrix is modified in updateObject(), mark an asteroid object as deleted when it moves offscreen, and randomize the initial z-coordinate for a new asteroid

  • Property mode set to 100644
File size: 12.5 KB
Line 
1#ifndef _VULKAN_GAME_H
2#define _VULKAN_GAME_H
3
4#include <chrono>
5
6#define GLM_FORCE_RADIANS
7#define GLM_FORCE_DEPTH_ZERO_TO_ONE // Since, in Vulkan, the depth range is 0 to 1 instead of -1 to 1
8#define GLM_FORCE_RIGHT_HANDED
9
10#include <glm/glm.hpp>
11#include <glm/gtc/matrix_transform.hpp>
12
13#include "game-gui-sdl.hpp"
14#include "graphics-pipeline_vulkan.hpp"
15
16#include "vulkan-utils.hpp"
17
18using namespace glm;
19using namespace std::chrono;
20
21#ifdef NDEBUG
22 const bool ENABLE_VALIDATION_LAYERS = false;
23#else
24 const bool ENABLE_VALIDATION_LAYERS = true;
25#endif
26
27struct OverlayVertex {
28 vec3 pos;
29 vec2 texCoord;
30};
31
32struct ModelVertex {
33 vec3 pos;
34 vec3 color;
35 vec2 texCoord;
36 unsigned int objIndex;
37};
38
39struct ShipVertex {
40 vec3 pos;
41 vec3 color;
42 vec3 normal;
43 unsigned int objIndex;
44};
45
46struct AsteroidVertex {
47 vec3 pos;
48 vec3 color;
49 vec3 normal;
50 unsigned int objIndex;
51};
52
53// TODO: Change the index type to uint32_t and check the Vulkan Tutorial loading model section as a reference
54// TODO: Create a typedef for index type so I can easily change uin16_t to something else later
55template<class VertexType, class SSBOType>
56struct SceneObject {
57 vector<VertexType> vertices;
58 vector<uint16_t> indices;
59 SSBOType ssbo;
60
61 mat4 model_base;
62 mat4 model_transform;
63 vec3 center; // currently only matters for asteroids
64 float radius; // currently only matters for asteroids
65};
66
67struct UBO_VP_mats {
68 alignas(16) mat4 view;
69 alignas(16) mat4 proj;
70};
71
72struct SSBO_ModelObject {
73 alignas(16) mat4 model;
74};
75
76struct SSBO_Asteroid {
77 alignas(16) mat4 model;
78 alignas(4) float hp;
79 alignas(4) unsigned int deleted;
80};
81
82// Have to figure out how to include an optional ssbo parameter for each object
83// Could probably use the same approach to make indices optional
84
85class VulkanGame {
86 public:
87 VulkanGame(int maxFramesInFlight);
88 ~VulkanGame();
89
90 void run(int width, int height, unsigned char guiFlags);
91
92 private:
93 const int MAX_FRAMES_IN_FLIGHT;
94
95 const float NEAR_CLIP = 0.1f;
96 const float FAR_CLIP = 100.0f;
97 const float FOV_ANGLE = 67.0f; // means the camera lens goes from -33 deg to 33 def
98
99 vec3 cam_pos;
100
101 GameGui* gui;
102
103 SDL_version sdlVersion;
104 SDL_Window* window = nullptr;
105 SDL_Renderer* renderer = nullptr;
106
107 SDL_Texture* uiOverlay = nullptr;
108
109 VkInstance instance;
110 VkDebugUtilsMessengerEXT debugMessenger;
111 VkSurfaceKHR surface; // TODO: Change the variable name to vulkanSurface
112 VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
113 VkDevice device;
114
115 VkQueue graphicsQueue;
116 VkQueue presentQueue;
117
118 VkSwapchainKHR swapChain;
119 vector<VkImage> swapChainImages;
120 VkFormat swapChainImageFormat;
121 VkExtent2D swapChainExtent;
122 vector<VkImageView> swapChainImageViews;
123 vector<VkFramebuffer> swapChainFramebuffers;
124
125 VkRenderPass renderPass;
126 VkCommandPool commandPool;
127 vector<VkCommandBuffer> commandBuffers;
128
129 VulkanImage depthImage;
130
131 VkSampler textureSampler;
132
133 VulkanImage floorTextureImage;
134 VkDescriptorImageInfo floorTextureImageDescriptor;
135
136 VulkanImage sdlOverlayImage;
137 VkDescriptorImageInfo sdlOverlayImageDescriptor;
138
139 TTF_Font* font;
140 SDL_Texture* fontSDLTexture;
141
142 SDL_Texture* imageSDLTexture;
143
144 vector<VkSemaphore> imageAvailableSemaphores;
145 vector<VkSemaphore> renderFinishedSemaphores;
146 vector<VkFence> inFlightFences;
147
148 size_t currentFrame;
149
150 bool framebufferResized;
151
152 mat4 viewMat, projMat;
153
154 GraphicsPipeline_Vulkan<OverlayVertex, void*> overlayPipeline;
155 vector<SceneObject<OverlayVertex, void*>> overlayObjects;
156
157 // TODO: Maybe make the ubo objects part of the pipeline class since there's only one ubo
158 // per pipeline.
159 // Or maybe create a higher level wrapper around GraphicsPipeline_Vulkan to hold things like
160 // the objects vector, the ubo, and the ssbo
161
162 // TODO: Rename *_VP_mats to *_uniforms and possibly use different types for each one
163 // if there is a need to add other uniform variables to one or more of the shaders
164
165 GraphicsPipeline_Vulkan<ModelVertex, SSBO_ModelObject> modelPipeline;
166 vector<SceneObject<ModelVertex, SSBO_ModelObject>> modelObjects;
167
168 vector<VkBuffer> uniformBuffers_modelPipeline;
169 vector<VkDeviceMemory> uniformBuffersMemory_modelPipeline;
170 vector<VkDescriptorBufferInfo> uniformBufferInfoList_modelPipeline;
171
172 UBO_VP_mats object_VP_mats;
173
174 GraphicsPipeline_Vulkan<ShipVertex, SSBO_ModelObject> shipPipeline;
175 vector<SceneObject<ShipVertex, SSBO_ModelObject>> shipObjects;
176
177 vector<VkBuffer> uniformBuffers_shipPipeline;
178 vector<VkDeviceMemory> uniformBuffersMemory_shipPipeline;
179 vector<VkDescriptorBufferInfo> uniformBufferInfoList_shipPipeline;
180
181 UBO_VP_mats ship_VP_mats;
182
183 GraphicsPipeline_Vulkan<AsteroidVertex, SSBO_Asteroid> asteroidPipeline;
184 vector<SceneObject<AsteroidVertex, SSBO_Asteroid>> asteroidObjects;
185
186 vector<VkBuffer> uniformBuffers_asteroidPipeline;
187 vector<VkDeviceMemory> uniformBuffersMemory_asteroidPipeline;
188 vector<VkDescriptorBufferInfo> uniformBufferInfoList_asteroidPipeline;
189
190 UBO_VP_mats asteroid_VP_mats;
191
192 time_point<steady_clock> startTime;
193 float curTime, prevTime, elapsedTime;
194
195 float shipSpeed = 0.5f;
196 float asteroidSpeed = 2.0f;
197
198 float spawnRate_asteroid = 0.5;
199 float lastSpawn_asteroid;
200
201 bool initWindow(int width, int height, unsigned char guiFlags);
202 void initVulkan();
203 void initGraphicsPipelines();
204 void initMatrices();
205 void mainLoop();
206 void updateScene(uint32_t currentImage);
207 void renderUI();
208 void renderScene();
209 void cleanup();
210
211 void createVulkanInstance(const vector<const char*> &validationLayers);
212 void setupDebugMessenger();
213 void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo);
214 void createVulkanSurface();
215 void pickPhysicalDevice(const vector<const char*>& deviceExtensions);
216 bool isDeviceSuitable(VkPhysicalDevice physicalDevice, const vector<const char*>& deviceExtensions);
217 void createLogicalDevice(
218 const vector<const char*> validationLayers,
219 const vector<const char*>& deviceExtensions);
220 void createSwapChain();
221 void createImageViews();
222 void createRenderPass();
223 VkFormat findDepthFormat();
224 void createCommandPool();
225 void createImageResources();
226
227 void createTextureSampler();
228 void createFramebuffers();
229 void createCommandBuffers();
230 void createSyncObjects();
231
232 template<class VertexType, class SSBOType>
233 void addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
234 GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline,
235 const vector<VertexType>& vertices, vector<uint16_t> indices, SSBOType ssbo,
236 bool pipelinesCreated);
237
238 template<class VertexType, class SSBOType>
239 void updateObject(vector<SceneObject<VertexType, SSBOType>>& objects,
240 GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline, size_t index);
241
242 template<class VertexType>
243 vector<VertexType> addVertexNormals(vector<VertexType> vertices);
244
245 template<class VertexType>
246 vector<VertexType> addObjectIndex(unsigned int objIndex, vector<VertexType> vertices);
247
248 template<class VertexType, class SSBOType>
249 void centerObject(SceneObject<VertexType, SSBOType>& object);
250
251 void createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags flags,
252 vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory, vector<VkDescriptorBufferInfo>& bufferInfoList);
253
254 void recreateSwapChain();
255
256 void cleanupSwapChain();
257
258 static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
259 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
260 VkDebugUtilsMessageTypeFlagsEXT messageType,
261 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
262 void* pUserData);
263};
264
265// TODO: Right now, it's basically necessary to pass the identity matrix in for ssbo.model
266// and to change the model matrix later by setting model_transform and then calling updateObject()
267// Figure out a better way to allow the model matrix to be set during objecting creation
268
269// TODO: Maybe return a reference to the object from this method if I decide that updating it
270// immediately after creation is a good idea (such as setting model_base)
271// Currently, model_base is set like this in a few places and the radius is set for asteroids
272// to account for scaling
273template<class VertexType, class SSBOType>
274void VulkanGame::addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
275 GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline,
276 const vector<VertexType>& vertices, vector<uint16_t> indices, SSBOType ssbo,
277 bool pipelinesCreated) {
278 // TODO: Use the model field of ssbo to set the object's model_base
279 // currently, the passed in model is useless since it gets overridden in updateObject() anyway
280 size_t numVertices = pipeline.getNumVertices();
281
282 for (uint16_t& idx : indices) {
283 idx += numVertices;
284 }
285
286 objects.push_back({ vertices, indices, ssbo, mat4(1.0f), mat4(1.0f) });
287
288 SceneObject<VertexType, SSBOType>& obj = objects.back();
289 centerObject(obj);
290
291 bool storageBufferResized = pipeline.addObject(obj.vertices, obj.indices, obj.ssbo, commandPool, graphicsQueue);
292
293 if (pipelinesCreated) {
294 vkDeviceWaitIdle(device);
295 vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
296
297 // TODO: The pipeline recreation only has to be done once per frame where at least
298 // one SSBO is resized.
299 // Refactor the logic to check for any resized SSBOs after all objects for the frame
300 // are created and then recreate each of the corresponding pipelines only once per frame
301 if (storageBufferResized) {
302 pipeline.createPipeline(pipeline.vertShaderFile, pipeline.fragShaderFile);
303 pipeline.createDescriptorPool(swapChainImages);
304 pipeline.createDescriptorSets(swapChainImages);
305 }
306
307 createCommandBuffers();
308 }
309}
310
311// TODO: Just pass in the single object instead of a list of all of them
312template<class VertexType, class SSBOType>
313void VulkanGame::updateObject(vector<SceneObject<VertexType, SSBOType>>& objects,
314 GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline, size_t index) {
315 SceneObject<VertexType, SSBOType>& obj = objects[index];
316
317 obj.ssbo.model = obj.model_transform * obj.model_base;
318 obj.center = vec3(obj.ssbo.model * vec4(0.0f, 0.0f, 0.0f, 1.0f));
319
320 pipeline.updateObject(index, obj.ssbo);
321}
322
323template<class VertexType>
324vector<VertexType> VulkanGame::addVertexNormals(vector<VertexType> vertices) {
325 for (unsigned int i = 0; i < vertices.size(); i += 3) {
326 vec3 p1 = vertices[i].pos;
327 vec3 p2 = vertices[i+1].pos;
328 vec3 p3 = vertices[i+2].pos;
329
330 vec3 normal = normalize(cross(p2 - p1, p3 - p1));
331
332 // Add the same normal for all 3 vertices
333 vertices[i].normal = normal;
334 vertices[i+1].normal = normal;
335 vertices[i+2].normal = normal;
336 }
337
338 return vertices;
339}
340
341template<class VertexType>
342vector<VertexType> VulkanGame::addObjectIndex(unsigned int objIndex, vector<VertexType> vertices) {
343 for (VertexType& vertex : vertices) {
344 vertex.objIndex = objIndex;
345 }
346
347 return vertices;
348}
349
350template<class VertexType, class SSBOType>
351void VulkanGame::centerObject(SceneObject<VertexType, SSBOType>& object) {
352 vector<VertexType>& vertices = object.vertices;
353
354 float min_x = vertices[0].pos.x;
355 float max_x = vertices[0].pos.x;
356 float min_y = vertices[0].pos.y;
357 float max_y = vertices[0].pos.y;
358 float min_z = vertices[0].pos.z;
359 float max_z = vertices[0].pos.z;
360
361 // start from the second point
362 for (unsigned int i = 1; i < vertices.size(); i++) {
363 vec3& pos = vertices[i].pos;
364
365 if (min_x > pos.x) {
366 min_x = pos.x;
367 } else if (max_x < pos.x) {
368 max_x = pos.x;
369 }
370
371 if (min_y > pos.y) {
372 min_y = pos.y;
373 } else if (max_y < pos.y) {
374 max_y = pos.y;
375 }
376
377 if (min_z > pos.z) {
378 min_z = pos.z;
379 } else if (max_z < pos.z) {
380 max_z = pos.z;
381 }
382 }
383
384 vec3 center = vec3(min_x + max_x, min_y + max_y, min_z + max_z) / 2.0f;
385
386 for (unsigned int i = 0; i < vertices.size(); i++) {
387 vertices[i].pos -= center;
388 }
389
390 object.radius = std::max(max_x - center.x, max_y - center.y);
391 object.radius = std::max(object.radius, max_z - center.z);
392
393 object.center = vec3(0.0f, 0.0f, 0.0f);
394}
395
396#endif // _VULKAN_GAME_H
Note: See TracBrowser for help on using the repository browser.