source: opengl-game/vulkan-game.cpp@ 52a02e6

feature/imgui-sdl
Last change on this file since 52a02e6 was 52a02e6, checked in by Dmitry Portnoy <dmitry.portnoy@…>, 4 years ago

Add a primitive topology parameter to the GraphicsPipeline_Vulkan constructor

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