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

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

Make some minor updates to VulkanGame

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