source: opengl-game/vulkan-game.cpp@ 4a9416a

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

Create a pipeline and shaders to render explosions

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