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

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

Create a system to draw and switch between different screens, a Screen class, a MainScreen class that extends it, and some classes for UI elements that can be added to screens.

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