source: opengl-game/vulkan-game.cpp@ 7297892

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

Make lasers deal damage to asteroids and eventually destroy them

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