source: opengl-game/vulkan-game.cpp@ 187b0f5

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

Change VulkanGame and SDLGame to only use discrete GPUs and switch the timer class to steady_clock

  • Property mode set to 100644
File size: 90.0 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;
61 currentFrame = 0;
[187b0f5]62 imageIndex = 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() {
[187b0f5]708 startTime = steady_clock::now();
709 curTime = duration<float, seconds::period>(steady_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;
[187b0f5]722 curTime = duration<float, seconds::period>(steady_clock::now() - this->startTime).count();
[6104594]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 shouldRecreateSwapChain = false;
904 }
905 }
906
[ea2b4dc]907 updateScene();
908
909 ImGui_ImplVulkan_NewFrame();
[8d96e95]910 ImGui_ImplSDL2_NewFrame(window);
[ea2b4dc]911 ImGui::NewFrame();
912
[20e4c2b]913 (this->*currentRenderScreenFn)();
[ea2b4dc]914
915 ImGui::Render();
916
[81869ef]917 gui->refreshWindowSize();
[8d96e95]918 const bool isMinimized = gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0;
[81869ef]919
920 if (!isMinimized) {
921 renderFrame(ImGui::GetDrawData());
922 presentFrame();
923 }
[0df3c9a]924 }
925}
926
[2da64ef]927// TODO: The only updates that need to happen once per Vulkan image are the SSBO ones,
928// which are already handled by updateObject(). Move this code to a different place,
929// where it will run just once per frame
[3f32dfd]930void VulkanGame::updateScene() {
[5ba732a]931 for (SceneObject<ModelVertex, SSBO_ModelObject>& model : this->modelObjects) {
932 model.model_transform =
[2da64ef]933 translate(mat4(1.0f), vec3(0.0f, -2.0f, -0.0f)) *
[6104594]934 rotate(mat4(1.0f), curTime * radians(90.0f), vec3(0.0f, 0.0f, 1.0f));
[5ba732a]935 model.modified = true;
[2da64ef]936 }
[3e8cc8b]937
[3950236]938 if (leftLaserIdx != -1) {
939 updateLaserTarget(leftLaserIdx);
940 }
941 if (rightLaserIdx != -1) {
942 updateLaserTarget(rightLaserIdx);
943 }
944
[7297892]945 for (vector<BaseEffectOverTime*>::iterator it = effects.begin(); it != effects.end(); ) {
946 if ((*it)->deleted) {
947 delete *it;
948 it = effects.erase(it);
949 } else {
950 BaseEffectOverTime* eot = *it;
951
[5192672]952 eot->applyEffect(curTime);
[7297892]953
954 it++;
955 }
956 }
957
[5ba732a]958 for (SceneObject<AsteroidVertex, SSBO_Asteroid>& asteroid : this->asteroidObjects) {
959 if (!asteroid.ssbo.deleted) {
960 vec3 objCenter = vec3(viewMat * vec4(asteroid.center, 1.0f));
961
[4a9416a]962 if (asteroid.ssbo.hp <= 0.0f) {
[5ba732a]963 asteroid.ssbo.deleted = true;
[2ba5617]964
[4a9416a]965 // TODO: Optimize this so I don't recalculate the camera rotation every time
966 // TODO: Also, avoid re-declaring cam_pitch
967 float cam_pitch = -50.0f;
968 mat4 pitch_mat = rotate(mat4(1.0f), radians(cam_pitch), vec3(1.0f, 0.0f, 0.0f));
969 mat4 model_mat = translate(mat4(1.0f), asteroid.center) * pitch_mat;
970
971 addExplosion(model_mat, 0.5f, curTime);
972
[f809ae6]973 this->score++;
[4a9416a]974 } else if ((objCenter.z - asteroid.radius) > -NEAR_CLIP) {
975 asteroid.ssbo.deleted = true;
[2ba5617]976 } else {
[5ba732a]977 asteroid.model_transform =
[2ba5617]978 translate(mat4(1.0f), vec3(0.0f, 0.0f, this->asteroidSpeed * this->elapsedTime)) *
[5ba732a]979 asteroid.model_transform;
[2ba5617]980 }
[0807aeb]981
[5ba732a]982 asteroid.modified = true;
[0807aeb]983 }
984 }
985
[6104594]986 if (curTime - this->lastSpawn_asteroid > this->spawnRate_asteroid) {
987 this->lastSpawn_asteroid = curTime;
[0807aeb]988
[4994692]989 SceneObject<AsteroidVertex, SSBO_Asteroid>& asteroid = addObject(
990 asteroidObjects, asteroidPipeline,
[0807aeb]991 addObjectIndex<AsteroidVertex>(asteroidObjects.size(),
[6104594]992 addVertexNormals<AsteroidVertex>({
993
994 // front
995 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
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
1002 // top
1003 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
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
1010 // bottom
1011 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1012 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1013 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1014 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1015 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1016 {{ 1.0f, -1.0f, -1.0}, {0.4f, 0.4f, 0.4f}},
1017
1018 // back
1019 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1020 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1021 {{-1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1022 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1023 {{ 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
1026 // right
1027 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1028 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1029 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1030 {{ 1.0f, 1.0f, -1.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
1034 // left
1035 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1036 {{-1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1037 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1038 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1039 {{-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 })), {
1042 0, 1, 2, 3, 4, 5,
1043 6, 7, 8, 9, 10, 11,
1044 12, 13, 14, 15, 16, 17,
1045 18, 19, 20, 21, 22, 23,
1046 24, 25, 26, 27, 28, 29,
1047 30, 31, 32, 33, 34, 35,
[0807aeb]1048 }, {
1049 mat4(1.0f),
1050 10.0f,
[4994692]1051 false
[0807aeb]1052 }, true);
1053
[2ba5617]1054 // This accounts for the scaling in model_base.
1055 // Dividing by 8 instead of 10 since the bounding radius algorithm
1056 // under-calculates the true value.
1057 // TODO: Figure out the best way to take scaling into account when calculating the radius
1058 // Keep in mind that the main complicating factor is the currently poor radius calculation
[4994692]1059 asteroid.radius /= 8.0f;
[2ba5617]1060
[4994692]1061 asteroid.model_base =
[2ba5617]1062 translate(mat4(1.0f), vec3(getRandomNum(-1.3f, 1.3f), -1.2f, getRandomNum(-5.5f, -4.5f))) *
[0807aeb]1063 rotate(mat4(1.0f), radians(60.0f), vec3(1.0f, 1.0f, -1.0f)) *
1064 scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
[5ba732a]1065 asteroid.modified = true;
1066 }
1067
[4a9416a]1068 for (SceneObject<ExplosionVertex, SSBO_Explosion>& explosion : this->explosionObjects) {
1069 if (!explosion.ssbo.deleted) {
1070 if (curTime > (explosion.ssbo.explosionStartTime + explosion.ssbo.explosionDuration)) {
1071 explosion.ssbo.deleted = true;
1072 explosion.modified = true;
1073 }
1074 }
1075 }
1076
[5ba732a]1077 for (size_t i = 0; i < shipObjects.size(); i++) {
1078 if (shipObjects[i].modified) {
1079 updateObject(shipObjects, shipPipeline, i);
1080 }
1081 }
[0807aeb]1082
[5ba732a]1083 for (size_t i = 0; i < modelObjects.size(); i++) {
1084 if (modelObjects[i].modified) {
1085 updateObject(modelObjects, modelPipeline, i);
1086 }
1087 }
1088
1089 for (size_t i = 0; i < asteroidObjects.size(); i++) {
1090 if (asteroidObjects[i].modified) {
1091 updateObject(asteroidObjects, asteroidPipeline, i);
1092 }
[0807aeb]1093 }
1094
[237cbec]1095 for (size_t i = 0; i < laserObjects.size(); i++) {
1096 if (laserObjects[i].modified) {
1097 updateObject(laserObjects, laserPipeline, i);
1098 }
1099 }
1100
[4a9416a]1101 for (size_t i = 0; i < explosionObjects.size(); i++) {
1102 if (explosionObjects[i].modified) {
1103 updateObject(explosionObjects, explosionPipeline, i);
1104 }
1105 }
1106
1107 explosion_UBO.cur_time = curTime;
1108
[3f32dfd]1109 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_modelPipeline[imageIndex], 0, object_VP_mats);
[055750a]1110
[3f32dfd]1111 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_shipPipeline[imageIndex], 0, ship_VP_mats);
[055750a]1112
[3f32dfd]1113 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_asteroidPipeline[imageIndex], 0, asteroid_VP_mats);
[237cbec]1114
[3f32dfd]1115 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_laserPipeline[imageIndex], 0, laser_VP_mats);
[4a9416a]1116
[3f32dfd]1117 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_explosionPipeline[imageIndex], 0, explosion_UBO);
[8e02b6b]1118}
1119
[99d44b2]1120void VulkanGame::cleanup() {
[1cb64e6]1121 VKUTIL_CHECK_RESULT(vkDeviceWaitIdle(device), "failed to wait for device!");
[3f32dfd]1122
[3b7d497]1123 ImGui_ImplVulkan_Shutdown();
1124 ImGui_ImplSDL2_Shutdown();
1125 ImGui::DestroyContext();
1126
[3f32dfd]1127 // TODO: Probably move this into cleanupSwapChain once I finish the integration
[3b7d497]1128 destroyImguiDescriptorPool();
1129
[c1c2021]1130 cleanupSwapChain();
1131
[4994692]1132 VulkanUtils::destroyVulkanImage(device, floorTextureImage);
[237cbec]1133 VulkanUtils::destroyVulkanImage(device, laserTextureImage);
[e83b155]1134
1135 vkDestroySampler(device, textureSampler, nullptr);
1136
[b8777b7]1137 modelPipeline.cleanupBuffers();
[3782d66]1138 shipPipeline.cleanupBuffers();
[3e8cc8b]1139 asteroidPipeline.cleanupBuffers();
[237cbec]1140 laserPipeline.cleanupBuffers();
[4a9416a]1141 explosionPipeline.cleanupBuffers();
[b794178]1142
[3f32dfd]1143 vkDestroyCommandPool(device, resourceCommandPool, nullptr);
1144
[c1c2021]1145 vkDestroyDevice(device, nullptr);
1146 vkDestroySurfaceKHR(instance, surface, nullptr);
1147
[c1d9b2a]1148 if (ENABLE_VALIDATION_LAYERS) {
1149 VulkanUtils::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
1150 }
[c1c2021]1151
[c1d9b2a]1152 vkDestroyInstance(instance, nullptr);
1153
[b6e60b4]1154 gui->destroyWindow();
1155 gui->shutdown();
[0df3c9a]1156 delete gui;
[c1d9b2a]1157}
1158
[c324d6a]1159void VulkanGame::createVulkanInstance(const vector<const char*>& validationLayers) {
[c1d9b2a]1160 if (ENABLE_VALIDATION_LAYERS && !VulkanUtils::checkValidationLayerSupport(validationLayers)) {
1161 throw runtime_error("validation layers requested, but not available!");
1162 }
1163
1164 VkApplicationInfo appInfo = {};
1165 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
1166 appInfo.pApplicationName = "Vulkan Game";
1167 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
1168 appInfo.pEngineName = "No Engine";
1169 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
1170 appInfo.apiVersion = VK_API_VERSION_1_0;
1171
1172 VkInstanceCreateInfo createInfo = {};
1173 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
1174 createInfo.pApplicationInfo = &appInfo;
1175
1176 vector<const char*> extensions = gui->getRequiredExtensions();
1177 if (ENABLE_VALIDATION_LAYERS) {
1178 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
1179 }
1180
1181 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
1182 createInfo.ppEnabledExtensionNames = extensions.data();
1183
1184 cout << endl << "Extensions:" << endl;
1185 for (const char* extensionName : extensions) {
1186 cout << extensionName << endl;
1187 }
1188 cout << endl;
1189
1190 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
1191 if (ENABLE_VALIDATION_LAYERS) {
1192 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
1193 createInfo.ppEnabledLayerNames = validationLayers.data();
1194
1195 populateDebugMessengerCreateInfo(debugCreateInfo);
1196 createInfo.pNext = &debugCreateInfo;
1197 } else {
1198 createInfo.enabledLayerCount = 0;
1199
1200 createInfo.pNext = nullptr;
1201 }
1202
1203 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
1204 throw runtime_error("failed to create instance!");
1205 }
1206}
1207
1208void VulkanGame::setupDebugMessenger() {
[c324d6a]1209 if (!ENABLE_VALIDATION_LAYERS) {
1210 return;
1211 }
[c1d9b2a]1212
1213 VkDebugUtilsMessengerCreateInfoEXT createInfo;
1214 populateDebugMessengerCreateInfo(createInfo);
1215
1216 if (VulkanUtils::createDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
1217 throw runtime_error("failed to set up debug messenger!");
1218 }
1219}
1220
1221void VulkanGame::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
1222 createInfo = {};
1223 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
1224 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;
1225 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;
1226 createInfo.pfnUserCallback = debugCallback;
1227}
1228
1229VKAPI_ATTR VkBool32 VKAPI_CALL VulkanGame::debugCallback(
1230 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
1231 VkDebugUtilsMessageTypeFlagsEXT messageType,
1232 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
1233 void* pUserData) {
1234 cerr << "validation layer: " << pCallbackData->pMessage << endl;
1235
[1cb64e6]1236 // TODO: Figure out what the return value means and if it should always be VK_FALSE
[c1d9b2a]1237 return VK_FALSE;
1238}
[90a424f]1239
1240void VulkanGame::createVulkanSurface() {
1241 if (gui->createVulkanSurface(instance, &surface) == RTWO_ERROR) {
1242 throw runtime_error("failed to create window surface!");
1243 }
1244}
1245
[fe5c3ba]1246void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
[90a424f]1247 uint32_t deviceCount = 0;
[c324d6a]1248 // TODO: Check VkResult
[90a424f]1249 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
1250
1251 if (deviceCount == 0) {
1252 throw runtime_error("failed to find GPUs with Vulkan support!");
1253 }
1254
1255 vector<VkPhysicalDevice> devices(deviceCount);
[c324d6a]1256 // TODO: Check VkResult
[90a424f]1257 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
1258
1259 cout << endl << "Graphics cards:" << endl;
1260 for (const VkPhysicalDevice& device : devices) {
[fe5c3ba]1261 if (isDeviceSuitable(device, deviceExtensions)) {
[90a424f]1262 physicalDevice = device;
1263 break;
1264 }
1265 }
1266 cout << endl;
1267
1268 if (physicalDevice == VK_NULL_HANDLE) {
1269 throw runtime_error("failed to find a suitable GPU!");
1270 }
1271}
1272
[6a39266]1273bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice, const vector<const char*>& deviceExtensions) {
[90a424f]1274 VkPhysicalDeviceProperties deviceProperties;
[fa9fa1c]1275 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
[90a424f]1276
1277 cout << "Device: " << deviceProperties.deviceName << endl;
1278
[187b0f5]1279 // TODO: Eventually, maybe let the user pick out of a set of GPUs in case the user does want to use
1280 // an integrated GPU. On my laptop, this function returns TRUE for the integrated GPU, but crashes
1281 // when trying to use it to render. Maybe I just need to figure out which other extensions and features
1282 // to check.
1283 if (deviceProperties.deviceType != VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
1284 return false;
1285 }
1286
[fa9fa1c]1287 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1288 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
[90a424f]1289 bool swapChainAdequate = false;
1290
1291 if (extensionsSupported) {
[3f32dfd]1292 vector<VkSurfaceFormatKHR> formats = VulkanUtils::querySwapChainFormats(physicalDevice, surface);
1293 vector<VkPresentModeKHR> presentModes = VulkanUtils::querySwapChainPresentModes(physicalDevice, surface);
1294
1295 swapChainAdequate = !formats.empty() && !presentModes.empty();
[90a424f]1296 }
1297
1298 VkPhysicalDeviceFeatures supportedFeatures;
[fa9fa1c]1299 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
[90a424f]1300
1301 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
[c1c2021]1302}
1303
[c324d6a]1304void VulkanGame::createLogicalDevice(const vector<const char*>& validationLayers,
1305 const vector<const char*>& deviceExtensions) {
[c1c2021]1306 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1307
[6a39266]1308 if (!indices.isComplete()) {
1309 throw runtime_error("failed to find required queue families!");
1310 }
1311
1312 // TODO: Using separate graphics and present queues currently works, but I should verify that I'm
1313 // using them correctly to get the most benefit out of separate queues
1314
[b794178]1315 vector<VkDeviceQueueCreateInfo> queueCreateInfoList;
[c1c2021]1316 set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1317
1318 float queuePriority = 1.0f;
1319 for (uint32_t queueFamily : uniqueQueueFamilies) {
1320 VkDeviceQueueCreateInfo queueCreateInfo = {};
1321 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1322 queueCreateInfo.queueCount = 1;
[6a39266]1323 queueCreateInfo.queueFamilyIndex = queueFamily;
[c1c2021]1324 queueCreateInfo.pQueuePriorities = &queuePriority;
1325
[b794178]1326 queueCreateInfoList.push_back(queueCreateInfo);
[c1c2021]1327 }
1328
1329 VkPhysicalDeviceFeatures deviceFeatures = {};
1330 deviceFeatures.samplerAnisotropy = VK_TRUE;
1331
1332 VkDeviceCreateInfo createInfo = {};
1333 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
[c324d6a]1334
[b794178]1335 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfoList.size());
1336 createInfo.pQueueCreateInfos = queueCreateInfoList.data();
[c1c2021]1337
1338 createInfo.pEnabledFeatures = &deviceFeatures;
1339
1340 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
1341 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
1342
1343 // These fields are ignored by up-to-date Vulkan implementations,
1344 // but it's a good idea to set them for backwards compatibility
1345 if (ENABLE_VALIDATION_LAYERS) {
1346 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
1347 createInfo.ppEnabledLayerNames = validationLayers.data();
1348 } else {
1349 createInfo.enabledLayerCount = 0;
1350 }
1351
1352 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
1353 throw runtime_error("failed to create logical device!");
1354 }
1355
1356 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
1357 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
[502bd0b]1358}
1359
[3f32dfd]1360void VulkanGame::chooseSwapChainProperties() {
1361 vector<VkSurfaceFormatKHR> availableFormats = VulkanUtils::querySwapChainFormats(physicalDevice, surface);
1362 vector<VkPresentModeKHR> availablePresentModes = VulkanUtils::querySwapChainPresentModes(physicalDevice, surface);
[502bd0b]1363
[3f32dfd]1364 swapChainSurfaceFormat = VulkanUtils::chooseSwapSurfaceFormat(availableFormats,
1365 { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM },
1366 VK_COLOR_SPACE_SRGB_NONLINEAR_KHR);
[502bd0b]1367
[3f32dfd]1368 vector<VkPresentModeKHR> presentModes{
1369 VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR
1370 };
1371 //vector<VkPresentModeKHR> presentModes{ VK_PRESENT_MODE_FIFO_KHR };
1372
1373 swapChainPresentMode = VulkanUtils::chooseSwapPresentMode(availablePresentModes, presentModes);
1374
1375 cout << "[vulkan] Selected PresentMode = " << swapChainPresentMode << endl;
1376
1377 VkSurfaceCapabilitiesKHR capabilities = VulkanUtils::querySwapChainCapabilities(physicalDevice, surface);
1378
1379 if (swapChainPresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
1380 swapChainMinImageCount = 3;
1381 } else if (swapChainPresentMode == VK_PRESENT_MODE_FIFO_KHR || swapChainPresentMode == VK_PRESENT_MODE_FIFO_RELAXED_KHR) {
1382 swapChainMinImageCount = 2;
1383 } else if (swapChainPresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
1384 swapChainMinImageCount = 1;
1385 } else {
1386 throw runtime_error("unexpected present mode!");
[502bd0b]1387 }
1388
[3f32dfd]1389 if (swapChainMinImageCount < capabilities.minImageCount) {
1390 swapChainMinImageCount = capabilities.minImageCount;
1391 } else if (capabilities.maxImageCount != 0 && swapChainMinImageCount > capabilities.maxImageCount) {
1392 swapChainMinImageCount = capabilities.maxImageCount;
1393 }
1394}
1395
1396void VulkanGame::createSwapChain() {
1397 VkSurfaceCapabilitiesKHR capabilities = VulkanUtils::querySwapChainCapabilities(physicalDevice, surface);
1398
1399 swapChainExtent = VulkanUtils::chooseSwapExtent(capabilities, gui->getWindowWidth(), gui->getWindowHeight());
1400
[502bd0b]1401 VkSwapchainCreateInfoKHR createInfo = {};
1402 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1403 createInfo.surface = surface;
[3f32dfd]1404 createInfo.minImageCount = swapChainMinImageCount;
1405 createInfo.imageFormat = swapChainSurfaceFormat.format;
1406 createInfo.imageColorSpace = swapChainSurfaceFormat.colorSpace;
1407 createInfo.imageExtent = swapChainExtent;
[502bd0b]1408 createInfo.imageArrayLayers = 1;
1409 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1410
[3f32dfd]1411 // TODO: Maybe save this result so I don't have to recalculate it every time
[502bd0b]1412 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1413 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1414
1415 if (indices.graphicsFamily != indices.presentFamily) {
1416 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
1417 createInfo.queueFamilyIndexCount = 2;
1418 createInfo.pQueueFamilyIndices = queueFamilyIndices;
[f94eea9]1419 } else {
[502bd0b]1420 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1421 createInfo.queueFamilyIndexCount = 0;
1422 createInfo.pQueueFamilyIndices = nullptr;
1423 }
1424
[3f32dfd]1425 createInfo.preTransform = capabilities.currentTransform;
[502bd0b]1426 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
[3f32dfd]1427 createInfo.presentMode = swapChainPresentMode;
[502bd0b]1428 createInfo.clipped = VK_TRUE;
1429 createInfo.oldSwapchain = VK_NULL_HANDLE;
1430
1431 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
1432 throw runtime_error("failed to create swap chain!");
1433 }
1434
[3f32dfd]1435 if (vkGetSwapchainImagesKHR(device, swapChain, &swapChainImageCount, nullptr) != VK_SUCCESS) {
1436 throw runtime_error("failed to get swap chain image count!");
1437 }
[502bd0b]1438
[3f32dfd]1439 swapChainImages.resize(swapChainImageCount);
1440 if (vkGetSwapchainImagesKHR(device, swapChain, &swapChainImageCount, swapChainImages.data()) != VK_SUCCESS) {
1441 throw runtime_error("failed to get swap chain images!");
1442 }
[f94eea9]1443}
1444
1445void VulkanGame::createImageViews() {
[3f32dfd]1446 swapChainImageViews.resize(swapChainImageCount);
[f94eea9]1447
[3f32dfd]1448 for (size_t i = 0; i < swapChainImageCount; i++) {
1449 swapChainImageViews[i] = VulkanUtils::createImageView(device, swapChainImages[i], swapChainSurfaceFormat.format,
[f94eea9]1450 VK_IMAGE_ASPECT_COLOR_BIT);
1451 }
1452}
1453
[6fc24c7]1454void VulkanGame::createRenderPass() {
1455 VkAttachmentDescription colorAttachment = {};
[3f32dfd]1456 colorAttachment.format = swapChainSurfaceFormat.format;
[6fc24c7]1457 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
1458 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1459 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1460 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1461 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1462 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1463 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1464
1465 VkAttachmentReference colorAttachmentRef = {};
1466 colorAttachmentRef.attachment = 0;
1467 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1468
1469 VkAttachmentDescription depthAttachment = {};
1470 depthAttachment.format = findDepthFormat();
1471 depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
1472 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1473 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1474 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1475 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1476 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1477 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1478
1479 VkAttachmentReference depthAttachmentRef = {};
1480 depthAttachmentRef.attachment = 1;
1481 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1482
1483 VkSubpassDescription subpass = {};
1484 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1485 subpass.colorAttachmentCount = 1;
1486 subpass.pColorAttachments = &colorAttachmentRef;
1487 subpass.pDepthStencilAttachment = &depthAttachmentRef;
1488
1489 VkSubpassDependency dependency = {};
1490 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
1491 dependency.dstSubpass = 0;
1492 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1493 dependency.srcAccessMask = 0;
1494 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1495 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1496
1497 array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
1498 VkRenderPassCreateInfo renderPassInfo = {};
1499 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1500 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1501 renderPassInfo.pAttachments = attachments.data();
1502 renderPassInfo.subpassCount = 1;
1503 renderPassInfo.pSubpasses = &subpass;
1504 renderPassInfo.dependencyCount = 1;
1505 renderPassInfo.pDependencies = &dependency;
1506
1507 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
1508 throw runtime_error("failed to create render pass!");
1509 }
1510}
1511
1512VkFormat VulkanGame::findDepthFormat() {
1513 return VulkanUtils::findSupportedFormat(
1514 physicalDevice,
1515 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
1516 VK_IMAGE_TILING_OPTIMAL,
1517 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
1518 );
1519}
1520
[3f32dfd]1521void VulkanGame::createResourceCommandPool() {
1522 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1523
1524 VkCommandPoolCreateInfo poolInfo = {};
1525 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1526 poolInfo.queueFamilyIndex = indices.graphicsFamily.value();
1527 poolInfo.flags = 0;
1528
1529 if (vkCreateCommandPool(device, &poolInfo, nullptr, &resourceCommandPool) != VK_SUCCESS) {
1530 throw runtime_error("failed to create resource command pool!");
1531 }
1532}
1533
[9c0a614]1534void VulkanGame::createCommandPools() {
1535 commandPools.resize(swapChainImageCount);
[fa9fa1c]1536
[9c0a614]1537 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1538
1539 for (size_t i = 0; i < swapChainImageCount; i++) {
1540 VkCommandPoolCreateInfo poolInfo = {};
1541 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1542 poolInfo.queueFamilyIndex = indices.graphicsFamily.value();
1543 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
[fa9fa1c]1544
[9c0a614]1545 VKUTIL_CHECK_RESULT(vkCreateCommandPool(device, &poolInfo, nullptr, &commandPools[i]),
1546 "failed to create graphics command pool!");
[fa9fa1c]1547 }
1548}
1549
[603b5bc]1550void VulkanGame::createImageResources() {
[3f32dfd]1551 VulkanUtils::createDepthImage(device, physicalDevice, resourceCommandPool, findDepthFormat(), swapChainExtent,
[603b5bc]1552 depthImage, graphicsQueue);
[b794178]1553
[603b5bc]1554 createTextureSampler();
[b794178]1555
[4994692]1556 // TODO: Move all images/textures somewhere into the assets folder
[b794178]1557
[3f32dfd]1558 VulkanUtils::createVulkanImageFromFile(device, physicalDevice, resourceCommandPool, "textures/texture.jpg",
[4994692]1559 floorTextureImage, graphicsQueue);
1560
1561 floorTextureImageDescriptor = {};
1562 floorTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1563 floorTextureImageDescriptor.imageView = floorTextureImage.imageView;
1564 floorTextureImageDescriptor.sampler = textureSampler;
[237cbec]1565
[3f32dfd]1566 VulkanUtils::createVulkanImageFromFile(device, physicalDevice, resourceCommandPool, "textures/laser.png",
[237cbec]1567 laserTextureImage, graphicsQueue);
1568
1569 laserTextureImageDescriptor = {};
1570 laserTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1571 laserTextureImageDescriptor.imageView = laserTextureImage.imageView;
1572 laserTextureImageDescriptor.sampler = textureSampler;
[b794178]1573}
1574
1575void VulkanGame::createTextureSampler() {
1576 VkSamplerCreateInfo samplerInfo = {};
1577 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
1578 samplerInfo.magFilter = VK_FILTER_LINEAR;
1579 samplerInfo.minFilter = VK_FILTER_LINEAR;
1580
1581 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1582 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1583 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1584
1585 samplerInfo.anisotropyEnable = VK_TRUE;
1586 samplerInfo.maxAnisotropy = 16;
1587 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
1588 samplerInfo.unnormalizedCoordinates = VK_FALSE;
1589 samplerInfo.compareEnable = VK_FALSE;
1590 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
1591 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
1592 samplerInfo.mipLodBias = 0.0f;
1593 samplerInfo.minLod = 0.0f;
1594 samplerInfo.maxLod = 0.0f;
1595
1596 if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
1597 throw runtime_error("failed to create texture sampler!");
1598 }
1599}
1600
[603b5bc]1601void VulkanGame::createFramebuffers() {
[3f32dfd]1602 swapChainFramebuffers.resize(swapChainImageCount);
1603
1604 VkFramebufferCreateInfo framebufferInfo = {};
1605 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1606 framebufferInfo.renderPass = renderPass;
1607 framebufferInfo.width = swapChainExtent.width;
1608 framebufferInfo.height = swapChainExtent.height;
1609 framebufferInfo.layers = 1;
[603b5bc]1610
[3f32dfd]1611 for (uint32_t i = 0; i < swapChainImageCount; i++) {
[603b5bc]1612 array<VkImageView, 2> attachments = {
1613 swapChainImageViews[i],
1614 depthImage.imageView
1615 };
1616
1617 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1618 framebufferInfo.pAttachments = attachments.data();
1619
1620 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
1621 throw runtime_error("failed to create framebuffer!");
1622 }
1623 }
1624}
1625
1626void VulkanGame::createCommandBuffers() {
[3f32dfd]1627 commandBuffers.resize(swapChainImageCount);
[603b5bc]1628
[9c0a614]1629 for (size_t i = 0; i < swapChainImageCount; i++) {
1630 VkCommandBufferAllocateInfo allocInfo = {};
1631 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1632 allocInfo.commandPool = commandPools[i];
1633 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1634 allocInfo.commandBufferCount = 1;
1635
1636 if (vkAllocateCommandBuffers(device, &allocInfo, &commandBuffers[i]) != VK_SUCCESS) {
1637 throw runtime_error("failed to allocate command buffer!");
1638 }
[603b5bc]1639 }
1640}
1641
[ea2b4dc]1642void VulkanGame::renderFrame(ImDrawData* draw_data) {
[4e2c709]1643 VkResult result = vkAcquireNextImageKHR(device, swapChain, numeric_limits<uint64_t>::max(),
1644 imageAcquiredSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
1645
[7734042]1646 if (result == VK_SUBOPTIMAL_KHR) {
1647 shouldRecreateSwapChain = true;
1648 } else if (result == VK_ERROR_OUT_OF_DATE_KHR) {
1649 shouldRecreateSwapChain = true;
[4e2c709]1650 return;
[7734042]1651 } else {
1652 VKUTIL_CHECK_RESULT(result, "failed to acquire swap chain image!");
[4e2c709]1653 }
1654
[7734042]1655 VKUTIL_CHECK_RESULT(
1656 vkWaitForFences(device, 1, &inFlightFences[imageIndex], VK_TRUE, numeric_limits<uint64_t>::max()),
1657 "failed waiting for fence!");
1658
1659 VKUTIL_CHECK_RESULT(vkResetFences(device, 1, &inFlightFences[imageIndex]),
1660 "failed to reset fence!");
[4e2c709]1661
[1cb64e6]1662 VKUTIL_CHECK_RESULT(vkResetCommandPool(device, commandPools[imageIndex], 0),
1663 "failed to reset command pool!");
1664
1665 VkCommandBufferBeginInfo beginInfo = {};
1666 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
[7734042]1667 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
[1cb64e6]1668
[7734042]1669 VKUTIL_CHECK_RESULT(vkBeginCommandBuffer(commandBuffers[imageIndex], &beginInfo),
1670 "failed to begin recording command buffer!");
[1cb64e6]1671
1672 VkRenderPassBeginInfo renderPassInfo = {};
1673 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1674 renderPassInfo.renderPass = renderPass;
1675 renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
1676 renderPassInfo.renderArea.offset = { 0, 0 };
1677 renderPassInfo.renderArea.extent = swapChainExtent;
1678
1679 array<VkClearValue, 2> clearValues = {};
1680 clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
1681 clearValues[1].depthStencil = { 1.0f, 0 };
1682
1683 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
1684 renderPassInfo.pClearValues = clearValues.data();
1685
1686 vkCmdBeginRenderPass(commandBuffers[imageIndex], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1687
[20e4c2b]1688 // TODO: Find a more elegant, per-screen solution for this
1689 if (currentRenderScreenFn == &VulkanGame::renderGameScreen) {
1690 modelPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
1691 shipPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
1692 asteroidPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
1693 laserPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
1694 explosionPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex);
1695 }
[1cb64e6]1696
[ea2b4dc]1697 ImGui_ImplVulkan_RenderDrawData(draw_data, commandBuffers[imageIndex]);
[1cb64e6]1698
1699 vkCmdEndRenderPass(commandBuffers[imageIndex]);
1700
[7734042]1701 VKUTIL_CHECK_RESULT(vkEndCommandBuffer(commandBuffers[imageIndex]),
1702 "failed to record command buffer!");
[1cb64e6]1703
[4e2c709]1704 VkSemaphore waitSemaphores[] = { imageAcquiredSemaphores[currentFrame] };
1705 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
1706 VkSemaphore signalSemaphores[] = { renderCompleteSemaphores[currentFrame] };
1707
1708 VkSubmitInfo submitInfo = {};
1709 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1710 submitInfo.waitSemaphoreCount = 1;
1711 submitInfo.pWaitSemaphores = waitSemaphores;
1712 submitInfo.pWaitDstStageMask = waitStages;
1713 submitInfo.commandBufferCount = 1;
1714 submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
1715 submitInfo.signalSemaphoreCount = 1;
1716 submitInfo.pSignalSemaphores = signalSemaphores;
1717
[1cb64e6]1718 VKUTIL_CHECK_RESULT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[imageIndex]),
1719 "failed to submit draw command buffer!");
[4e2c709]1720}
1721
1722void VulkanGame::presentFrame() {
1723 VkSemaphore signalSemaphores[] = { renderCompleteSemaphores[currentFrame] };
1724
1725 VkPresentInfoKHR presentInfo = {};
1726 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1727 presentInfo.waitSemaphoreCount = 1;
1728 presentInfo.pWaitSemaphores = signalSemaphores;
1729 presentInfo.swapchainCount = 1;
1730 presentInfo.pSwapchains = &swapChain;
1731 presentInfo.pImageIndices = &imageIndex;
1732 presentInfo.pResults = nullptr;
1733
1734 VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo);
1735
[1cb64e6]1736 if (result == VK_SUBOPTIMAL_KHR) {
1737 shouldRecreateSwapChain = true;
1738 } else if (result == VK_ERROR_OUT_OF_DATE_KHR) {
1739 shouldRecreateSwapChain = true;
1740 return;
1741 } else {
1742 VKUTIL_CHECK_RESULT(result, "failed to present swap chain image!");
[4e2c709]1743 }
1744
1745 currentFrame = (currentFrame + 1) % swapChainImageCount;
1746}
1747
[3f32dfd]1748void VulkanGame::createSyncObjects() {
1749 imageAcquiredSemaphores.resize(swapChainImageCount);
1750 renderCompleteSemaphores.resize(swapChainImageCount);
1751 inFlightFences.resize(swapChainImageCount);
1752
1753 VkSemaphoreCreateInfo semaphoreInfo = {};
1754 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1755
1756 VkFenceCreateInfo fenceInfo = {};
1757 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1758 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1759
1760 for (size_t i = 0; i < swapChainImageCount; i++) {
1761 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAcquiredSemaphores[i]) != VK_SUCCESS) {
1762 throw runtime_error("failed to create image acquired sempahore for a frame!");
1763 }
1764
1765 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderCompleteSemaphores[i]) != VK_SUCCESS) {
1766 throw runtime_error("failed to create render complete sempahore for a frame!");
1767 }
1768
1769 if (vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
1770 throw runtime_error("failed to create fence for a frame!");
1771 }
1772 }
1773}
1774
[3b7d497]1775void VulkanGame::createImguiDescriptorPool() {
1776 vector<VkDescriptorPoolSize> pool_sizes{
1777 { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
1778 { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
1779 { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
1780 { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
1781 { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
1782 { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
1783 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
1784 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
1785 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
1786 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
1787 { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
1788 };
1789
1790 VkDescriptorPoolCreateInfo pool_info = {};
1791 pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
1792 pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
1793 pool_info.maxSets = 1000 * pool_sizes.size();
1794 pool_info.poolSizeCount = static_cast<uint32_t>(pool_sizes.size());
1795 pool_info.pPoolSizes = pool_sizes.data();
1796 if (vkCreateDescriptorPool(device, &pool_info, nullptr, &imguiDescriptorPool) != VK_SUCCESS) {
1797 throw runtime_error("failed to create IMGUI descriptor pool!");
1798 }
1799}
1800
1801void VulkanGame::destroyImguiDescriptorPool() {
1802 vkDestroyDescriptorPool(device, imguiDescriptorPool, nullptr);
1803}
1804
[52a02e6]1805void VulkanGame::addLaser(vec3 start, vec3 end, vec3 color, float width) {
[1f81ecc]1806 vec3 ray = end - start;
1807 float length = glm::length(ray);
1808
1809 SceneObject<LaserVertex, SSBO_Laser>& laser = addObject(
1810 laserObjects, laserPipeline,
1811 addObjectIndex<LaserVertex>(laserObjects.size(), {
1812 {{ width / 2, 0.0f, -width / 2 }, {1.0f, 0.5f }},
1813 {{-width / 2, 0.0f, -width / 2 }, {0.0f, 0.5f }},
1814 {{-width / 2, 0.0f, 0.0f }, {0.0f, 0.0f }},
1815 {{ width / 2, 0.0f, 0.0f }, {1.0f, 0.0f }},
[3950236]1816 {{ width / 2, 0.0f, -length + width / 2}, {1.0f, 0.51f}},
[1f81ecc]1817 {{-width / 2, 0.0f, -length + width / 2}, {0.0f, 0.51f}},
1818 {{ width / 2, 0.0f, -length, }, {1.0f, 1.0f }},
1819 {{-width / 2, 0.0f, -length }, {0.0f, 1.0f }}
1820 }), {
1821 0, 1, 2, 0, 2, 3,
1822 4, 5, 1, 4, 1, 0,
1823 6, 7, 5, 6, 5, 4
1824 }, {
1825 mat4(1.0f),
1826 color,
1827 false
1828 }, true);
1829
1830 float xAxisRotation = asin(ray.y / length);
1831 float yAxisRotation = atan2(-ray.x, -ray.z);
1832
1833 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
1834 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
1835 vec4(0.0f, 1.0f, 0.0f, 1.0f));
1836
1837 // To project point P onto line AB:
1838 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
1839 vec3 projOnLaser = start + glm::dot(this->cam_pos - start, ray) / (length * length) * ray;
1840 vec3 laserToCam = this->cam_pos - projOnLaser;
1841
1842 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
1843
[3950236]1844 laser.targetAsteroid = nullptr;
1845
[1f81ecc]1846 laser.model_base =
1847 rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
1848
1849 laser.model_transform =
1850 translate(mat4(1.0f), start) *
1851 rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
1852 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f));
1853
1854 laser.modified = true;
1855}
1856
1857void VulkanGame::translateLaser(size_t index, const vec3& translation) {
1858 SceneObject<LaserVertex, SSBO_Laser>& laser = this->laserObjects[index];
1859
1860 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
1861 // and then re-used here
1862
1863 vec3 start = vec3(laser.model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
1864 vec3 end = vec3(laser.model_transform * vec4(0.0f, 0.0f, laser.vertices[6].pos.z, 1.0f));
1865
1866 vec3 ray = end - start;
1867 float length = glm::length(ray);
1868
1869 float xAxisRotation = asin(ray.y / length);
1870 float yAxisRotation = atan2(-ray.x, -ray.z);
1871
1872 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
1873 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
1874 vec4(0.0f, 1.0f, 0.0f, 1.0f));
1875
1876 // To project point P onto line AB:
1877 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
1878 vec3 projOnLaser = start + glm::dot(cam_pos - start, ray) / (length*length) * ray;
1879 vec3 laserToCam = cam_pos - projOnLaser;
1880
1881 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
1882
1883 laser.model_base = rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
1884 laser.model_transform = translate(mat4(1.0f), translation) * laser.model_transform;
1885
1886 laser.modified = true;
1887}
1888
[3950236]1889void VulkanGame::updateLaserTarget(size_t index) {
1890 SceneObject<LaserVertex, SSBO_Laser>& laser = this->laserObjects[index];
1891
1892 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
1893 // and then re-used here
1894
1895 vec3 start = vec3(laser.model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
1896 vec3 end = vec3(laser.model_transform * vec4(0.0f, 0.0f, laser.vertices[6].pos.z, 1.0f));
1897
1898 vec3 intersection(0.0f), closestIntersection(0.0f);
1899 SceneObject<AsteroidVertex, SSBO_Asteroid>* closestAsteroid = nullptr;
1900 unsigned int closestAsteroidIndex = -1;
1901
1902 for (int i = 0; i < this->asteroidObjects.size(); i++) {
1903 if (!this->asteroidObjects[i].ssbo.deleted &&
1904 this->getLaserAndAsteroidIntersection(this->asteroidObjects[i], start, end, intersection)) {
1905 // TODO: Implement a more generic algorithm for testing the closest object by getting the distance between the points
1906 // TODO: Also check which intersection is close to the start of the laser. This would make the algorithm work
1907 // regardless of which way -Z is pointing
1908 if (closestAsteroid == nullptr || intersection.z > closestIntersection.z) {
1909 // TODO: At this point, find the real intersection of the laser with one of the asteroid's sides
1910 closestAsteroid = &asteroidObjects[i];
1911 closestIntersection = intersection;
1912 closestAsteroidIndex = i;
1913 }
1914 }
1915 }
1916
1917 float width = laser.vertices[0].pos.x - laser.vertices[1].pos.x;
1918
1919 if (laser.targetAsteroid != closestAsteroid) {
[7297892]1920 if (laser.targetAsteroid != nullptr) {
1921 if (index == leftLaserIdx && leftLaserEffect != nullptr) {
1922 leftLaserEffect->deleted = true;
1923 } else if (index == rightLaserIdx && rightLaserEffect != nullptr) {
1924 rightLaserEffect->deleted = true;
1925 }
1926 }
1927
1928 EffectOverTime<AsteroidVertex, SSBO_Asteroid>* eot = nullptr;
1929
1930 if (closestAsteroid != nullptr) {
1931 // TODO: Use some sort of smart pointer instead
[aa7707d]1932 eot = new EffectOverTime<AsteroidVertex, SSBO_Asteroid>(asteroidPipeline, asteroidObjects, closestAsteroidIndex,
[5192672]1933 offset_of(&SSBO_Asteroid::hp), curTime, -20.0f);
[7297892]1934 effects.push_back(eot);
1935 }
1936
1937 if (index == leftLaserIdx) {
1938 leftLaserEffect = eot;
1939 } else if (index == rightLaserIdx) {
1940 rightLaserEffect = eot;
1941 }
1942
[3950236]1943 laser.targetAsteroid = closestAsteroid;
1944 }
1945
1946 // Make the laser go past the end of the screen if it doesn't hit anything
1947 float length = closestAsteroid == nullptr ? 5.24f : glm::length(closestIntersection - start);
1948
1949 laser.vertices[4].pos.z = -length + width / 2;
1950 laser.vertices[5].pos.z = -length + width / 2;
1951 laser.vertices[6].pos.z = -length;
1952 laser.vertices[7].pos.z = -length;
1953
[5192672]1954 // TODO: Consider if I want to set a flag and do this update in updateScene() instead
[3950236]1955 updateObjectVertices(this->laserPipeline, laser, index);
1956}
1957
1958// TODO: Determine if I should pass start and end by reference or value since they don't get changed
1959// Probably use const reference
1960bool VulkanGame::getLaserAndAsteroidIntersection(SceneObject<AsteroidVertex, SSBO_Asteroid>& asteroid,
1961 vec3& start, vec3& end, vec3& intersection) {
1962 /*
1963 ### LINE EQUATIONS ###
1964 x = x1 + u * (x2 - x1)
1965 y = y1 + u * (y2 - y1)
1966 z = z1 + u * (z2 - z1)
1967
1968 ### SPHERE EQUATION ###
1969 (x - x3)^2 + (y - y3)^2 + (z - z3)^2 = r^2
1970
1971 ### QUADRATIC EQUATION TO SOLVE ###
1972 a*u^2 + b*u + c = 0
1973 WHERE THE CONSTANTS ARE
1974 a = (x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2
1975 b = 2*( (x2 - x1)*(x1 - x3) + (y2 - y1)*(y1 - y3) + (z2 - z1)*(z1 - z3) )
1976 c = x3^2 + y3^2 + z3^2 + x1^2 + y1^2 + z1^2 - 2(x3*x1 + y3*y1 + z3*z1) - r^2
1977
1978 u = (-b +- sqrt(b^2 - 4*a*c)) / 2a
1979
1980 If the value under the root is >= 0, we got an intersection
1981 If the value > 0, there are two solutions. Take the one closer to 0, since that's the
1982 one closer to the laser start point
1983 */
1984
1985 vec3& center = asteroid.center;
1986
1987 float a = pow(end.x - start.x, 2) + pow(end.y - start.y, 2) + pow(end.z - start.z, 2);
1988 float b = 2 * ((start.x - end.x) * (start.x - center.x) + (end.y - start.y) * (start.y - center.y) +
1989 (end.z - start.z) * (start.z - center.z));
1990 float c = pow(center.x, 2) + pow(center.y, 2) + pow(center.z, 2) + pow(start.x, 2) + pow(start.y, 2) +
1991 pow(start.z, 2) - 2 * (center.x * start.x + center.y * start.y + center.z * start.z) -
1992 pow(asteroid.radius, 2);
1993 float discriminant = pow(b, 2) - 4 * a * c;
1994
1995 if (discriminant >= 0.0f) {
1996 // In this case, the negative root will always give the point closer to the laser start point
1997 float u = (-b - sqrt(discriminant)) / (2 * a);
1998
1999 // Check that the intersection is within the line segment corresponding to the laser
2000 if (0.0f <= u && u <= 1.0f) {
2001 intersection = start + u * (end - start);
2002 return true;
2003 }
2004 }
2005
2006 return false;
2007}
2008
[055750a]2009void VulkanGame::createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags flags,
2010 vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory, vector<VkDescriptorBufferInfo>& bufferInfoList) {
[3f32dfd]2011 buffers.resize(swapChainImageCount);
2012 buffersMemory.resize(swapChainImageCount);
2013 bufferInfoList.resize(swapChainImageCount);
[055750a]2014
[3f32dfd]2015 for (size_t i = 0; i < swapChainImageCount; i++) {
[055750a]2016 VulkanUtils::createBuffer(device, physicalDevice, bufferSize, flags,
2017 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
2018 buffers[i], buffersMemory[i]);
2019
2020 bufferInfoList[i].buffer = buffers[i];
2021 bufferInfoList[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
2022 bufferInfoList[i].range = bufferSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
2023 }
2024}
2025
[4a9416a]2026void VulkanGame::addExplosion(mat4 model_mat, float duration, float cur_time) {
2027 vector<ExplosionVertex> vertices;
2028 vertices.reserve(EXPLOSION_PARTICLE_COUNT);
2029
2030 float particle_start_time = 0.0f;
2031
2032 for (int i = 0; i < EXPLOSION_PARTICLE_COUNT; i++) {
2033 float randx = ((float)rand() / (float)RAND_MAX) - 0.5f;
2034 float randy = ((float)rand() / (float)RAND_MAX) - 0.5f;
2035
2036 vertices.push_back({ vec3(randx, randy, 0.0f), particle_start_time});
2037
2038 particle_start_time += .01f;
2039 // TODO: Get this working
2040 // particle_start_time += 1.0f * EXPLOSION_PARTICLE_COUNT / duration
2041 }
2042
2043 // Fill the indices with the the first EXPLOSION_PARTICLE_COUNT ints
2044 vector<uint16_t> indices(EXPLOSION_PARTICLE_COUNT);
2045 iota(indices.begin(), indices.end(), 0);
2046
2047 SceneObject<ExplosionVertex, SSBO_Explosion>& explosion = addObject(
2048 explosionObjects, explosionPipeline,
2049 addObjectIndex(explosionObjects.size(), vertices),
2050 indices, {
2051 mat4(1.0f),
2052 cur_time,
2053 duration,
2054 false
2055 }, true);
2056
2057 explosion.model_base = model_mat;
2058 explosion.model_transform = mat4(1.0f);
2059
2060 explosion.modified = true;
2061}
2062
[d2d9286]2063void VulkanGame::recreateSwapChain() {
[ce9dc9f]2064 if (vkDeviceWaitIdle(device) != VK_SUCCESS) {
2065 throw runtime_error("failed to wait for device!");
2066 }
[d2d9286]2067
[0ae182f]2068 cleanupSwapChain();
2069
2070 createSwapChain();
2071 createImageViews();
2072 createRenderPass();
2073
[9c0a614]2074 createCommandPools();
2075
2076 // The depth buffer does need to be recreated with the swap chain since its dimensions depend on the window size
2077 // and resizing the window is a common reason to recreate the swapchain
[3f32dfd]2078 VulkanUtils::createDepthImage(device, physicalDevice, resourceCommandPool, findDepthFormat(), swapChainExtent,
[0ae182f]2079 depthImage, graphicsQueue);
2080 createFramebuffers();
[f97c5e7]2081
[2da64ef]2082 // TODO: Move UBO creation/management into GraphicsPipeline_Vulkan, like I did with SSBOs
2083
[055750a]2084 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
[d25381b]2085 uniformBuffers_modelPipeline, uniformBuffersMemory_modelPipeline, uniformBufferInfoList_modelPipeline);
[0ae182f]2086
[b8777b7]2087 modelPipeline.updateRenderPass(renderPass);
2088 modelPipeline.createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
2089 modelPipeline.createDescriptorPool(swapChainImages);
2090 modelPipeline.createDescriptorSets(swapChainImages);
[0ae182f]2091
[055750a]2092 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
2093 uniformBuffers_shipPipeline, uniformBuffersMemory_shipPipeline, uniformBufferInfoList_shipPipeline);
[3782d66]2094
2095 shipPipeline.updateRenderPass(renderPass);
2096 shipPipeline.createPipeline("shaders/ship-vert.spv", "shaders/ship-frag.spv");
2097 shipPipeline.createDescriptorPool(swapChainImages);
2098 shipPipeline.createDescriptorSets(swapChainImages);
2099
[3e8cc8b]2100 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
2101 uniformBuffers_asteroidPipeline, uniformBuffersMemory_asteroidPipeline, uniformBufferInfoList_asteroidPipeline);
2102
2103 asteroidPipeline.updateRenderPass(renderPass);
2104 asteroidPipeline.createPipeline("shaders/asteroid-vert.spv", "shaders/asteroid-frag.spv");
2105 asteroidPipeline.createDescriptorPool(swapChainImages);
2106 asteroidPipeline.createDescriptorSets(swapChainImages);
2107
[237cbec]2108 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
2109 uniformBuffers_laserPipeline, uniformBuffersMemory_laserPipeline, uniformBufferInfoList_laserPipeline);
2110
2111 laserPipeline.updateRenderPass(renderPass);
2112 laserPipeline.createPipeline("shaders/laser-vert.spv", "shaders/laser-frag.spv");
2113 laserPipeline.createDescriptorPool(swapChainImages);
2114 laserPipeline.createDescriptorSets(swapChainImages);
2115
[4a9416a]2116 createBufferSet(sizeof(UBO_Explosion), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
2117 uniformBuffers_explosionPipeline, uniformBuffersMemory_explosionPipeline, uniformBufferInfoList_explosionPipeline);
2118
2119 explosionPipeline.updateRenderPass(renderPass);
2120 explosionPipeline.createPipeline("shaders/explosion-vert.spv", "shaders/explosion-frag.spv");
2121 explosionPipeline.createDescriptorPool(swapChainImages);
2122 explosionPipeline.createDescriptorSets(swapChainImages);
2123
[0ae182f]2124 createCommandBuffers();
[3f32dfd]2125
2126 createSyncObjects();
2127
2128 imageIndex = 0;
[d2d9286]2129}
2130
[f94eea9]2131void VulkanGame::cleanupSwapChain() {
[603b5bc]2132 VulkanUtils::destroyVulkanImage(device, depthImage);
2133
2134 for (VkFramebuffer framebuffer : swapChainFramebuffers) {
2135 vkDestroyFramebuffer(device, framebuffer, nullptr);
2136 }
2137
[9c0a614]2138 for (uint32_t i = 0; i < swapChainImageCount; i++) {
2139 vkFreeCommandBuffers(device, commandPools[i], 1, &commandBuffers[i]);
2140 vkDestroyCommandPool(device, commandPools[i], nullptr);
2141 }
[603b5bc]2142
[860a0da]2143 modelPipeline.cleanup();
[3782d66]2144 shipPipeline.cleanup();
[3e8cc8b]2145 asteroidPipeline.cleanup();
[237cbec]2146 laserPipeline.cleanup();
[4a9416a]2147 explosionPipeline.cleanup();
[b794178]2148
[d25381b]2149 for (size_t i = 0; i < uniformBuffers_modelPipeline.size(); i++) {
2150 vkDestroyBuffer(device, uniformBuffers_modelPipeline[i], nullptr);
2151 vkFreeMemory(device, uniformBuffersMemory_modelPipeline[i], nullptr);
[055750a]2152 }
2153
[3782d66]2154 for (size_t i = 0; i < uniformBuffers_shipPipeline.size(); i++) {
2155 vkDestroyBuffer(device, uniformBuffers_shipPipeline[i], nullptr);
2156 vkFreeMemory(device, uniformBuffersMemory_shipPipeline[i], nullptr);
2157 }
[055750a]2158
[3e8cc8b]2159 for (size_t i = 0; i < uniformBuffers_asteroidPipeline.size(); i++) {
2160 vkDestroyBuffer(device, uniformBuffers_asteroidPipeline[i], nullptr);
2161 vkFreeMemory(device, uniformBuffersMemory_asteroidPipeline[i], nullptr);
2162 }
2163
[237cbec]2164 for (size_t i = 0; i < uniformBuffers_laserPipeline.size(); i++) {
2165 vkDestroyBuffer(device, uniformBuffers_laserPipeline[i], nullptr);
2166 vkFreeMemory(device, uniformBuffersMemory_laserPipeline[i], nullptr);
2167 }
2168
[4a9416a]2169 for (size_t i = 0; i < uniformBuffers_explosionPipeline.size(); i++) {
2170 vkDestroyBuffer(device, uniformBuffers_explosionPipeline[i], nullptr);
2171 vkFreeMemory(device, uniformBuffersMemory_explosionPipeline[i], nullptr);
2172 }
2173
[3f32dfd]2174 for (size_t i = 0; i < swapChainImageCount; i++) {
2175 vkDestroySemaphore(device, imageAcquiredSemaphores[i], nullptr);
2176 vkDestroySemaphore(device, renderCompleteSemaphores[i], nullptr);
2177 vkDestroyFence(device, inFlightFences[i], nullptr);
2178 }
2179
[860a0da]2180 vkDestroyRenderPass(device, renderPass, nullptr);
2181
2182 for (VkImageView imageView : swapChainImageViews) {
2183 vkDestroyImageView(device, imageView, nullptr);
[3e8cc8b]2184 }
[860a0da]2185
2186 vkDestroySwapchainKHR(device, swapChain, nullptr);
[cc4a8b5]2187}
[20e4c2b]2188
2189void VulkanGame::renderMainScreen() {
2190 unsigned int windowWidth = 640;
2191 unsigned int windowHeight = 480;
2192
2193 {
2194 int padding = 4;
2195 ImGui::SetNextWindowPos(ImVec2(-padding, -padding), ImGuiCond_Once);
2196 ImGui::SetNextWindowSize(ImVec2(windowWidth + 2 * padding, windowHeight + 2 * padding), ImGuiCond_Always);
2197 ImGui::Begin("WndMain", nullptr,
2198 ImGuiWindowFlags_NoTitleBar |
2199 ImGuiWindowFlags_NoResize |
2200 ImGuiWindowFlags_NoMove);
2201
2202 ImGui::InvisibleButton("", ImVec2(10, 80));
2203 ImGui::InvisibleButton("", ImVec2(285, 18));
2204 ImGui::SameLine();
2205 if (ImGui::Button("New Game")) {
2206 goToScreen(&VulkanGame::renderGameScreen);
2207 }
2208
2209 ImGui::InvisibleButton("", ImVec2(10, 15));
2210 ImGui::InvisibleButton("", ImVec2(300, 18));
2211 ImGui::SameLine();
2212 if (ImGui::Button("Quit")) {
2213 quitGame();
2214 }
2215
2216 ImGui::End();
2217 }
2218}
2219
2220void VulkanGame::renderGameScreen() {
2221 {
2222 ImGui::SetNextWindowSize(ImVec2(130, 65), ImGuiCond_Once);
2223 ImGui::SetNextWindowPos(ImVec2(10, 50), ImGuiCond_Once);
2224 ImGui::Begin("WndStats", nullptr,
2225 ImGuiWindowFlags_NoTitleBar |
2226 ImGuiWindowFlags_NoResize |
2227 ImGuiWindowFlags_NoMove);
2228
2229 //ImGui::Text(ImGui::GetIO().Framerate);
2230 renderGuiValueList(valueLists["stats value list"]);
2231
2232 ImGui::End();
2233 }
2234
2235 {
2236 ImGui::SetNextWindowSize(ImVec2(250, 35), ImGuiCond_Once);
2237 ImGui::SetNextWindowPos(ImVec2(380, 10), ImGuiCond_Once);
2238 ImGui::Begin("WndMenubar", nullptr,
2239 ImGuiWindowFlags_NoTitleBar |
2240 ImGuiWindowFlags_NoResize |
2241 ImGuiWindowFlags_NoMove);
2242 ImGui::InvisibleButton("", ImVec2(155, 18));
2243 ImGui::SameLine();
2244 if (ImGui::Button("Main Menu")) {
2245 goToScreen(&VulkanGame::renderMainScreen);
2246 }
2247 ImGui::End();
2248 }
2249
2250 {
2251 ImGui::SetNextWindowSize(ImVec2(200, 200), ImGuiCond_Once);
2252 ImGui::SetNextWindowPos(ImVec2(430, 60), ImGuiCond_Once);
2253 ImGui::Begin("WndDebug", nullptr,
2254 ImGuiWindowFlags_NoTitleBar |
2255 ImGuiWindowFlags_NoResize |
2256 ImGuiWindowFlags_NoMove);
2257
2258 renderGuiValueList(valueLists["debug value list"]);
2259
2260 ImGui::End();
2261 }
2262}
2263
2264void VulkanGame::initGuiValueLists(map<string, vector<UIValue>>& valueLists) {
2265 valueLists["stats value list"] = vector<UIValue>();
2266 valueLists["debug value list"] = vector<UIValue>();
2267}
2268
2269void VulkanGame::renderGuiValueList(vector<UIValue>& values) {
2270 float maxWidth = 0.0f;
2271 float cursorStartPos = ImGui::GetCursorPosX();
2272
2273 for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
2274 float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
2275
2276 if (maxWidth < textWidth)
2277 maxWidth = textWidth;
2278 }
2279
2280 stringstream ss;
2281
2282 // TODO: Possibly implement this based on gui/ui-value.hpp instead and use templates
2283 // to keep track of the type. This should make it a bit easier to use and maintain
2284 // Also, implement this in a way that's agnostic to the UI renderer.
2285 for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
2286 ss.str("");
2287 ss.clear();
2288
2289 switch (it->type) {
2290 case UIVALUE_INT:
2291 ss << it->label << ": " << *(unsigned int*)it->value;
2292 break;
2293 case UIVALUE_DOUBLE:
2294 ss << it->label << ": " << *(double*)it->value;
2295 break;
2296 }
2297
2298 float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
2299
2300 ImGui::SetCursorPosX(cursorStartPos + maxWidth - textWidth);
2301 //ImGui::Text("%s", ss.str().c_str());
2302 ImGui::Text("%s: %.1f", it->label.c_str(), *(float*)it->value);
2303 }
2304}
2305
2306void VulkanGame::goToScreen(void (VulkanGame::* renderScreenFn)()) {
2307 currentRenderScreenFn = renderScreenFn;
2308
2309 // TODO: Maybe just set shouldRecreateSwapChain to true instead. Check this render loop logic
2310 // to make sure there'd be no issues
2311 //recreateSwapChain();
2312}
2313
2314void VulkanGame::quitGame() {
2315 done = true;
2316}
Note: See TracBrowser for help on using the repository browser.