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

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

In VulkanGame, generate the IMGUI draw data outside of renderFrame() and
pass it in, instead ofgenerating it inside the function.

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