source: opengl-game/vulkan-game.cpp@ 6493e43

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

In sdl-game, add support for using separate graphics and present queues, and move the example ImGui code for (re)creating the swapchain into my own files

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