source: opengl-game/vulkan-game.cpp@ 20e4c2b

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

In VulkanGame, use ImGui for the UI instead of using SDL to draw elements onto an overlay, and remove everything associated with the overlay pipeline

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