source: opengl-game/vulkan-game.hpp@ 5ba732a

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

Add a modified flag to SceneObject and, instead of copying an object's data to the graphics card immediately after it is updated, instead set the flag to true and update all modified objects at once right before rendering

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