source: opengl-game/vulkan-game.cpp@ 85b5fec

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

Use the new UI system in SDLGame as well

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