source: opengl-game/vulkan-game.cpp@ 5192672

feature/imgui-sdl
Last change on this file since 5192672 was 5192672, checked in by Dmitry Portnoy <dportnoy@…>, 4 years ago

Add ui-value\.hpp to the VulkanGame project and make curTime an instance variable of VulkanGame instead of a global

  • Property mode set to 100644
File size: 90.3 KB
Line 
1#include "vulkan-game.hpp"
2
3#include <array>
4#include <iostream>
5#include <numeric>
6#include <set>
7#include <stdexcept>
8
9#include "IMGUI/imgui_impl_sdl.h"
10
11#include "logger.hpp"
12
13#include "utils.hpp"
14
15#include "gui/main-screen.hpp"
16#include "gui/game-screen.hpp"
17
18using namespace std;
19
20// TODO: Update all instances of the "... != VK_SUCCESS" check to something similar to
21// the agp error checking function, which prints an appropriate error message based on the error code
22
23// TODO: Update all occurances of instance variables to use this-> (Actually, not sure if I really want to do this)
24
25/* TODO: Try doing the following tasks based on the Vulkan implementation of IMGUI (Also maybe looks at Sascha Willems' code to see how he does these things)
26 *
27 * - When recreating the swapchain, pass the old one in and destroy the old one after the new one is created
28 * - Recreate semaphores when recreating the swapchain
29 * - imgui uses one image acquired and one render complete sem and once fence per frame\
30 * - IMGUI creates one command pool per framebuffer
31 */
32
33/* NOTES WHEN ADDING IMGUI
34 *
35 * Possibly cleanup the imgui pipeline in cleanupSwapchain or call some imgui function that does this for me
36 * call ImGui_ImplVulkan_RenderDrawData, without passing in a pipeline, to do the rendering
37 */
38
39static void check_imgui_vk_result(VkResult res) {
40 if (res == VK_SUCCESS) {
41 return;
42 }
43
44 ostringstream oss;
45 oss << "[imgui] Vulkan error! VkResult is \"" << VulkanUtils::resultString(res) << "\"" << __LINE__;
46 if (res < 0) {
47 throw runtime_error("Fatal: " + oss.str());
48 } else {
49 cerr << oss.str();
50 }
51}
52
53VulkanGame::VulkanGame() {
54 // TODO: Double-check whether initialization should happen in the header, where the variables are declared, or here
55 // Also, decide whether to use this-> for all instance variables, or only when necessary
56
57 debugMessenger = VK_NULL_HANDLE;
58
59 gui = nullptr;
60 window = nullptr;
61
62 swapChainPresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR;
63 swapChainMinImageCount = 0;
64
65 currentFrame = 0;
66 shouldRecreateSwapChain = false;
67
68 object_VP_mats = {};
69 ship_VP_mats = {};
70 asteroid_VP_mats = {};
71 laser_VP_mats = {};
72 explosion_UBO = {};
73}
74
75VulkanGame::~VulkanGame() {
76}
77
78void VulkanGame::run(int width, int height, unsigned char guiFlags) {
79 seedRandomNums();
80
81 cout << "DEBUGGING IS " << (ENABLE_VALIDATION_LAYERS ? "ON" : "OFF") << endl;
82
83 cout << "Vulkan Game" << endl;
84
85 this->score = 0;
86
87 if (initUI(width, height, guiFlags) == RTWO_ERROR) {
88 return;
89 }
90
91 // TODO: Maybe make a struct of properties to share with each screen instead of passing
92 // in all of VulkanGame
93 screens[SCREEN_MAIN] = new MainScreen(*renderer, *this);
94 screens[SCREEN_GAME] = new GameScreen(*renderer, *this);
95
96 currentScreen = screens[SCREEN_MAIN];
97
98 initVulkan();
99 mainLoop();
100 cleanup();
101
102 close_log();
103}
104
105void VulkanGame::goToScreen(Screen* screen) {
106 currentScreen = screen;
107 currentScreen->init();
108
109 // TODO: Maybe just set shouldRecreateSwapChain to true instead. Check this render loop logic
110 // to make sure there'd be no issues
111 recreateSwapChain();
112}
113
114void VulkanGame::quitGame() {
115 done = true;
116}
117
118bool VulkanGame::initUI(int width, int height, unsigned char guiFlags) {
119 // TODO: Create a game-gui function to get the gui version and retrieve it that way
120
121 SDL_VERSION(&sdlVersion); // This gets the compile-time version
122 SDL_GetVersion(&sdlVersion); // This gets the runtime version
123
124 cout << "SDL "<<
125 to_string(sdlVersion.major) << "." <<
126 to_string(sdlVersion.minor) << "." <<
127 to_string(sdlVersion.patch) << endl;
128
129 // TODO: Refactor the logger api to be more flexible,
130 // esp. since gl_log() and gl_log_err() have issues printing anything besides strings
131 restart_gl_log();
132 gl_log("starting SDL\n%s.%s.%s",
133 to_string(sdlVersion.major).c_str(),
134 to_string(sdlVersion.minor).c_str(),
135 to_string(sdlVersion.patch).c_str());
136
137 // TODO: Use open_Log() and related functions instead of gl_log ones
138 // TODO: In addition, delete the gl_log functions
139 open_log();
140 get_log() << "starting SDL" << endl;
141 get_log() <<
142 (int)sdlVersion.major << "." <<
143 (int)sdlVersion.minor << "." <<
144 (int)sdlVersion.patch << endl;
145
146 // TODO: Put all fonts, textures, and images in the assets folder
147 gui = new GameGui_SDL();
148
149 if (gui->init() == RTWO_ERROR) {
150 // TODO: Also print these sorts of errors to the log
151 cout << "UI library could not be initialized!" << endl;
152 cout << gui->getError() << endl;
153 return RTWO_ERROR;
154 }
155
156 window = (SDL_Window*) gui->createWindow("Vulkan Game", width, height, guiFlags & GUI_FLAGS_WINDOW_FULLSCREEN);
157 if (window == nullptr) {
158 cout << "Window could not be created!" << endl;
159 cout << gui->getError() << endl;
160 return RTWO_ERROR;
161 }
162
163 cout << "Target window size: (" << width << ", " << height << ")" << endl;
164 cout << "Actual window size: (" << gui->getWindowWidth() << ", " << gui->getWindowHeight() << ")" << endl;
165
166 renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
167 if (renderer == nullptr) {
168 cout << "Renderer could not be created!" << endl;
169 cout << gui->getError() << endl;
170 return RTWO_ERROR;
171 }
172
173 uiOverlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
174 gui->getWindowWidth(), gui->getWindowHeight());
175
176 if (uiOverlay == nullptr) {
177 cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << endl;
178 return RTWO_ERROR;
179 }
180 if (SDL_SetTextureBlendMode(uiOverlay, SDL_BLENDMODE_BLEND) != 0) {
181 cout << "Unable to set texture blend mode! SDL Error: " << SDL_GetError() << endl;
182 return RTWO_ERROR;
183 }
184
185 SDL_SetRenderTarget(renderer, uiOverlay);
186
187 // TODO: Print the filename of the font in the error message
188
189 lazyFont = TTF_OpenFont("assets/fonts/lazy.ttf", 28);
190 if (lazyFont == nullptr) {
191 cout << "Failed to load lazy font! SDL_ttf Error: " << TTF_GetError() << endl;
192 return RTWO_ERROR;
193 }
194
195 proggyFont = TTF_OpenFont("assets/fonts/ProggyClean.ttf", 16);
196 if (proggyFont == nullptr) {
197 cout << "Failed to load proggy font! SDL_ttf Error: " << TTF_GetError() << endl;
198 return RTWO_ERROR;
199 }
200
201 return RTWO_SUCCESS;
202}
203
204void VulkanGame::initVulkan() {
205 const vector<const char*> validationLayers = {
206 "VK_LAYER_KHRONOS_validation"
207 };
208 const vector<const char*> deviceExtensions = {
209 VK_KHR_SWAPCHAIN_EXTENSION_NAME
210 };
211
212 createVulkanInstance(validationLayers);
213 setupDebugMessenger();
214 createVulkanSurface();
215 pickPhysicalDevice(deviceExtensions);
216 createLogicalDevice(validationLayers, deviceExtensions);
217 chooseSwapChainProperties();
218 createSwapChain();
219 createImageViews();
220 createRenderPass();
221
222 createResourceCommandPool();
223 createCommandPools();
224
225 createImageResources();
226 createFramebuffers();
227
228 // TODO: I think I can start setting up IMGUI here
229 // ImGui_ImplVulkan_Init will create the Vulkan pipeline for ImGui for me
230 // imgui_impl_vulkan keeps track of the imgui pipeline internally
231 // TODO: Check how the example recreates the pipeline and what code I need
232 // to copy over to do that
233
234 createImguiDescriptorPool();
235
236 IMGUI_CHECKVERSION();
237 ImGui::CreateContext();
238 ImGuiIO& io = ImGui::GetIO(); (void)io;
239 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
240 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
241
242 // Setup Dear ImGui style
243 ImGui::StyleColorsDark();
244 //ImGui::StyleColorsClassic();
245
246 // TODO: Maybe call this once and save the results since it's also called when creating the logical device
247 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
248
249 ImGui_ImplSDL2_InitForVulkan(window);
250 ImGui_ImplVulkan_InitInfo init_info = {};
251 init_info.Instance = this->instance;
252 init_info.PhysicalDevice = this->physicalDevice;
253 init_info.Device = this->device;
254 init_info.QueueFamily = indices.graphicsFamily.value();
255 init_info.Queue = graphicsQueue;
256 init_info.DescriptorPool = this->imguiDescriptorPool; // TODO: Create a descriptor pool for IMGUI
257 init_info.Allocator = nullptr;
258 init_info.MinImageCount = this->swapChainMinImageCount;
259 init_info.ImageCount = this->swapChainImageCount;
260 init_info.CheckVkResultFn = check_imgui_vk_result;
261 ImGui_ImplVulkan_Init(&init_info, this->renderPass);
262
263 cout << "Got here" << endl;
264
265 // TODO: I think I have code in VkUtil for creating VkImages, which uses command buffers
266 // Maybe check how that code works
267
268 // Upload Fonts
269 {
270 VkCommandBuffer command_buffer;
271
272 // Create the command buffer to load
273 VkCommandBufferAllocateInfo info = {};
274 info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
275 info.commandPool = resourceCommandPool;
276 info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
277 info.commandBufferCount = 1;
278
279 VKUTIL_CHECK_RESULT(vkAllocateCommandBuffers(this->device, &info, &command_buffer),
280 "failed to allocate command buffers!");
281
282 //err = vkResetCommandPool(this->device, command_pool, 0); // Probably not really needed here since the command pool is never used before this
283 //check_vk_result(err);
284 VkCommandBufferBeginInfo begin_info = {};
285 begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
286 begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
287 VKUTIL_CHECK_RESULT(vkBeginCommandBuffer(command_buffer, &begin_info),
288 "failed to begin recording command buffer!");
289
290 ImGui_ImplVulkan_CreateFontsTexture(command_buffer);
291
292 VkSubmitInfo end_info = {};
293 end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
294 end_info.commandBufferCount = 1;
295 end_info.pCommandBuffers = &command_buffer;
296
297 VKUTIL_CHECK_RESULT(vkEndCommandBuffer(command_buffer),
298 "failed to record command buffer!");
299
300 VKUTIL_CHECK_RESULT(vkQueueSubmit(this->graphicsQueue, 1, &end_info, VK_NULL_HANDLE),
301 "failed to submit draw command buffer!");
302
303 if (vkDeviceWaitIdle(this->device) != VK_SUCCESS) {
304 throw runtime_error("failed to wait for device!");
305 }
306
307 ImGui_ImplVulkan_DestroyFontUploadObjects();
308
309 // This should make the command pool reusable for later
310 VKUTIL_CHECK_RESULT(vkResetCommandPool(this->device, resourceCommandPool, 0),
311 "failed to reset command pool!");
312 }
313
314 cout << "And now here" << endl;
315
316 initMatrices();
317
318 // TODO: Figure out how much of ubo creation and associated variables should be in the pipeline class
319 // Maybe combine the ubo-related objects into a new class
320
321 initGraphicsPipelines();
322
323 overlayPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&OverlayVertex::pos));
324 overlayPipeline.addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&OverlayVertex::texCoord));
325
326 overlayPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
327 VK_SHADER_STAGE_FRAGMENT_BIT, &sdlOverlayImageDescriptor);
328
329 addObject(overlayObjects, overlayPipeline,
330 {
331 {{-1.0f, 1.0f, 0.0f}, {0.0f, 1.0f}},
332 {{ 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
333 {{ 1.0f, -1.0f, 0.0f}, {1.0f, 0.0f}},
334 {{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f}}
335 }, {
336 0, 1, 2, 2, 3, 0
337 }, {}, false);
338
339 overlayPipeline.createDescriptorSetLayout();
340 overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
341 overlayPipeline.createDescriptorPool(swapChainImages);
342 overlayPipeline.createDescriptorSets(swapChainImages);
343
344 modelPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::pos));
345 modelPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::color));
346 modelPipeline.addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&ModelVertex::texCoord));
347 modelPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ModelVertex::objIndex));
348
349 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
350 uniformBuffers_modelPipeline, uniformBuffersMemory_modelPipeline, uniformBufferInfoList_modelPipeline);
351
352 modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
353 VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_modelPipeline);
354 modelPipeline.addStorageDescriptor(VK_SHADER_STAGE_VERTEX_BIT);
355 modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
356 VK_SHADER_STAGE_FRAGMENT_BIT, &floorTextureImageDescriptor);
357
358 SceneObject<ModelVertex, SSBO_ModelObject>* texturedSquare = nullptr;
359
360 texturedSquare = &addObject(modelObjects, modelPipeline,
361 addObjectIndex<ModelVertex>(modelObjects.size(), {
362 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
363 {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
364 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
365 {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
366 }), {
367 0, 1, 2, 2, 3, 0
368 }, {
369 mat4(1.0f)
370 }, false);
371
372 texturedSquare->model_base =
373 translate(mat4(1.0f), vec3(0.0f, 0.0f, -2.0f));
374 texturedSquare->modified = true;
375
376 texturedSquare = &addObject(modelObjects, modelPipeline,
377 addObjectIndex<ModelVertex>(modelObjects.size(), {
378 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
379 {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
380 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
381 {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
382 }), {
383 0, 1, 2, 2, 3, 0
384 }, {
385 mat4(1.0f)
386 }, false);
387
388 texturedSquare->model_base =
389 translate(mat4(1.0f), vec3(0.0f, 0.0f, -1.5f));
390 texturedSquare->modified = true;
391
392 modelPipeline.createDescriptorSetLayout();
393 modelPipeline.createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
394 modelPipeline.createDescriptorPool(swapChainImages);
395 modelPipeline.createDescriptorSets(swapChainImages);
396
397 shipPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ShipVertex::pos));
398 shipPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ShipVertex::color));
399 shipPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ShipVertex::normal));
400 shipPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ShipVertex::objIndex));
401
402 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
403 uniformBuffers_shipPipeline, uniformBuffersMemory_shipPipeline, uniformBufferInfoList_shipPipeline);
404
405 shipPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
406 VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_shipPipeline);
407 shipPipeline.addStorageDescriptor(VK_SHADER_STAGE_VERTEX_BIT);
408
409 // TODO: With the normals, indexing basically becomes pointless since no vertices will have exactly
410 // the same data. Add an option to make some pipelines not use indexing
411 SceneObject<ShipVertex, SSBO_ModelObject>& ship = addObject(shipObjects, shipPipeline,
412 addObjectIndex<ShipVertex>(shipObjects.size(),
413 addVertexNormals<ShipVertex>({
414
415 //back
416 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
417 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
418 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
419 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
420 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
421 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
422
423 // left back
424 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
425 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
426 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
427 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
428 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
429 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
430
431 // right back
432 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
433 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
434 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
435 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
436 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
437 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
438
439 // left mid
440 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
441 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
442 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
443 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
444 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
445 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
446
447 // right mid
448 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
449 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
450 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
451 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
452 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
453 {{ 0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
454
455 // left front
456 {{ 0.0f, 0.0f, -3.5f}, {0.0f, 0.0f, 1.0f}},
457 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
458 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
459
460 // right front
461 {{ 0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
462 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
463 {{ 0.0f, 0.0f, -3.5f}, {0.0f, 0.0f, 1.0f}},
464
465 // top back
466 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
467 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 1.0f}},
468 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 1.0f}},
469 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
470 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 1.0f}},
471 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
472
473 // bottom back
474 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
475 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
476 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
477 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
478 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
479 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
480
481 // top mid
482 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
483 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
484 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
485 {{ -0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
486 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
487 {{ 0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
488
489 // bottom mid
490 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
491 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
492 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
493 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
494 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
495 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
496
497 // top front
498 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
499 {{ 0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
500 {{ 0.0f, 0.0f, -3.5f}, {0.0f, 0.0f, 0.3f}},
501
502 // bottom front
503 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
504 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
505 {{ 0.0f, 0.0f, -3.5f}, {0.0f, 0.0f, 0.3f}},
506
507 // left wing start back
508 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
509 {{ -1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
510 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
511 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
512 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
513 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
514
515 // left wing start top
516 {{ -0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
517 {{ -1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
518 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
519 {{ -0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
520 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
521 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
522
523 // left wing start front
524 {{ -0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
525 {{ -0.5f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
526 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
527 {{ -0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
528 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
529 {{ -1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
530
531 // left wing start bottom
532 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
533 {{ -1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
534 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
535 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
536 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
537 {{ -0.5f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
538
539 // left wing end outside
540 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
541 {{ -2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
542 {{ -1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
543
544 // left wing end top
545 {{ -1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
546 {{ -2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
547 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
548
549 // left wing end front
550 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
551 {{ -2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
552 {{ -1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
553
554 // left wing end bottom
555 {{ -1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
556 {{ -2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
557 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
558
559 // right wing start back
560 {{ 1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
561 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
562 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
563 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
564 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
565 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
566
567 // right wing start top
568 {{ 1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
569 {{ 0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
570 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
571 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
572 {{ 0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
573 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
574
575 // right wing start front
576 {{ 0.5f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
577 {{ 0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
578 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
579 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
580 {{ 0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
581 {{ 1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
582
583 // right wing start bottom
584 {{ 1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
585 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
586 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
587 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
588 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
589 {{ 0.5f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
590
591 // right wing end outside
592 {{ 2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
593 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
594 {{ 1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
595
596 // right wing end top
597 {{ 2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
598 {{ 1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
599 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
600
601 // right wing end front
602 {{ 2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
603 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
604 {{ 1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
605
606 // right wing end bottom
607 {{ 2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
608 {{ 1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
609 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
610 })), {
611 0, 1, 2, 3, 4, 5,
612 6, 7, 8, 9, 10, 11,
613 12, 13, 14, 15, 16, 17,
614 18, 19, 20, 21, 22, 23,
615 24, 25, 26, 27, 28, 29,
616 30, 31, 32,
617 33, 34, 35,
618 36, 37, 38, 39, 40, 41,
619 42, 43, 44, 45, 46, 47,
620 48, 49, 50, 51, 52, 53,
621 54, 55, 56, 57, 58, 59,
622 60, 61, 62,
623 63, 64, 65,
624 66, 67, 68, 69, 70, 71,
625 72, 73, 74, 75, 76, 77,
626 78, 79, 80, 81, 82, 83,
627 84, 85, 86, 87, 88, 89,
628 90, 91, 92,
629 93, 94, 95,
630 96, 97, 98,
631 99, 100, 101,
632 102, 103, 104, 105, 106, 107,
633 108, 109, 110, 111, 112, 113,
634 114, 115, 116, 117, 118, 119,
635 120, 121, 122, 123, 124, 125,
636 126, 127, 128,
637 129, 130, 131,
638 132, 133, 134,
639 135, 136, 137,
640 }, {
641 mat4(1.0f)
642 }, false);
643
644 ship.model_base =
645 translate(mat4(1.0f), vec3(0.0f, -1.2f, 1.65f)) *
646 scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
647 ship.modified = true;
648
649 shipPipeline.createDescriptorSetLayout();
650 shipPipeline.createPipeline("shaders/ship-vert.spv", "shaders/ship-frag.spv");
651 shipPipeline.createDescriptorPool(swapChainImages);
652 shipPipeline.createDescriptorSets(swapChainImages);
653
654 asteroidPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&AsteroidVertex::pos));
655 asteroidPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&AsteroidVertex::color));
656 asteroidPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&AsteroidVertex::normal));
657 asteroidPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&AsteroidVertex::objIndex));
658
659 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
660 uniformBuffers_asteroidPipeline, uniformBuffersMemory_asteroidPipeline, uniformBufferInfoList_asteroidPipeline);
661
662 asteroidPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
663 VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_asteroidPipeline);
664 asteroidPipeline.addStorageDescriptor(VK_SHADER_STAGE_VERTEX_BIT);
665
666 asteroidPipeline.createDescriptorSetLayout();
667 asteroidPipeline.createPipeline("shaders/asteroid-vert.spv", "shaders/asteroid-frag.spv");
668 asteroidPipeline.createDescriptorPool(swapChainImages);
669 asteroidPipeline.createDescriptorSets(swapChainImages);
670
671 laserPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&LaserVertex::pos));
672 laserPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&LaserVertex::texCoord));
673 laserPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&LaserVertex::objIndex));
674
675 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
676 uniformBuffers_laserPipeline, uniformBuffersMemory_laserPipeline, uniformBufferInfoList_laserPipeline);
677
678 laserPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
679 VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_laserPipeline);
680 laserPipeline.addStorageDescriptor(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
681 laserPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
682 VK_SHADER_STAGE_FRAGMENT_BIT, &laserTextureImageDescriptor);
683
684 laserPipeline.createDescriptorSetLayout();
685 laserPipeline.createPipeline("shaders/laser-vert.spv", "shaders/laser-frag.spv");
686 laserPipeline.createDescriptorPool(swapChainImages);
687 laserPipeline.createDescriptorSets(swapChainImages);
688
689 explosionPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ExplosionVertex::particleStartVelocity));
690 explosionPipeline.addAttribute(VK_FORMAT_R32_SFLOAT, offset_of(&ExplosionVertex::particleStartTime));
691 explosionPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ExplosionVertex::objIndex));
692
693 createBufferSet(sizeof(UBO_Explosion), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
694 uniformBuffers_explosionPipeline, uniformBuffersMemory_explosionPipeline, uniformBufferInfoList_explosionPipeline);
695
696 explosionPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
697 VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_explosionPipeline);
698 explosionPipeline.addStorageDescriptor(VK_SHADER_STAGE_VERTEX_BIT);
699
700 explosionPipeline.createDescriptorSetLayout();
701 explosionPipeline.createPipeline("shaders/explosion-vert.spv", "shaders/explosion-frag.spv");
702 explosionPipeline.createDescriptorPool(swapChainImages);
703 explosionPipeline.createDescriptorSets(swapChainImages);
704
705 cout << "Created all the graphics pipelines" << endl;
706
707 createCommandBuffers();
708
709 createSyncObjects();
710
711 cout << "Finished init function" << endl;
712}
713
714void VulkanGame::initGraphicsPipelines() {
715 overlayPipeline = GraphicsPipeline_Vulkan<OverlayVertex, void*>(
716 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
717 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 4, 6, 0);
718
719 modelPipeline = GraphicsPipeline_Vulkan<ModelVertex, SSBO_ModelObject>(
720 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
721 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 16, 24, 10);
722
723 shipPipeline = GraphicsPipeline_Vulkan<ShipVertex, SSBO_ModelObject>(
724 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
725 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 138, 138, 10);
726
727 asteroidPipeline = GraphicsPipeline_Vulkan<AsteroidVertex, SSBO_Asteroid>(
728 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
729 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 24, 36, 10);
730
731 laserPipeline = GraphicsPipeline_Vulkan<LaserVertex, SSBO_Laser>(
732 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
733 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 8, 18, 2);
734
735 explosionPipeline = GraphicsPipeline_Vulkan<ExplosionVertex, SSBO_Explosion>(
736 VK_PRIMITIVE_TOPOLOGY_POINT_LIST, physicalDevice, device, renderPass,
737 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height },
738 swapChainImages, EXPLOSION_PARTICLE_COUNT, EXPLOSION_PARTICLE_COUNT, 2);
739}
740
741// TODO: Maybe changes the name to initScene() or something similar
742void VulkanGame::initMatrices() {
743 this->cam_pos = vec3(0.0f, 0.0f, 2.0f);
744
745 float cam_yaw = 0.0f;
746 float cam_pitch = -50.0f;
747
748 mat4 yaw_mat = rotate(mat4(1.0f), radians(-cam_yaw), vec3(0.0f, 1.0f, 0.0f));
749 mat4 pitch_mat = rotate(mat4(1.0f), radians(-cam_pitch), vec3(1.0f, 0.0f, 0.0f));
750
751 mat4 R_view = pitch_mat * yaw_mat;
752 mat4 T_view = translate(mat4(1.0f), vec3(-this->cam_pos.x, -this->cam_pos.y, -this->cam_pos.z));
753 viewMat = R_view * T_view;
754
755 projMat = perspective(radians(FOV_ANGLE), (float)swapChainExtent.width / (float)swapChainExtent.height, NEAR_CLIP, FAR_CLIP);
756 projMat[1][1] *= -1; // flip the y-axis so that +y is up
757
758 object_VP_mats.view = viewMat;
759 object_VP_mats.proj = projMat;
760
761 ship_VP_mats.view = viewMat;
762 ship_VP_mats.proj = projMat;
763
764 asteroid_VP_mats.view = viewMat;
765 asteroid_VP_mats.proj = projMat;
766
767 laser_VP_mats.view = viewMat;
768 laser_VP_mats.proj = projMat;
769
770 explosion_UBO.view = viewMat;
771 explosion_UBO.proj = projMat;
772}
773
774void VulkanGame::mainLoop() {
775 this->startTime = high_resolution_clock::now();
776 curTime = duration<float, seconds::period>(high_resolution_clock::now() - this->startTime).count();
777
778 this->fpsStartTime = curTime;
779 this->frameCount = 0;
780
781 lastSpawn_asteroid = curTime;
782
783 ImGuiIO& io = ImGui::GetIO();
784
785 done = false;
786 while (!done) {
787
788 this->prevTime = curTime;
789 curTime = duration<float, seconds::period>(high_resolution_clock::now() - this->startTime).count();
790 this->elapsedTime = curTime - this->prevTime;
791
792 if (curTime - this->fpsStartTime >= 1.0f) {
793 this->fps = (float)frameCount / (curTime - this->fpsStartTime);
794
795 this->frameCount = 0;
796 this->fpsStartTime = curTime;
797 }
798
799 this->frameCount++;
800
801 gui->processEvents();
802
803 UIEvent uiEvent;
804 while (gui->pollEvent(&uiEvent)) {
805 GameEvent& e = uiEvent.event;
806 SDL_Event sdlEvent = uiEvent.rawEvent.sdl;
807
808 ImGui_ImplSDL2_ProcessEvent(&sdlEvent);
809 if (io.WantCaptureMouse &&
810 (e.type == UI_EVENT_MOUSEBUTTONDOWN || e.type == UI_EVENT_MOUSEBUTTONUP || e.type == UI_EVENT_UNKNOWN)) {
811 if (sdlEvent.type == SDL_MOUSEWHEEL || sdlEvent.type == SDL_MOUSEBUTTONDOWN || sdlEvent.type == SDL_MOUSEBUTTONUP) {
812 continue;
813 }
814 }
815 if (io.WantCaptureKeyboard &&
816 (e.type == UI_EVENT_KEYDOWN || e.type == UI_EVENT_KEYUP)) {
817 if (sdlEvent.type == SDL_KEYDOWN || sdlEvent.type == SDL_KEYUP) {
818 continue;
819 }
820 }
821 if (io.WantTextInput) {
822 // show onscreen keyboard if on mobile
823 }
824
825 switch (e.type) {
826 case UI_EVENT_QUIT:
827 cout << "Quit event detected" << endl;
828 done = true;
829 break;
830 case UI_EVENT_WINDOW:
831 cout << "Window event detected" << endl;
832 // Currently unused
833 break;
834 case UI_EVENT_WINDOWRESIZE:
835 cout << "Window resize event detected" << endl;
836 shouldRecreateSwapChain = true;
837 break;
838 case UI_EVENT_KEYDOWN:
839 if (e.key.repeat) {
840 break;
841 }
842
843 if (e.key.keycode == SDL_SCANCODE_ESCAPE) {
844 done = true;
845 } else if (e.key.keycode == SDL_SCANCODE_SPACE) {
846 cout << "Adding a plane" << endl;
847 float zOffset = -2.0f + (0.5f * modelObjects.size());
848
849 SceneObject<ModelVertex, SSBO_ModelObject>& texturedSquare =
850 addObject(modelObjects, modelPipeline,
851 addObjectIndex<ModelVertex>(modelObjects.size(), {
852 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
853 {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
854 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
855 {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
856 }), {
857 0, 1, 2, 2, 3, 0
858 }, {
859 mat4(1.0f)
860 }, true);
861
862 texturedSquare.model_base =
863 translate(mat4(1.0f), vec3(0.0f, 0.0f, zOffset));
864 texturedSquare.modified = true;
865 } else if (e.key.keycode == SDL_SCANCODE_Z && leftLaserIdx == -1) {
866 // TODO: When I start actually removing objects from the object vectors,
867 // I will need to update the indices since they might become incorrect
868 // or invalid as objects get moved around
869
870 vec3 offset(shipObjects[0].model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
871
872 addLaser(
873 vec3(-0.21f, -1.19f, 1.76f) + offset,
874 vec3(-0.21f, -1.19f, -3.0f) + offset,
875 LASER_COLOR, 0.03f);
876
877 leftLaserIdx = laserObjects.size() - 1;
878 } else if (e.key.keycode == SDL_SCANCODE_X && rightLaserIdx == -1) {
879 vec3 offset(shipObjects[0].model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
880
881 addLaser(
882 vec3(0.21f, -1.19f, 1.76f) + offset,
883 vec3(0.21f, -1.19f, -3.0f) + offset,
884 LASER_COLOR, 0.03f);
885
886 rightLaserIdx = laserObjects.size() - 1;
887 } else {
888 cout << "Key event detected" << endl;
889 }
890 break;
891 case UI_EVENT_KEYUP:
892 if (e.key.keycode == SDL_SCANCODE_Z && leftLaserIdx != -1) {
893 laserObjects[leftLaserIdx].ssbo.deleted = true;
894 laserObjects[leftLaserIdx].modified = true;
895 leftLaserIdx = -1;
896
897 if (leftLaserEffect != nullptr) {
898 leftLaserEffect->deleted = true;
899 leftLaserEffect = nullptr;
900 }
901 } else if (e.key.keycode == SDL_SCANCODE_X && rightLaserIdx != -1) {
902 laserObjects[rightLaserIdx].ssbo.deleted = true;
903 laserObjects[rightLaserIdx].modified = true;
904 rightLaserIdx = -1;
905
906 if (rightLaserEffect != nullptr) {
907 rightLaserEffect->deleted = true;
908 rightLaserEffect = nullptr;
909 }
910 }
911 break;
912 case UI_EVENT_MOUSEBUTTONDOWN:
913 case UI_EVENT_MOUSEBUTTONUP:
914 case UI_EVENT_MOUSEMOTION:
915 break;
916 case UI_EVENT_UNKNOWN:
917 //cout << "Unknown event type: 0x" << hex << e.unknown.eventType << dec << endl;
918 break;
919 default:
920 cout << "Unhandled UI event: " << e.type << endl;
921 }
922
923 currentScreen->handleEvent(e);
924 }
925
926 // Check which keys are held down
927
928 SceneObject<ShipVertex, SSBO_ModelObject>& ship = shipObjects[0];
929
930 if (gui->keyPressed(SDL_SCANCODE_LEFT)) {
931 float distance = -this->shipSpeed * this->elapsedTime;
932
933 ship.model_transform = translate(mat4(1.0f), vec3(distance, 0.0f, 0.0f))
934 * shipObjects[0].model_transform;
935 ship.modified = true;
936
937 if (leftLaserIdx != -1) {
938 translateLaser(leftLaserIdx, vec3(distance, 0.0f, 0.0f));
939 }
940 if (rightLaserIdx != -1) {
941 translateLaser(rightLaserIdx, vec3(distance, 0.0f, 0.0f));
942 }
943 } else if (gui->keyPressed(SDL_SCANCODE_RIGHT)) {
944 float distance = this->shipSpeed * this->elapsedTime;
945
946 ship.model_transform = translate(mat4(1.0f), vec3(distance, 0.0f, 0.0f))
947 * shipObjects[0].model_transform;
948 ship.modified = true;
949
950 if (leftLaserIdx != -1) {
951 translateLaser(leftLaserIdx, vec3(distance, 0.0f, 0.0f));
952 }
953 if (rightLaserIdx != -1) {
954 translateLaser(rightLaserIdx, vec3(distance, 0.0f, 0.0f));
955 }
956 }
957
958 if (shouldRecreateSwapChain) {
959 gui->refreshWindowSize();
960 const bool isMinimized = gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0;
961
962 if (!isMinimized) {
963 // TODO: This should be used if the min image count changes, presumably because a new surface was created
964 // with a different image count or something like that. Maybe I want to add code to query for a new min image count
965 // during swapchain recreation to take advantage of this
966 ImGui_ImplVulkan_SetMinImageCount(swapChainMinImageCount);
967
968 recreateSwapChain();
969
970 imageIndex = 0;
971 shouldRecreateSwapChain = false;
972 }
973 }
974
975 currentScreen->renderUI();
976
977 // Copy the UI image to a vulkan texture
978 // TODO: I'm pretty sure this severely slows down the pipeline since this functions waits for the copy to be
979 // complete before continuing. See if I can find a more efficient method.
980 VulkanUtils::populateVulkanImageFromSDLTexture(device, physicalDevice, resourceCommandPool, uiOverlay, renderer,
981 sdlOverlayImage, graphicsQueue);
982
983 updateScene();
984
985 ImGui_ImplVulkan_NewFrame();
986 ImGui_ImplSDL2_NewFrame(window);
987 ImGui::NewFrame();
988
989 {
990 ImGui::SetNextWindowSize(ImVec2(250, 35), ImGuiCond_Once);
991 ImGui::SetNextWindowPos(ImVec2(380, 10), ImGuiCond_Once);
992 ImGui::Begin("WndMenubar", NULL,
993 ImGuiWindowFlags_NoTitleBar |
994 ImGuiWindowFlags_NoResize |
995 ImGuiWindowFlags_NoMove);
996 ImGui::InvisibleButton("", ImVec2(155, 18));
997 ImGui::SameLine();
998 if (ImGui::Button("Main Menu")) {
999 cout << "Clicked on the main button" << endl;
1000 //events.push(Event::GO_TO_MAIN_MENU);
1001 }
1002 ImGui::End();
1003 }
1004
1005 ImGui::Render();
1006
1007 // There is already code in renderFrame to render screen-specific things
1008 // The imgui code above should be moved to the renderUI function of the particular screen it's needed in
1009 // and currentScreen->renderUI should be called in renderFrame.
1010 // Since I am no longer drawing the UI to an sdl texture and then rendering that, I don't have to worry
1011 // about calling populateVulkanImageFromSDLTexture at all.
1012 // If I do ever want to do that again, I can still actually do it inside renderFrame
1013
1014 gui->refreshWindowSize();
1015 const bool isMinimized = gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0;
1016
1017 if (!isMinimized) {
1018 renderFrame(ImGui::GetDrawData());
1019 presentFrame();
1020 }
1021 }
1022}
1023
1024// TODO: The only updates that need to happen once per Vulkan image are the SSBO ones,
1025// which are already handled by updateObject(). Move this code to a different place,
1026// where it will run just once per frame
1027void VulkanGame::updateScene() {
1028 for (SceneObject<ModelVertex, SSBO_ModelObject>& model : this->modelObjects) {
1029 model.model_transform =
1030 translate(mat4(1.0f), vec3(0.0f, -2.0f, -0.0f)) *
1031 rotate(mat4(1.0f), curTime * radians(90.0f), vec3(0.0f, 0.0f, 1.0f));
1032 model.modified = true;
1033 }
1034
1035 if (leftLaserIdx != -1) {
1036 updateLaserTarget(leftLaserIdx);
1037 }
1038 if (rightLaserIdx != -1) {
1039 updateLaserTarget(rightLaserIdx);
1040 }
1041
1042 for (vector<BaseEffectOverTime*>::iterator it = effects.begin(); it != effects.end(); ) {
1043 if ((*it)->deleted) {
1044 delete *it;
1045 it = effects.erase(it);
1046 } else {
1047 BaseEffectOverTime* eot = *it;
1048
1049 eot->applyEffect(curTime);
1050
1051 it++;
1052 }
1053 }
1054
1055 for (SceneObject<AsteroidVertex, SSBO_Asteroid>& asteroid : this->asteroidObjects) {
1056 if (!asteroid.ssbo.deleted) {
1057 vec3 objCenter = vec3(viewMat * vec4(asteroid.center, 1.0f));
1058
1059 if (asteroid.ssbo.hp <= 0.0f) {
1060 asteroid.ssbo.deleted = true;
1061
1062 // TODO: Optimize this so I don't recalculate the camera rotation every time
1063 // TODO: Also, avoid re-declaring cam_pitch
1064 float cam_pitch = -50.0f;
1065 mat4 pitch_mat = rotate(mat4(1.0f), radians(cam_pitch), vec3(1.0f, 0.0f, 0.0f));
1066 mat4 model_mat = translate(mat4(1.0f), asteroid.center) * pitch_mat;
1067
1068 addExplosion(model_mat, 0.5f, curTime);
1069
1070 this->score++;
1071 } else if ((objCenter.z - asteroid.radius) > -NEAR_CLIP) {
1072 asteroid.ssbo.deleted = true;
1073 } else {
1074 asteroid.model_transform =
1075 translate(mat4(1.0f), vec3(0.0f, 0.0f, this->asteroidSpeed * this->elapsedTime)) *
1076 asteroid.model_transform;
1077 }
1078
1079 asteroid.modified = true;
1080 }
1081 }
1082
1083 if (curTime - this->lastSpawn_asteroid > this->spawnRate_asteroid) {
1084 this->lastSpawn_asteroid = curTime;
1085
1086 SceneObject<AsteroidVertex, SSBO_Asteroid>& asteroid = addObject(
1087 asteroidObjects, asteroidPipeline,
1088 addObjectIndex<AsteroidVertex>(asteroidObjects.size(),
1089 addVertexNormals<AsteroidVertex>({
1090
1091 // front
1092 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1093 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1094 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1095 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1096 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1097 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1098
1099 // top
1100 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1101 {{-1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1102 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1103 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1104 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1105 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1106
1107 // bottom
1108 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1109 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1110 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1111 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1112 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1113 {{ 1.0f, -1.0f, -1.0}, {0.4f, 0.4f, 0.4f}},
1114
1115 // back
1116 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1117 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1118 {{-1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1119 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1120 {{ 1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1121 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1122
1123 // right
1124 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1125 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1126 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1127 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1128 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1129 {{ 1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1130
1131 // left
1132 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1133 {{-1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1134 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1135 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1136 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1137 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1138 })), {
1139 0, 1, 2, 3, 4, 5,
1140 6, 7, 8, 9, 10, 11,
1141 12, 13, 14, 15, 16, 17,
1142 18, 19, 20, 21, 22, 23,
1143 24, 25, 26, 27, 28, 29,
1144 30, 31, 32, 33, 34, 35,
1145 }, {
1146 mat4(1.0f),
1147 10.0f,
1148 false
1149 }, true);
1150
1151 // This accounts for the scaling in model_base.
1152 // Dividing by 8 instead of 10 since the bounding radius algorithm
1153 // under-calculates the true value.
1154 // TODO: Figure out the best way to take scaling into account when calculating the radius
1155 // Keep in mind that the main complicating factor is the currently poor radius calculation
1156 asteroid.radius /= 8.0f;
1157
1158 asteroid.model_base =
1159 translate(mat4(1.0f), vec3(getRandomNum(-1.3f, 1.3f), -1.2f, getRandomNum(-5.5f, -4.5f))) *
1160 rotate(mat4(1.0f), radians(60.0f), vec3(1.0f, 1.0f, -1.0f)) *
1161 scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
1162 asteroid.modified = true;
1163 }
1164
1165 for (SceneObject<ExplosionVertex, SSBO_Explosion>& explosion : this->explosionObjects) {
1166 if (!explosion.ssbo.deleted) {
1167 if (curTime > (explosion.ssbo.explosionStartTime + explosion.ssbo.explosionDuration)) {
1168 explosion.ssbo.deleted = true;
1169 explosion.modified = true;
1170 }
1171 }
1172 }
1173
1174 for (size_t i = 0; i < shipObjects.size(); i++) {
1175 if (shipObjects[i].modified) {
1176 updateObject(shipObjects, shipPipeline, i);
1177 }
1178 }
1179
1180 for (size_t i = 0; i < modelObjects.size(); i++) {
1181 if (modelObjects[i].modified) {
1182 updateObject(modelObjects, modelPipeline, i);
1183 }
1184 }
1185
1186 for (size_t i = 0; i < asteroidObjects.size(); i++) {
1187 if (asteroidObjects[i].modified) {
1188 updateObject(asteroidObjects, asteroidPipeline, i);
1189 }
1190 }
1191
1192 for (size_t i = 0; i < laserObjects.size(); i++) {
1193 if (laserObjects[i].modified) {
1194 updateObject(laserObjects, laserPipeline, i);
1195 }
1196 }
1197
1198 for (size_t i = 0; i < explosionObjects.size(); i++) {
1199 if (explosionObjects[i].modified) {
1200 updateObject(explosionObjects, explosionPipeline, i);
1201 }
1202 }
1203
1204 explosion_UBO.cur_time = curTime;
1205
1206 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_modelPipeline[imageIndex], 0, object_VP_mats);
1207
1208 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_shipPipeline[imageIndex], 0, ship_VP_mats);
1209
1210 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_asteroidPipeline[imageIndex], 0, asteroid_VP_mats);
1211
1212 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_laserPipeline[imageIndex], 0, laser_VP_mats);
1213
1214 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_explosionPipeline[imageIndex], 0, explosion_UBO);
1215}
1216
1217void VulkanGame::cleanup() {
1218 VKUTIL_CHECK_RESULT(vkDeviceWaitIdle(device), "failed to wait for device!");
1219
1220 ImGui_ImplVulkan_Shutdown();
1221 ImGui_ImplSDL2_Shutdown();
1222 ImGui::DestroyContext();
1223
1224 // TODO: Probably move this into cleanupSwapChain once I finish the integration
1225 destroyImguiDescriptorPool();
1226
1227 cleanupSwapChain();
1228
1229 VulkanUtils::destroyVulkanImage(device, sdlOverlayImage);
1230 VulkanUtils::destroyVulkanImage(device, floorTextureImage);
1231 VulkanUtils::destroyVulkanImage(device, laserTextureImage);
1232
1233 vkDestroySampler(device, textureSampler, nullptr);
1234
1235 modelPipeline.cleanupBuffers();
1236 overlayPipeline.cleanupBuffers();
1237 shipPipeline.cleanupBuffers();
1238 asteroidPipeline.cleanupBuffers();
1239 laserPipeline.cleanupBuffers();
1240 explosionPipeline.cleanupBuffers();
1241
1242 vkDestroyCommandPool(device, resourceCommandPool, nullptr);
1243
1244 vkDestroyDevice(device, nullptr);
1245 vkDestroySurfaceKHR(instance, surface, nullptr);
1246
1247 if (ENABLE_VALIDATION_LAYERS) {
1248 VulkanUtils::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
1249 }
1250
1251 vkDestroyInstance(instance, nullptr);
1252
1253 delete screens[SCREEN_MAIN];
1254 delete screens[SCREEN_GAME];
1255
1256 if (lazyFont != nullptr) {
1257 TTF_CloseFont(lazyFont);
1258 lazyFont = nullptr;
1259 }
1260
1261 if (proggyFont != nullptr) {
1262 TTF_CloseFont(proggyFont);
1263 proggyFont = nullptr;
1264 }
1265
1266 if (uiOverlay != nullptr) {
1267 SDL_DestroyTexture(uiOverlay);
1268 uiOverlay = nullptr;
1269 }
1270
1271 SDL_DestroyRenderer(renderer);
1272 renderer = nullptr;
1273
1274 gui->destroyWindow();
1275 gui->shutdown();
1276 delete gui;
1277}
1278
1279void VulkanGame::createVulkanInstance(const vector<const char*>& validationLayers) {
1280 if (ENABLE_VALIDATION_LAYERS && !VulkanUtils::checkValidationLayerSupport(validationLayers)) {
1281 throw runtime_error("validation layers requested, but not available!");
1282 }
1283
1284 VkApplicationInfo appInfo = {};
1285 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
1286 appInfo.pApplicationName = "Vulkan Game";
1287 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
1288 appInfo.pEngineName = "No Engine";
1289 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
1290 appInfo.apiVersion = VK_API_VERSION_1_0;
1291
1292 VkInstanceCreateInfo createInfo = {};
1293 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
1294 createInfo.pApplicationInfo = &appInfo;
1295
1296 vector<const char*> extensions = gui->getRequiredExtensions();
1297 if (ENABLE_VALIDATION_LAYERS) {
1298 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
1299 }
1300
1301 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
1302 createInfo.ppEnabledExtensionNames = extensions.data();
1303
1304 cout << endl << "Extensions:" << endl;
1305 for (const char* extensionName : extensions) {
1306 cout << extensionName << endl;
1307 }
1308 cout << endl;
1309
1310 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
1311 if (ENABLE_VALIDATION_LAYERS) {
1312 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
1313 createInfo.ppEnabledLayerNames = validationLayers.data();
1314
1315 populateDebugMessengerCreateInfo(debugCreateInfo);
1316 createInfo.pNext = &debugCreateInfo;
1317 } else {
1318 createInfo.enabledLayerCount = 0;
1319
1320 createInfo.pNext = nullptr;
1321 }
1322
1323 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
1324 throw runtime_error("failed to create instance!");
1325 }
1326}
1327
1328void VulkanGame::setupDebugMessenger() {
1329 if (!ENABLE_VALIDATION_LAYERS) {
1330 return;
1331 }
1332
1333 VkDebugUtilsMessengerCreateInfoEXT createInfo;
1334 populateDebugMessengerCreateInfo(createInfo);
1335
1336 if (VulkanUtils::createDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
1337 throw runtime_error("failed to set up debug messenger!");
1338 }
1339}
1340
1341void VulkanGame::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
1342 createInfo = {};
1343 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
1344 createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
1345 createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
1346 createInfo.pfnUserCallback = debugCallback;
1347}
1348
1349VKAPI_ATTR VkBool32 VKAPI_CALL VulkanGame::debugCallback(
1350 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
1351 VkDebugUtilsMessageTypeFlagsEXT messageType,
1352 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
1353 void* pUserData) {
1354 cerr << "validation layer: " << pCallbackData->pMessage << endl;
1355
1356 // TODO: Figure out what the return value means and if it should always be VK_FALSE
1357 return VK_FALSE;
1358}
1359
1360void VulkanGame::createVulkanSurface() {
1361 if (gui->createVulkanSurface(instance, &surface) == RTWO_ERROR) {
1362 throw runtime_error("failed to create window surface!");
1363 }
1364}
1365
1366void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
1367 uint32_t deviceCount = 0;
1368 // TODO: Check VkResult
1369 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
1370
1371 if (deviceCount == 0) {
1372 throw runtime_error("failed to find GPUs with Vulkan support!");
1373 }
1374
1375 vector<VkPhysicalDevice> devices(deviceCount);
1376 // TODO: Check VkResult
1377 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
1378
1379 cout << endl << "Graphics cards:" << endl;
1380 for (const VkPhysicalDevice& device : devices) {
1381 if (isDeviceSuitable(device, deviceExtensions)) {
1382 physicalDevice = device;
1383 break;
1384 }
1385 }
1386 cout << endl;
1387
1388 if (physicalDevice == VK_NULL_HANDLE) {
1389 throw runtime_error("failed to find a suitable GPU!");
1390 }
1391}
1392
1393bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice, const vector<const char*>& deviceExtensions) {
1394 VkPhysicalDeviceProperties deviceProperties;
1395 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
1396
1397 cout << "Device: " << deviceProperties.deviceName << endl;
1398
1399 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1400 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
1401 bool swapChainAdequate = false;
1402
1403 if (extensionsSupported) {
1404 vector<VkSurfaceFormatKHR> formats = VulkanUtils::querySwapChainFormats(physicalDevice, surface);
1405 vector<VkPresentModeKHR> presentModes = VulkanUtils::querySwapChainPresentModes(physicalDevice, surface);
1406
1407 swapChainAdequate = !formats.empty() && !presentModes.empty();
1408 }
1409
1410 VkPhysicalDeviceFeatures supportedFeatures;
1411 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
1412
1413 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
1414}
1415
1416void VulkanGame::createLogicalDevice(const vector<const char*>& validationLayers,
1417 const vector<const char*>& deviceExtensions) {
1418 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1419
1420 if (!indices.isComplete()) {
1421 throw runtime_error("failed to find required queue families!");
1422 }
1423
1424 // TODO: Using separate graphics and present queues currently works, but I should verify that I'm
1425 // using them correctly to get the most benefit out of separate queues
1426
1427 vector<VkDeviceQueueCreateInfo> queueCreateInfoList;
1428 set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1429
1430 float queuePriority = 1.0f;
1431 for (uint32_t queueFamily : uniqueQueueFamilies) {
1432 VkDeviceQueueCreateInfo queueCreateInfo = {};
1433 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1434 queueCreateInfo.queueCount = 1;
1435 queueCreateInfo.queueFamilyIndex = queueFamily;
1436 queueCreateInfo.pQueuePriorities = &queuePriority;
1437
1438 queueCreateInfoList.push_back(queueCreateInfo);
1439 }
1440
1441 VkPhysicalDeviceFeatures deviceFeatures = {};
1442 deviceFeatures.samplerAnisotropy = VK_TRUE;
1443
1444 VkDeviceCreateInfo createInfo = {};
1445 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
1446
1447 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfoList.size());
1448 createInfo.pQueueCreateInfos = queueCreateInfoList.data();
1449
1450 createInfo.pEnabledFeatures = &deviceFeatures;
1451
1452 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
1453 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
1454
1455 // These fields are ignored by up-to-date Vulkan implementations,
1456 // but it's a good idea to set them for backwards compatibility
1457 if (ENABLE_VALIDATION_LAYERS) {
1458 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
1459 createInfo.ppEnabledLayerNames = validationLayers.data();
1460 } else {
1461 createInfo.enabledLayerCount = 0;
1462 }
1463
1464 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
1465 throw runtime_error("failed to create logical device!");
1466 }
1467
1468 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
1469 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
1470}
1471
1472void VulkanGame::chooseSwapChainProperties() {
1473 vector<VkSurfaceFormatKHR> availableFormats = VulkanUtils::querySwapChainFormats(physicalDevice, surface);
1474 vector<VkPresentModeKHR> availablePresentModes = VulkanUtils::querySwapChainPresentModes(physicalDevice, surface);
1475
1476 swapChainSurfaceFormat = VulkanUtils::chooseSwapSurfaceFormat(availableFormats,
1477 { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM },
1478 VK_COLOR_SPACE_SRGB_NONLINEAR_KHR);
1479
1480 vector<VkPresentModeKHR> presentModes{
1481 VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR
1482 };
1483 //vector<VkPresentModeKHR> presentModes{ VK_PRESENT_MODE_FIFO_KHR };
1484
1485 swapChainPresentMode = VulkanUtils::chooseSwapPresentMode(availablePresentModes, presentModes);
1486
1487 cout << "[vulkan] Selected PresentMode = " << swapChainPresentMode << endl;
1488
1489 VkSurfaceCapabilitiesKHR capabilities = VulkanUtils::querySwapChainCapabilities(physicalDevice, surface);
1490
1491 if (swapChainPresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
1492 swapChainMinImageCount = 3;
1493 } else if (swapChainPresentMode == VK_PRESENT_MODE_FIFO_KHR || swapChainPresentMode == VK_PRESENT_MODE_FIFO_RELAXED_KHR) {
1494 swapChainMinImageCount = 2;
1495 } else if (swapChainPresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
1496 swapChainMinImageCount = 1;
1497 } else {
1498 throw runtime_error("unexpected present mode!");
1499 }
1500
1501 if (swapChainMinImageCount < capabilities.minImageCount) {
1502 swapChainMinImageCount = capabilities.minImageCount;
1503 } else if (capabilities.maxImageCount != 0 && swapChainMinImageCount > capabilities.maxImageCount) {
1504 swapChainMinImageCount = capabilities.maxImageCount;
1505 }
1506}
1507
1508void VulkanGame::createSwapChain() {
1509 VkSurfaceCapabilitiesKHR capabilities = VulkanUtils::querySwapChainCapabilities(physicalDevice, surface);
1510
1511 swapChainExtent = VulkanUtils::chooseSwapExtent(capabilities, gui->getWindowWidth(), gui->getWindowHeight());
1512
1513 VkSwapchainCreateInfoKHR createInfo = {};
1514 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1515 createInfo.surface = surface;
1516 createInfo.minImageCount = swapChainMinImageCount;
1517 createInfo.imageFormat = swapChainSurfaceFormat.format;
1518 createInfo.imageColorSpace = swapChainSurfaceFormat.colorSpace;
1519 createInfo.imageExtent = swapChainExtent;
1520 createInfo.imageArrayLayers = 1;
1521 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1522
1523 // TODO: Maybe save this result so I don't have to recalculate it every time
1524 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1525 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1526
1527 if (indices.graphicsFamily != indices.presentFamily) {
1528 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
1529 createInfo.queueFamilyIndexCount = 2;
1530 createInfo.pQueueFamilyIndices = queueFamilyIndices;
1531 } else {
1532 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1533 createInfo.queueFamilyIndexCount = 0;
1534 createInfo.pQueueFamilyIndices = nullptr;
1535 }
1536
1537 createInfo.preTransform = capabilities.currentTransform;
1538 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1539 createInfo.presentMode = swapChainPresentMode;
1540 createInfo.clipped = VK_TRUE;
1541 createInfo.oldSwapchain = VK_NULL_HANDLE;
1542
1543 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
1544 throw runtime_error("failed to create swap chain!");
1545 }
1546
1547 if (vkGetSwapchainImagesKHR(device, swapChain, &swapChainImageCount, nullptr) != VK_SUCCESS) {
1548 throw runtime_error("failed to get swap chain image count!");
1549 }
1550
1551 swapChainImages.resize(swapChainImageCount);
1552 if (vkGetSwapchainImagesKHR(device, swapChain, &swapChainImageCount, swapChainImages.data()) != VK_SUCCESS) {
1553 throw runtime_error("failed to get swap chain images!");
1554 }
1555}
1556
1557void VulkanGame::createImageViews() {
1558 swapChainImageViews.resize(swapChainImageCount);
1559
1560 for (size_t i = 0; i < swapChainImageCount; i++) {
1561 swapChainImageViews[i] = VulkanUtils::createImageView(device, swapChainImages[i], swapChainSurfaceFormat.format,
1562 VK_IMAGE_ASPECT_COLOR_BIT);
1563 }
1564}
1565
1566void VulkanGame::createRenderPass() {
1567 VkAttachmentDescription colorAttachment = {};
1568 colorAttachment.format = swapChainSurfaceFormat.format;
1569 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
1570 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1571 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1572 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1573 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1574 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1575 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1576
1577 VkAttachmentReference colorAttachmentRef = {};
1578 colorAttachmentRef.attachment = 0;
1579 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1580
1581 VkAttachmentDescription depthAttachment = {};
1582 depthAttachment.format = findDepthFormat();
1583 depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
1584 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1585 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1586 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1587 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1588 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1589 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1590
1591 VkAttachmentReference depthAttachmentRef = {};
1592 depthAttachmentRef.attachment = 1;
1593 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1594
1595 VkSubpassDescription subpass = {};
1596 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1597 subpass.colorAttachmentCount = 1;
1598 subpass.pColorAttachments = &colorAttachmentRef;
1599 subpass.pDepthStencilAttachment = &depthAttachmentRef;
1600
1601 VkSubpassDependency dependency = {};
1602 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
1603 dependency.dstSubpass = 0;
1604 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1605 dependency.srcAccessMask = 0;
1606 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1607 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1608
1609 array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
1610 VkRenderPassCreateInfo renderPassInfo = {};
1611 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1612 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1613 renderPassInfo.pAttachments = attachments.data();
1614 renderPassInfo.subpassCount = 1;
1615 renderPassInfo.pSubpasses = &subpass;
1616 renderPassInfo.dependencyCount = 1;
1617 renderPassInfo.pDependencies = &dependency;
1618
1619 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
1620 throw runtime_error("failed to create render pass!");
1621 }
1622}
1623
1624VkFormat VulkanGame::findDepthFormat() {
1625 return VulkanUtils::findSupportedFormat(
1626 physicalDevice,
1627 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
1628 VK_IMAGE_TILING_OPTIMAL,
1629 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
1630 );
1631}
1632
1633void VulkanGame::createResourceCommandPool() {
1634 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1635
1636 VkCommandPoolCreateInfo poolInfo = {};
1637 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1638 poolInfo.queueFamilyIndex = indices.graphicsFamily.value();
1639 poolInfo.flags = 0;
1640
1641 if (vkCreateCommandPool(device, &poolInfo, nullptr, &resourceCommandPool) != VK_SUCCESS) {
1642 throw runtime_error("failed to create resource command pool!");
1643 }
1644}
1645
1646void VulkanGame::createCommandPools() {
1647 commandPools.resize(swapChainImageCount);
1648
1649 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1650
1651 for (size_t i = 0; i < swapChainImageCount; i++) {
1652 VkCommandPoolCreateInfo poolInfo = {};
1653 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1654 poolInfo.queueFamilyIndex = indices.graphicsFamily.value();
1655 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
1656
1657 VKUTIL_CHECK_RESULT(vkCreateCommandPool(device, &poolInfo, nullptr, &commandPools[i]),
1658 "failed to create graphics command pool!");
1659 }
1660}
1661
1662void VulkanGame::createImageResources() {
1663 VulkanUtils::createDepthImage(device, physicalDevice, resourceCommandPool, findDepthFormat(), swapChainExtent,
1664 depthImage, graphicsQueue);
1665
1666 createTextureSampler();
1667
1668 // TODO: Move all images/textures somewhere into the assets folder
1669
1670 VulkanUtils::createVulkanImageFromSDLTexture(device, physicalDevice, uiOverlay, sdlOverlayImage);
1671
1672 sdlOverlayImageDescriptor = {};
1673 sdlOverlayImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1674 sdlOverlayImageDescriptor.imageView = sdlOverlayImage.imageView;
1675 sdlOverlayImageDescriptor.sampler = textureSampler;
1676
1677 VulkanUtils::createVulkanImageFromFile(device, physicalDevice, resourceCommandPool, "textures/texture.jpg",
1678 floorTextureImage, graphicsQueue);
1679
1680 floorTextureImageDescriptor = {};
1681 floorTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1682 floorTextureImageDescriptor.imageView = floorTextureImage.imageView;
1683 floorTextureImageDescriptor.sampler = textureSampler;
1684
1685 VulkanUtils::createVulkanImageFromFile(device, physicalDevice, resourceCommandPool, "textures/laser.png",
1686 laserTextureImage, graphicsQueue);
1687
1688 laserTextureImageDescriptor = {};
1689 laserTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1690 laserTextureImageDescriptor.imageView = laserTextureImage.imageView;
1691 laserTextureImageDescriptor.sampler = textureSampler;
1692}
1693
1694void VulkanGame::createTextureSampler() {
1695 VkSamplerCreateInfo samplerInfo = {};
1696 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
1697 samplerInfo.magFilter = VK_FILTER_LINEAR;
1698 samplerInfo.minFilter = VK_FILTER_LINEAR;
1699
1700 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1701 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1702 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1703
1704 samplerInfo.anisotropyEnable = VK_TRUE;
1705 samplerInfo.maxAnisotropy = 16;
1706 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
1707 samplerInfo.unnormalizedCoordinates = VK_FALSE;
1708 samplerInfo.compareEnable = VK_FALSE;
1709 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
1710 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
1711 samplerInfo.mipLodBias = 0.0f;
1712 samplerInfo.minLod = 0.0f;
1713 samplerInfo.maxLod = 0.0f;
1714
1715 if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
1716 throw runtime_error("failed to create texture sampler!");
1717 }
1718}
1719
1720void VulkanGame::createFramebuffers() {
1721 swapChainFramebuffers.resize(swapChainImageCount);
1722
1723 VkFramebufferCreateInfo framebufferInfo = {};
1724 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1725 framebufferInfo.renderPass = renderPass;
1726 framebufferInfo.width = swapChainExtent.width;
1727 framebufferInfo.height = swapChainExtent.height;
1728 framebufferInfo.layers = 1;
1729
1730 for (uint32_t i = 0; i < swapChainImageCount; i++) {
1731 array<VkImageView, 2> attachments = {
1732 swapChainImageViews[i],
1733 depthImage.imageView
1734 };
1735
1736 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1737 framebufferInfo.pAttachments = attachments.data();
1738
1739 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
1740 throw runtime_error("failed to create framebuffer!");
1741 }
1742 }
1743}
1744
1745void VulkanGame::createCommandBuffers() {
1746 commandBuffers.resize(swapChainImageCount);
1747
1748 for (size_t i = 0; i < swapChainImageCount; i++) {
1749 VkCommandBufferAllocateInfo allocInfo = {};
1750 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1751 allocInfo.commandPool = commandPools[i];
1752 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1753 allocInfo.commandBufferCount = 1;
1754
1755 if (vkAllocateCommandBuffers(device, &allocInfo, &commandBuffers[i]) != VK_SUCCESS) {
1756 throw runtime_error("failed to allocate command buffer!");
1757 }
1758 }
1759}
1760
1761void VulkanGame::renderFrame(ImDrawData* draw_data) {
1762 VkResult result = vkAcquireNextImageKHR(device, swapChain, numeric_limits<uint64_t>::max(),
1763 imageAcquiredSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
1764
1765 if (result == VK_SUBOPTIMAL_KHR) {
1766 shouldRecreateSwapChain = true;
1767 } else if (result == VK_ERROR_OUT_OF_DATE_KHR) {
1768 shouldRecreateSwapChain = true;
1769 return;
1770 } else {
1771 VKUTIL_CHECK_RESULT(result, "failed to acquire swap chain image!");
1772 }
1773
1774 VKUTIL_CHECK_RESULT(
1775 vkWaitForFences(device, 1, &inFlightFences[imageIndex], VK_TRUE, numeric_limits<uint64_t>::max()),
1776 "failed waiting for fence!");
1777
1778 VKUTIL_CHECK_RESULT(vkResetFences(device, 1, &inFlightFences[imageIndex]),
1779 "failed to reset fence!");
1780
1781 VKUTIL_CHECK_RESULT(vkResetCommandPool(device, commandPools[imageIndex], 0),
1782 "failed to reset command pool!");
1783
1784 VkCommandBufferBeginInfo beginInfo = {};
1785 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1786 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1787
1788 VKUTIL_CHECK_RESULT(vkBeginCommandBuffer(commandBuffers[imageIndex], &beginInfo),
1789 "failed to begin recording command buffer!");
1790
1791 VkRenderPassBeginInfo renderPassInfo = {};
1792 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1793 renderPassInfo.renderPass = renderPass;
1794 renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
1795 renderPassInfo.renderArea.offset = { 0, 0 };
1796 renderPassInfo.renderArea.extent = swapChainExtent;
1797
1798 array<VkClearValue, 2> clearValues = {};
1799 clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
1800 clearValues[1].depthStencil = { 1.0f, 0 };
1801
1802 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
1803 renderPassInfo.pClearValues = clearValues.data();
1804
1805 vkCmdBeginRenderPass(commandBuffers[imageIndex], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1806
1807 currentScreen->createRenderCommands(commandBuffers[imageIndex], imageIndex);
1808
1809 ImGui_ImplVulkan_RenderDrawData(draw_data, commandBuffers[imageIndex]);
1810
1811 vkCmdEndRenderPass(commandBuffers[imageIndex]);
1812
1813 VKUTIL_CHECK_RESULT(vkEndCommandBuffer(commandBuffers[imageIndex]),
1814 "failed to record command buffer!");
1815
1816 VkSemaphore waitSemaphores[] = { imageAcquiredSemaphores[currentFrame] };
1817 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
1818 VkSemaphore signalSemaphores[] = { renderCompleteSemaphores[currentFrame] };
1819
1820 VkSubmitInfo submitInfo = {};
1821 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1822 submitInfo.waitSemaphoreCount = 1;
1823 submitInfo.pWaitSemaphores = waitSemaphores;
1824 submitInfo.pWaitDstStageMask = waitStages;
1825 submitInfo.commandBufferCount = 1;
1826 submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
1827 submitInfo.signalSemaphoreCount = 1;
1828 submitInfo.pSignalSemaphores = signalSemaphores;
1829
1830 VKUTIL_CHECK_RESULT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[imageIndex]),
1831 "failed to submit draw command buffer!");
1832}
1833
1834void VulkanGame::presentFrame() {
1835 VkSemaphore signalSemaphores[] = { renderCompleteSemaphores[currentFrame] };
1836
1837 VkPresentInfoKHR presentInfo = {};
1838 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1839 presentInfo.waitSemaphoreCount = 1;
1840 presentInfo.pWaitSemaphores = signalSemaphores;
1841 presentInfo.swapchainCount = 1;
1842 presentInfo.pSwapchains = &swapChain;
1843 presentInfo.pImageIndices = &imageIndex;
1844 presentInfo.pResults = nullptr;
1845
1846 VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo);
1847
1848 if (result == VK_SUBOPTIMAL_KHR) {
1849 shouldRecreateSwapChain = true;
1850 } else if (result == VK_ERROR_OUT_OF_DATE_KHR) {
1851 shouldRecreateSwapChain = true;
1852 return;
1853 } else {
1854 VKUTIL_CHECK_RESULT(result, "failed to present swap chain image!");
1855 }
1856
1857 currentFrame = (currentFrame + 1) % swapChainImageCount;
1858}
1859
1860void VulkanGame::createSyncObjects() {
1861 imageAcquiredSemaphores.resize(swapChainImageCount);
1862 renderCompleteSemaphores.resize(swapChainImageCount);
1863 inFlightFences.resize(swapChainImageCount);
1864
1865 VkSemaphoreCreateInfo semaphoreInfo = {};
1866 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1867
1868 VkFenceCreateInfo fenceInfo = {};
1869 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1870 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1871
1872 for (size_t i = 0; i < swapChainImageCount; i++) {
1873 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAcquiredSemaphores[i]) != VK_SUCCESS) {
1874 throw runtime_error("failed to create image acquired sempahore for a frame!");
1875 }
1876
1877 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderCompleteSemaphores[i]) != VK_SUCCESS) {
1878 throw runtime_error("failed to create render complete sempahore for a frame!");
1879 }
1880
1881 if (vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
1882 throw runtime_error("failed to create fence for a frame!");
1883 }
1884 }
1885}
1886
1887void VulkanGame::createImguiDescriptorPool() {
1888 vector<VkDescriptorPoolSize> pool_sizes{
1889 { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
1890 { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
1891 { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
1892 { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
1893 { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
1894 { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
1895 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
1896 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
1897 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
1898 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
1899 { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
1900 };
1901
1902 VkDescriptorPoolCreateInfo pool_info = {};
1903 pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
1904 pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
1905 pool_info.maxSets = 1000 * pool_sizes.size();
1906 pool_info.poolSizeCount = static_cast<uint32_t>(pool_sizes.size());
1907 pool_info.pPoolSizes = pool_sizes.data();
1908 if (vkCreateDescriptorPool(device, &pool_info, nullptr, &imguiDescriptorPool) != VK_SUCCESS) {
1909 throw runtime_error("failed to create IMGUI descriptor pool!");
1910 }
1911}
1912
1913void VulkanGame::destroyImguiDescriptorPool() {
1914 vkDestroyDescriptorPool(device, imguiDescriptorPool, nullptr);
1915}
1916
1917void VulkanGame::addLaser(vec3 start, vec3 end, vec3 color, float width) {
1918 vec3 ray = end - start;
1919 float length = glm::length(ray);
1920
1921 SceneObject<LaserVertex, SSBO_Laser>& laser = addObject(
1922 laserObjects, laserPipeline,
1923 addObjectIndex<LaserVertex>(laserObjects.size(), {
1924 {{ width / 2, 0.0f, -width / 2 }, {1.0f, 0.5f }},
1925 {{-width / 2, 0.0f, -width / 2 }, {0.0f, 0.5f }},
1926 {{-width / 2, 0.0f, 0.0f }, {0.0f, 0.0f }},
1927 {{ width / 2, 0.0f, 0.0f }, {1.0f, 0.0f }},
1928 {{ width / 2, 0.0f, -length + width / 2}, {1.0f, 0.51f}},
1929 {{-width / 2, 0.0f, -length + width / 2}, {0.0f, 0.51f}},
1930 {{ width / 2, 0.0f, -length, }, {1.0f, 1.0f }},
1931 {{-width / 2, 0.0f, -length }, {0.0f, 1.0f }}
1932 }), {
1933 0, 1, 2, 0, 2, 3,
1934 4, 5, 1, 4, 1, 0,
1935 6, 7, 5, 6, 5, 4
1936 }, {
1937 mat4(1.0f),
1938 color,
1939 false
1940 }, true);
1941
1942 float xAxisRotation = asin(ray.y / length);
1943 float yAxisRotation = atan2(-ray.x, -ray.z);
1944
1945 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
1946 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
1947 vec4(0.0f, 1.0f, 0.0f, 1.0f));
1948
1949 // To project point P onto line AB:
1950 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
1951 vec3 projOnLaser = start + glm::dot(this->cam_pos - start, ray) / (length * length) * ray;
1952 vec3 laserToCam = this->cam_pos - projOnLaser;
1953
1954 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
1955
1956 laser.targetAsteroid = nullptr;
1957
1958 laser.model_base =
1959 rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
1960
1961 laser.model_transform =
1962 translate(mat4(1.0f), start) *
1963 rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
1964 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f));
1965
1966 laser.modified = true;
1967}
1968
1969void VulkanGame::translateLaser(size_t index, const vec3& translation) {
1970 SceneObject<LaserVertex, SSBO_Laser>& laser = this->laserObjects[index];
1971
1972 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
1973 // and then re-used here
1974
1975 vec3 start = vec3(laser.model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
1976 vec3 end = vec3(laser.model_transform * vec4(0.0f, 0.0f, laser.vertices[6].pos.z, 1.0f));
1977
1978 vec3 ray = end - start;
1979 float length = glm::length(ray);
1980
1981 float xAxisRotation = asin(ray.y / length);
1982 float yAxisRotation = atan2(-ray.x, -ray.z);
1983
1984 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
1985 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
1986 vec4(0.0f, 1.0f, 0.0f, 1.0f));
1987
1988 // To project point P onto line AB:
1989 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
1990 vec3 projOnLaser = start + glm::dot(cam_pos - start, ray) / (length*length) * ray;
1991 vec3 laserToCam = cam_pos - projOnLaser;
1992
1993 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
1994
1995 laser.model_base = rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
1996 laser.model_transform = translate(mat4(1.0f), translation) * laser.model_transform;
1997
1998 laser.modified = true;
1999}
2000
2001void VulkanGame::updateLaserTarget(size_t index) {
2002 SceneObject<LaserVertex, SSBO_Laser>& laser = this->laserObjects[index];
2003
2004 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
2005 // and then re-used here
2006
2007 vec3 start = vec3(laser.model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
2008 vec3 end = vec3(laser.model_transform * vec4(0.0f, 0.0f, laser.vertices[6].pos.z, 1.0f));
2009
2010 vec3 intersection(0.0f), closestIntersection(0.0f);
2011 SceneObject<AsteroidVertex, SSBO_Asteroid>* closestAsteroid = nullptr;
2012 unsigned int closestAsteroidIndex = -1;
2013
2014 for (int i = 0; i < this->asteroidObjects.size(); i++) {
2015 if (!this->asteroidObjects[i].ssbo.deleted &&
2016 this->getLaserAndAsteroidIntersection(this->asteroidObjects[i], start, end, intersection)) {
2017 // TODO: Implement a more generic algorithm for testing the closest object by getting the distance between the points
2018 // TODO: Also check which intersection is close to the start of the laser. This would make the algorithm work
2019 // regardless of which way -Z is pointing
2020 if (closestAsteroid == nullptr || intersection.z > closestIntersection.z) {
2021 // TODO: At this point, find the real intersection of the laser with one of the asteroid's sides
2022 closestAsteroid = &asteroidObjects[i];
2023 closestIntersection = intersection;
2024 closestAsteroidIndex = i;
2025 }
2026 }
2027 }
2028
2029 float width = laser.vertices[0].pos.x - laser.vertices[1].pos.x;
2030
2031 if (laser.targetAsteroid != closestAsteroid) {
2032 if (laser.targetAsteroid != nullptr) {
2033 if (index == leftLaserIdx && leftLaserEffect != nullptr) {
2034 leftLaserEffect->deleted = true;
2035 } else if (index == rightLaserIdx && rightLaserEffect != nullptr) {
2036 rightLaserEffect->deleted = true;
2037 }
2038 }
2039
2040 EffectOverTime<AsteroidVertex, SSBO_Asteroid>* eot = nullptr;
2041
2042 if (closestAsteroid != nullptr) {
2043 // TODO: Use some sort of smart pointer instead
2044 eot = new EffectOverTime<AsteroidVertex, SSBO_Asteroid>(asteroidPipeline, asteroidObjects, closestAsteroidIndex,
2045 offset_of(&SSBO_Asteroid::hp), curTime, -20.0f);
2046 effects.push_back(eot);
2047 }
2048
2049 if (index == leftLaserIdx) {
2050 leftLaserEffect = eot;
2051 } else if (index == rightLaserIdx) {
2052 rightLaserEffect = eot;
2053 }
2054
2055 laser.targetAsteroid = closestAsteroid;
2056 }
2057
2058 // Make the laser go past the end of the screen if it doesn't hit anything
2059 float length = closestAsteroid == nullptr ? 5.24f : glm::length(closestIntersection - start);
2060
2061 laser.vertices[4].pos.z = -length + width / 2;
2062 laser.vertices[5].pos.z = -length + width / 2;
2063 laser.vertices[6].pos.z = -length;
2064 laser.vertices[7].pos.z = -length;
2065
2066 // TODO: Consider if I want to set a flag and do this update in updateScene() instead
2067 updateObjectVertices(this->laserPipeline, laser, index);
2068}
2069
2070// TODO: Determine if I should pass start and end by reference or value since they don't get changed
2071// Probably use const reference
2072bool VulkanGame::getLaserAndAsteroidIntersection(SceneObject<AsteroidVertex, SSBO_Asteroid>& asteroid,
2073 vec3& start, vec3& end, vec3& intersection) {
2074 /*
2075 ### LINE EQUATIONS ###
2076 x = x1 + u * (x2 - x1)
2077 y = y1 + u * (y2 - y1)
2078 z = z1 + u * (z2 - z1)
2079
2080 ### SPHERE EQUATION ###
2081 (x - x3)^2 + (y - y3)^2 + (z - z3)^2 = r^2
2082
2083 ### QUADRATIC EQUATION TO SOLVE ###
2084 a*u^2 + b*u + c = 0
2085 WHERE THE CONSTANTS ARE
2086 a = (x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2
2087 b = 2*( (x2 - x1)*(x1 - x3) + (y2 - y1)*(y1 - y3) + (z2 - z1)*(z1 - z3) )
2088 c = x3^2 + y3^2 + z3^2 + x1^2 + y1^2 + z1^2 - 2(x3*x1 + y3*y1 + z3*z1) - r^2
2089
2090 u = (-b +- sqrt(b^2 - 4*a*c)) / 2a
2091
2092 If the value under the root is >= 0, we got an intersection
2093 If the value > 0, there are two solutions. Take the one closer to 0, since that's the
2094 one closer to the laser start point
2095 */
2096
2097 vec3& center = asteroid.center;
2098
2099 float a = pow(end.x - start.x, 2) + pow(end.y - start.y, 2) + pow(end.z - start.z, 2);
2100 float b = 2 * ((start.x - end.x) * (start.x - center.x) + (end.y - start.y) * (start.y - center.y) +
2101 (end.z - start.z) * (start.z - center.z));
2102 float c = pow(center.x, 2) + pow(center.y, 2) + pow(center.z, 2) + pow(start.x, 2) + pow(start.y, 2) +
2103 pow(start.z, 2) - 2 * (center.x * start.x + center.y * start.y + center.z * start.z) -
2104 pow(asteroid.radius, 2);
2105 float discriminant = pow(b, 2) - 4 * a * c;
2106
2107 if (discriminant >= 0.0f) {
2108 // In this case, the negative root will always give the point closer to the laser start point
2109 float u = (-b - sqrt(discriminant)) / (2 * a);
2110
2111 // Check that the intersection is within the line segment corresponding to the laser
2112 if (0.0f <= u && u <= 1.0f) {
2113 intersection = start + u * (end - start);
2114 return true;
2115 }
2116 }
2117
2118 return false;
2119}
2120
2121void VulkanGame::createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags flags,
2122 vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory, vector<VkDescriptorBufferInfo>& bufferInfoList) {
2123 buffers.resize(swapChainImageCount);
2124 buffersMemory.resize(swapChainImageCount);
2125 bufferInfoList.resize(swapChainImageCount);
2126
2127 for (size_t i = 0; i < swapChainImageCount; i++) {
2128 VulkanUtils::createBuffer(device, physicalDevice, bufferSize, flags,
2129 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
2130 buffers[i], buffersMemory[i]);
2131
2132 bufferInfoList[i].buffer = buffers[i];
2133 bufferInfoList[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
2134 bufferInfoList[i].range = bufferSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
2135 }
2136}
2137
2138void VulkanGame::addExplosion(mat4 model_mat, float duration, float cur_time) {
2139 vector<ExplosionVertex> vertices;
2140 vertices.reserve(EXPLOSION_PARTICLE_COUNT);
2141
2142 float particle_start_time = 0.0f;
2143
2144 for (int i = 0; i < EXPLOSION_PARTICLE_COUNT; i++) {
2145 float randx = ((float)rand() / (float)RAND_MAX) - 0.5f;
2146 float randy = ((float)rand() / (float)RAND_MAX) - 0.5f;
2147
2148 vertices.push_back({ vec3(randx, randy, 0.0f), particle_start_time});
2149
2150 particle_start_time += .01f;
2151 // TODO: Get this working
2152 // particle_start_time += 1.0f * EXPLOSION_PARTICLE_COUNT / duration
2153 }
2154
2155 // Fill the indices with the the first EXPLOSION_PARTICLE_COUNT ints
2156 vector<uint16_t> indices(EXPLOSION_PARTICLE_COUNT);
2157 iota(indices.begin(), indices.end(), 0);
2158
2159 SceneObject<ExplosionVertex, SSBO_Explosion>& explosion = addObject(
2160 explosionObjects, explosionPipeline,
2161 addObjectIndex(explosionObjects.size(), vertices),
2162 indices, {
2163 mat4(1.0f),
2164 cur_time,
2165 duration,
2166 false
2167 }, true);
2168
2169 explosion.model_base = model_mat;
2170 explosion.model_transform = mat4(1.0f);
2171
2172 explosion.modified = true;
2173}
2174
2175void VulkanGame::recreateSwapChain() {
2176 if (vkDeviceWaitIdle(device) != VK_SUCCESS) {
2177 throw runtime_error("failed to wait for device!");
2178 }
2179
2180 cleanupSwapChain();
2181
2182 createSwapChain();
2183 createImageViews();
2184 createRenderPass();
2185
2186 createCommandPools();
2187
2188 // The depth buffer does need to be recreated with the swap chain since its dimensions depend on the window size
2189 // and resizing the window is a common reason to recreate the swapchain
2190 VulkanUtils::createDepthImage(device, physicalDevice, resourceCommandPool, findDepthFormat(), swapChainExtent,
2191 depthImage, graphicsQueue);
2192 createFramebuffers();
2193
2194 // TODO: Move UBO creation/management into GraphicsPipeline_Vulkan, like I did with SSBOs
2195
2196 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
2197 uniformBuffers_modelPipeline, uniformBuffersMemory_modelPipeline, uniformBufferInfoList_modelPipeline);
2198
2199 modelPipeline.updateRenderPass(renderPass);
2200 modelPipeline.createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
2201 modelPipeline.createDescriptorPool(swapChainImages);
2202 modelPipeline.createDescriptorSets(swapChainImages);
2203
2204 overlayPipeline.updateRenderPass(renderPass);
2205 overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
2206 overlayPipeline.createDescriptorPool(swapChainImages);
2207 overlayPipeline.createDescriptorSets(swapChainImages);
2208
2209 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
2210 uniformBuffers_shipPipeline, uniformBuffersMemory_shipPipeline, uniformBufferInfoList_shipPipeline);
2211
2212 shipPipeline.updateRenderPass(renderPass);
2213 shipPipeline.createPipeline("shaders/ship-vert.spv", "shaders/ship-frag.spv");
2214 shipPipeline.createDescriptorPool(swapChainImages);
2215 shipPipeline.createDescriptorSets(swapChainImages);
2216
2217 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
2218 uniformBuffers_asteroidPipeline, uniformBuffersMemory_asteroidPipeline, uniformBufferInfoList_asteroidPipeline);
2219
2220 asteroidPipeline.updateRenderPass(renderPass);
2221 asteroidPipeline.createPipeline("shaders/asteroid-vert.spv", "shaders/asteroid-frag.spv");
2222 asteroidPipeline.createDescriptorPool(swapChainImages);
2223 asteroidPipeline.createDescriptorSets(swapChainImages);
2224
2225 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
2226 uniformBuffers_laserPipeline, uniformBuffersMemory_laserPipeline, uniformBufferInfoList_laserPipeline);
2227
2228 laserPipeline.updateRenderPass(renderPass);
2229 laserPipeline.createPipeline("shaders/laser-vert.spv", "shaders/laser-frag.spv");
2230 laserPipeline.createDescriptorPool(swapChainImages);
2231 laserPipeline.createDescriptorSets(swapChainImages);
2232
2233 createBufferSet(sizeof(UBO_Explosion), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
2234 uniformBuffers_explosionPipeline, uniformBuffersMemory_explosionPipeline, uniformBufferInfoList_explosionPipeline);
2235
2236 explosionPipeline.updateRenderPass(renderPass);
2237 explosionPipeline.createPipeline("shaders/explosion-vert.spv", "shaders/explosion-frag.spv");
2238 explosionPipeline.createDescriptorPool(swapChainImages);
2239 explosionPipeline.createDescriptorSets(swapChainImages);
2240
2241 createCommandBuffers();
2242
2243 createSyncObjects();
2244
2245 imageIndex = 0;
2246}
2247
2248void VulkanGame::cleanupSwapChain() {
2249 VulkanUtils::destroyVulkanImage(device, depthImage);
2250
2251 for (VkFramebuffer framebuffer : swapChainFramebuffers) {
2252 vkDestroyFramebuffer(device, framebuffer, nullptr);
2253 }
2254
2255 for (uint32_t i = 0; i < swapChainImageCount; i++) {
2256 vkFreeCommandBuffers(device, commandPools[i], 1, &commandBuffers[i]);
2257 vkDestroyCommandPool(device, commandPools[i], nullptr);
2258 }
2259
2260 overlayPipeline.cleanup();
2261 modelPipeline.cleanup();
2262 shipPipeline.cleanup();
2263 asteroidPipeline.cleanup();
2264 laserPipeline.cleanup();
2265 explosionPipeline.cleanup();
2266
2267 for (size_t i = 0; i < uniformBuffers_modelPipeline.size(); i++) {
2268 vkDestroyBuffer(device, uniformBuffers_modelPipeline[i], nullptr);
2269 vkFreeMemory(device, uniformBuffersMemory_modelPipeline[i], nullptr);
2270 }
2271
2272 for (size_t i = 0; i < uniformBuffers_shipPipeline.size(); i++) {
2273 vkDestroyBuffer(device, uniformBuffers_shipPipeline[i], nullptr);
2274 vkFreeMemory(device, uniformBuffersMemory_shipPipeline[i], nullptr);
2275 }
2276
2277 for (size_t i = 0; i < uniformBuffers_asteroidPipeline.size(); i++) {
2278 vkDestroyBuffer(device, uniformBuffers_asteroidPipeline[i], nullptr);
2279 vkFreeMemory(device, uniformBuffersMemory_asteroidPipeline[i], nullptr);
2280 }
2281
2282 for (size_t i = 0; i < uniformBuffers_laserPipeline.size(); i++) {
2283 vkDestroyBuffer(device, uniformBuffers_laserPipeline[i], nullptr);
2284 vkFreeMemory(device, uniformBuffersMemory_laserPipeline[i], nullptr);
2285 }
2286
2287 for (size_t i = 0; i < uniformBuffers_explosionPipeline.size(); i++) {
2288 vkDestroyBuffer(device, uniformBuffers_explosionPipeline[i], nullptr);
2289 vkFreeMemory(device, uniformBuffersMemory_explosionPipeline[i], nullptr);
2290 }
2291
2292 for (size_t i = 0; i < swapChainImageCount; i++) {
2293 vkDestroySemaphore(device, imageAcquiredSemaphores[i], nullptr);
2294 vkDestroySemaphore(device, renderCompleteSemaphores[i], nullptr);
2295 vkDestroyFence(device, inFlightFences[i], nullptr);
2296 }
2297
2298 vkDestroyRenderPass(device, renderPass, nullptr);
2299
2300 for (VkImageView imageView : swapChainImageViews) {
2301 vkDestroyImageView(device, imageView, nullptr);
2302 }
2303
2304 vkDestroySwapchainKHR(device, swapChain, nullptr);
2305}
Note: See TracBrowser for help on using the repository browser.