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

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

Remove all dependencies on VulkanH functions and structures from SDLGame

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