source: opengl-game/vulkan-game.cpp@ 845a2cb

points-test
Last change on this file since 845a2cb was 845a2cb, checked in by Dmitry Portnoy <dmitry.portnoy@…>, 4 years ago

test point size

  • Property mode set to 100644
File size: 79.2 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 vec3(1.0f, 0.0f, 0.0f), 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 vec3(0.0f, 1.0f, 0.0f), 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 (size_t i = 0; i < shipObjects.size(); i++) {
953 if (shipObjects[i].modified) {
954 updateObject(shipObjects, shipPipeline, i);
955 }
956 }
957
958 for (size_t i = 0; i < modelObjects.size(); i++) {
959 if (modelObjects[i].modified) {
960 updateObject(modelObjects, modelPipeline, i);
961 }
962 }
963
964 for (size_t i = 0; i < asteroidObjects.size(); i++) {
965 if (asteroidObjects[i].modified) {
966 updateObject(asteroidObjects, asteroidPipeline, i);
967 }
968 }
969
970 for (size_t i = 0; i < laserObjects.size(); i++) {
971 if (laserObjects[i].modified) {
972 updateObject(laserObjects, laserPipeline, i);
973 }
974 }
975
976 for (size_t i = 0; i < explosionObjects.size(); i++) {
977 if (explosionObjects[i].modified) {
978 updateObject(explosionObjects, explosionPipeline, i);
979 }
980 }
981
982 explosion_UBO.cur_time = curTime;
983
984 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_modelPipeline[currentImage], 0, object_VP_mats);
985
986 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_shipPipeline[currentImage], 0, ship_VP_mats);
987
988 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_asteroidPipeline[currentImage], 0, asteroid_VP_mats);
989
990 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_laserPipeline[currentImage], 0, laser_VP_mats);
991
992 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_explosionPipeline[currentImage], 0, explosion_UBO);
993}
994
995void VulkanGame::renderUI() {
996 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
997 SDL_RenderClear(renderer);
998
999 SDL_Rect rect = {280, 220, 100, 100};
1000 SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF);
1001 SDL_RenderFillRect(renderer, &rect);
1002
1003 rect = {10, 10, 0, 0};
1004 SDL_QueryTexture(fontSDLTexture, nullptr, nullptr, &(rect.w), &(rect.h));
1005 SDL_RenderCopy(renderer, fontSDLTexture, nullptr, &rect);
1006
1007 rect = {10, 80, 0, 0};
1008 SDL_QueryTexture(imageSDLTexture, nullptr, nullptr, &(rect.w), &(rect.h));
1009 SDL_RenderCopy(renderer, imageSDLTexture, nullptr, &rect);
1010
1011 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0xFF, 0xFF);
1012 SDL_RenderDrawLine(renderer, 50, 5, 150, 500);
1013
1014 VulkanUtils::populateVulkanImageFromSDLTexture(device, physicalDevice, commandPool, uiOverlay, renderer,
1015 sdlOverlayImage, graphicsQueue);
1016}
1017
1018void VulkanGame::renderScene() {
1019 vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, numeric_limits<uint64_t>::max());
1020
1021 uint32_t imageIndex;
1022
1023 VkResult result = vkAcquireNextImageKHR(device, swapChain, numeric_limits<uint64_t>::max(),
1024 imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
1025
1026 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
1027 recreateSwapChain();
1028 return;
1029 } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
1030 throw runtime_error("failed to acquire swap chain image!");
1031 }
1032
1033 // TODO: Figure out a more elegant way to only do updates and render the UI once per scene render
1034 // Probably move some of the renderScene() code into a higher function that updates the UI, and renders
1035 // the UI and scene
1036 updateScene(imageIndex);
1037
1038 VkSubmitInfo submitInfo = {};
1039 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1040
1041 VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
1042 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
1043
1044 submitInfo.waitSemaphoreCount = 1;
1045 submitInfo.pWaitSemaphores = waitSemaphores;
1046 submitInfo.pWaitDstStageMask = waitStages;
1047 submitInfo.commandBufferCount = 1;
1048 submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
1049
1050 VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
1051
1052 submitInfo.signalSemaphoreCount = 1;
1053 submitInfo.pSignalSemaphores = signalSemaphores;
1054
1055 vkResetFences(device, 1, &inFlightFences[currentFrame]);
1056
1057 if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
1058 throw runtime_error("failed to submit draw command buffer!");
1059 }
1060
1061 VkPresentInfoKHR presentInfo = {};
1062 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1063 presentInfo.waitSemaphoreCount = 1;
1064 presentInfo.pWaitSemaphores = signalSemaphores;
1065
1066 VkSwapchainKHR swapChains[] = { swapChain };
1067 presentInfo.swapchainCount = 1;
1068 presentInfo.pSwapchains = swapChains;
1069 presentInfo.pImageIndices = &imageIndex;
1070 presentInfo.pResults = nullptr;
1071
1072 result = vkQueuePresentKHR(presentQueue, &presentInfo);
1073
1074 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
1075 framebufferResized = false;
1076 recreateSwapChain();
1077 } else if (result != VK_SUCCESS) {
1078 throw runtime_error("failed to present swap chain image!");
1079 }
1080
1081 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
1082 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
1083}
1084
1085void VulkanGame::cleanup() {
1086 cleanupSwapChain();
1087
1088 VulkanUtils::destroyVulkanImage(device, sdlOverlayImage);
1089 VulkanUtils::destroyVulkanImage(device, floorTextureImage);
1090 VulkanUtils::destroyVulkanImage(device, laserTextureImage);
1091
1092 vkDestroySampler(device, textureSampler, nullptr);
1093
1094 modelPipeline.cleanupBuffers();
1095 overlayPipeline.cleanupBuffers();
1096 shipPipeline.cleanupBuffers();
1097 asteroidPipeline.cleanupBuffers();
1098 laserPipeline.cleanupBuffers();
1099 explosionPipeline.cleanupBuffers();
1100
1101 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
1102 vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
1103 vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
1104 vkDestroyFence(device, inFlightFences[i], nullptr);
1105 }
1106
1107 vkDestroyCommandPool(device, commandPool, nullptr);
1108 vkDestroyDevice(device, nullptr);
1109 vkDestroySurfaceKHR(instance, surface, nullptr);
1110
1111 if (ENABLE_VALIDATION_LAYERS) {
1112 VulkanUtils::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
1113 }
1114
1115 vkDestroyInstance(instance, nullptr);
1116
1117 // TODO: Check if any of these functions accept null parameters
1118 // If they do, I don't need to check for that
1119
1120 if (uiOverlay != nullptr) {
1121 SDL_DestroyTexture(uiOverlay);
1122 uiOverlay = nullptr;
1123 }
1124
1125 if (fontSDLTexture != nullptr) {
1126 SDL_DestroyTexture(fontSDLTexture);
1127 fontSDLTexture = nullptr;
1128 }
1129
1130 if (imageSDLTexture != nullptr) {
1131 SDL_DestroyTexture(imageSDLTexture);
1132 imageSDLTexture = nullptr;
1133 }
1134
1135 TTF_CloseFont(font);
1136 font = nullptr;
1137
1138 SDL_DestroyRenderer(renderer);
1139 renderer = nullptr;
1140
1141 gui->destroyWindow();
1142 gui->shutdown();
1143 delete gui;
1144}
1145
1146void VulkanGame::createVulkanInstance(const vector<const char*> &validationLayers) {
1147 if (ENABLE_VALIDATION_LAYERS && !VulkanUtils::checkValidationLayerSupport(validationLayers)) {
1148 throw runtime_error("validation layers requested, but not available!");
1149 }
1150
1151 VkApplicationInfo appInfo = {};
1152 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
1153 appInfo.pApplicationName = "Vulkan Game";
1154 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
1155 appInfo.pEngineName = "No Engine";
1156 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
1157 appInfo.apiVersion = VK_API_VERSION_1_0;
1158
1159 VkInstanceCreateInfo createInfo = {};
1160 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
1161 createInfo.pApplicationInfo = &appInfo;
1162
1163 vector<const char*> extensions = gui->getRequiredExtensions();
1164 if (ENABLE_VALIDATION_LAYERS) {
1165 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
1166 }
1167
1168 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
1169 createInfo.ppEnabledExtensionNames = extensions.data();
1170
1171 cout << endl << "Extensions:" << endl;
1172 for (const char* extensionName : extensions) {
1173 cout << extensionName << endl;
1174 }
1175 cout << endl;
1176
1177 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
1178 if (ENABLE_VALIDATION_LAYERS) {
1179 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
1180 createInfo.ppEnabledLayerNames = validationLayers.data();
1181
1182 populateDebugMessengerCreateInfo(debugCreateInfo);
1183 createInfo.pNext = &debugCreateInfo;
1184 } else {
1185 createInfo.enabledLayerCount = 0;
1186
1187 createInfo.pNext = nullptr;
1188 }
1189
1190 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
1191 throw runtime_error("failed to create instance!");
1192 }
1193}
1194
1195void VulkanGame::setupDebugMessenger() {
1196 if (!ENABLE_VALIDATION_LAYERS) return;
1197
1198 VkDebugUtilsMessengerCreateInfoEXT createInfo;
1199 populateDebugMessengerCreateInfo(createInfo);
1200
1201 if (VulkanUtils::createDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
1202 throw runtime_error("failed to set up debug messenger!");
1203 }
1204}
1205
1206void VulkanGame::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
1207 createInfo = {};
1208 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
1209 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;
1210 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;
1211 createInfo.pfnUserCallback = debugCallback;
1212}
1213
1214VKAPI_ATTR VkBool32 VKAPI_CALL VulkanGame::debugCallback(
1215 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
1216 VkDebugUtilsMessageTypeFlagsEXT messageType,
1217 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
1218 void* pUserData) {
1219 cerr << "validation layer: " << pCallbackData->pMessage << endl;
1220
1221 return VK_FALSE;
1222}
1223
1224void VulkanGame::createVulkanSurface() {
1225 if (gui->createVulkanSurface(instance, &surface) == RTWO_ERROR) {
1226 throw runtime_error("failed to create window surface!");
1227 }
1228}
1229
1230void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
1231 uint32_t deviceCount = 0;
1232 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
1233
1234 if (deviceCount == 0) {
1235 throw runtime_error("failed to find GPUs with Vulkan support!");
1236 }
1237
1238 vector<VkPhysicalDevice> devices(deviceCount);
1239 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
1240
1241 cout << endl << "Graphics cards:" << endl;
1242 for (const VkPhysicalDevice& device : devices) {
1243 if (isDeviceSuitable(device, deviceExtensions)) {
1244 physicalDevice = device;
1245 break;
1246 }
1247 }
1248 cout << endl;
1249
1250 if (physicalDevice == VK_NULL_HANDLE) {
1251 throw runtime_error("failed to find a suitable GPU!");
1252 }
1253}
1254
1255bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice,
1256 const vector<const char*>& deviceExtensions) {
1257 VkPhysicalDeviceProperties deviceProperties;
1258 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
1259
1260 cout << "Device: " << deviceProperties.deviceName << endl;
1261
1262 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1263 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
1264 bool swapChainAdequate = false;
1265
1266 if (extensionsSupported) {
1267 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
1268 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
1269 }
1270
1271 VkPhysicalDeviceFeatures supportedFeatures;
1272 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
1273
1274 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
1275}
1276
1277void VulkanGame::createLogicalDevice(
1278 const vector<const char*> validationLayers, const vector<const char*>& deviceExtensions) {
1279 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1280
1281 vector<VkDeviceQueueCreateInfo> queueCreateInfoList;
1282 set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1283
1284 float queuePriority = 1.0f;
1285 for (uint32_t queueFamily : uniqueQueueFamilies) {
1286 VkDeviceQueueCreateInfo queueCreateInfo = {};
1287 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1288 queueCreateInfo.queueFamilyIndex = queueFamily;
1289 queueCreateInfo.queueCount = 1;
1290 queueCreateInfo.pQueuePriorities = &queuePriority;
1291
1292 queueCreateInfoList.push_back(queueCreateInfo);
1293 }
1294
1295 VkPhysicalDeviceFeatures deviceFeatures = {};
1296 deviceFeatures.samplerAnisotropy = VK_TRUE;
1297
1298 VkDeviceCreateInfo createInfo = {};
1299 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
1300 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfoList.size());
1301 createInfo.pQueueCreateInfos = queueCreateInfoList.data();
1302
1303 createInfo.pEnabledFeatures = &deviceFeatures;
1304
1305 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
1306 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
1307
1308 // These fields are ignored by up-to-date Vulkan implementations,
1309 // but it's a good idea to set them for backwards compatibility
1310 if (ENABLE_VALIDATION_LAYERS) {
1311 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
1312 createInfo.ppEnabledLayerNames = validationLayers.data();
1313 } else {
1314 createInfo.enabledLayerCount = 0;
1315 }
1316
1317 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
1318 throw runtime_error("failed to create logical device!");
1319 }
1320
1321 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
1322 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
1323}
1324
1325void VulkanGame::createSwapChain() {
1326 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
1327
1328 VkSurfaceFormatKHR surfaceFormat = VulkanUtils::chooseSwapSurfaceFormat(swapChainSupport.formats);
1329 VkPresentModeKHR presentMode = VulkanUtils::chooseSwapPresentMode(swapChainSupport.presentModes);
1330 VkExtent2D extent = VulkanUtils::chooseSwapExtent(swapChainSupport.capabilities, gui->getWindowWidth(), gui->getWindowHeight());
1331
1332 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
1333 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
1334 imageCount = swapChainSupport.capabilities.maxImageCount;
1335 }
1336
1337 VkSwapchainCreateInfoKHR createInfo = {};
1338 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1339 createInfo.surface = surface;
1340 createInfo.minImageCount = imageCount;
1341 createInfo.imageFormat = surfaceFormat.format;
1342 createInfo.imageColorSpace = surfaceFormat.colorSpace;
1343 createInfo.imageExtent = extent;
1344 createInfo.imageArrayLayers = 1;
1345 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1346
1347 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1348 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1349
1350 if (indices.graphicsFamily != indices.presentFamily) {
1351 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
1352 createInfo.queueFamilyIndexCount = 2;
1353 createInfo.pQueueFamilyIndices = queueFamilyIndices;
1354 } else {
1355 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1356 createInfo.queueFamilyIndexCount = 0;
1357 createInfo.pQueueFamilyIndices = nullptr;
1358 }
1359
1360 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
1361 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1362 createInfo.presentMode = presentMode;
1363 createInfo.clipped = VK_TRUE;
1364 createInfo.oldSwapchain = VK_NULL_HANDLE;
1365
1366 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
1367 throw runtime_error("failed to create swap chain!");
1368 }
1369
1370 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
1371 swapChainImages.resize(imageCount);
1372 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
1373
1374 swapChainImageFormat = surfaceFormat.format;
1375 swapChainExtent = extent;
1376}
1377
1378void VulkanGame::createImageViews() {
1379 swapChainImageViews.resize(swapChainImages.size());
1380
1381 for (size_t i = 0; i < swapChainImages.size(); i++) {
1382 swapChainImageViews[i] = VulkanUtils::createImageView(device, swapChainImages[i], swapChainImageFormat,
1383 VK_IMAGE_ASPECT_COLOR_BIT);
1384 }
1385}
1386
1387void VulkanGame::createRenderPass() {
1388 VkAttachmentDescription colorAttachment = {};
1389 colorAttachment.format = swapChainImageFormat;
1390 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
1391 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1392 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1393 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1394 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1395 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1396 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1397
1398 VkAttachmentReference colorAttachmentRef = {};
1399 colorAttachmentRef.attachment = 0;
1400 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1401
1402 VkAttachmentDescription depthAttachment = {};
1403 depthAttachment.format = findDepthFormat();
1404 depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
1405 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1406 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1407 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1408 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1409 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1410 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1411
1412 VkAttachmentReference depthAttachmentRef = {};
1413 depthAttachmentRef.attachment = 1;
1414 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1415
1416 VkSubpassDescription subpass = {};
1417 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1418 subpass.colorAttachmentCount = 1;
1419 subpass.pColorAttachments = &colorAttachmentRef;
1420 subpass.pDepthStencilAttachment = &depthAttachmentRef;
1421
1422 VkSubpassDependency dependency = {};
1423 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
1424 dependency.dstSubpass = 0;
1425 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1426 dependency.srcAccessMask = 0;
1427 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1428 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1429
1430 array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
1431 VkRenderPassCreateInfo renderPassInfo = {};
1432 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1433 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1434 renderPassInfo.pAttachments = attachments.data();
1435 renderPassInfo.subpassCount = 1;
1436 renderPassInfo.pSubpasses = &subpass;
1437 renderPassInfo.dependencyCount = 1;
1438 renderPassInfo.pDependencies = &dependency;
1439
1440 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
1441 throw runtime_error("failed to create render pass!");
1442 }
1443}
1444
1445VkFormat VulkanGame::findDepthFormat() {
1446 return VulkanUtils::findSupportedFormat(
1447 physicalDevice,
1448 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
1449 VK_IMAGE_TILING_OPTIMAL,
1450 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
1451 );
1452}
1453
1454void VulkanGame::createCommandPool() {
1455 QueueFamilyIndices queueFamilyIndices = VulkanUtils::findQueueFamilies(physicalDevice, surface);;
1456
1457 VkCommandPoolCreateInfo poolInfo = {};
1458 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1459 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
1460 poolInfo.flags = 0;
1461
1462 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
1463 throw runtime_error("failed to create graphics command pool!");
1464 }
1465}
1466
1467void VulkanGame::createImageResources() {
1468 VulkanUtils::createDepthImage(device, physicalDevice, commandPool, findDepthFormat(), swapChainExtent,
1469 depthImage, graphicsQueue);
1470
1471 createTextureSampler();
1472
1473 // TODO: Move all images/textures somewhere into the assets folder
1474
1475 VulkanUtils::createVulkanImageFromSDLTexture(device, physicalDevice, uiOverlay, sdlOverlayImage);
1476
1477 sdlOverlayImageDescriptor = {};
1478 sdlOverlayImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1479 sdlOverlayImageDescriptor.imageView = sdlOverlayImage.imageView;
1480 sdlOverlayImageDescriptor.sampler = textureSampler;
1481
1482 VulkanUtils::createVulkanImageFromFile(device, physicalDevice, commandPool, "textures/texture.jpg",
1483 floorTextureImage, graphicsQueue);
1484
1485 floorTextureImageDescriptor = {};
1486 floorTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1487 floorTextureImageDescriptor.imageView = floorTextureImage.imageView;
1488 floorTextureImageDescriptor.sampler = textureSampler;
1489
1490 VulkanUtils::createVulkanImageFromFile(device, physicalDevice, commandPool, "textures/laser.png",
1491 laserTextureImage, graphicsQueue);
1492
1493 laserTextureImageDescriptor = {};
1494 laserTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1495 laserTextureImageDescriptor.imageView = laserTextureImage.imageView;
1496 laserTextureImageDescriptor.sampler = textureSampler;
1497}
1498
1499void VulkanGame::createTextureSampler() {
1500 VkSamplerCreateInfo samplerInfo = {};
1501 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
1502 samplerInfo.magFilter = VK_FILTER_LINEAR;
1503 samplerInfo.minFilter = VK_FILTER_LINEAR;
1504
1505 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1506 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1507 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1508
1509 samplerInfo.anisotropyEnable = VK_TRUE;
1510 samplerInfo.maxAnisotropy = 16;
1511 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
1512 samplerInfo.unnormalizedCoordinates = VK_FALSE;
1513 samplerInfo.compareEnable = VK_FALSE;
1514 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
1515 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
1516 samplerInfo.mipLodBias = 0.0f;
1517 samplerInfo.minLod = 0.0f;
1518 samplerInfo.maxLod = 0.0f;
1519
1520 if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
1521 throw runtime_error("failed to create texture sampler!");
1522 }
1523}
1524
1525void VulkanGame::createFramebuffers() {
1526 swapChainFramebuffers.resize(swapChainImageViews.size());
1527
1528 for (size_t i = 0; i < swapChainImageViews.size(); i++) {
1529 array<VkImageView, 2> attachments = {
1530 swapChainImageViews[i],
1531 depthImage.imageView
1532 };
1533
1534 VkFramebufferCreateInfo framebufferInfo = {};
1535 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1536 framebufferInfo.renderPass = renderPass;
1537 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1538 framebufferInfo.pAttachments = attachments.data();
1539 framebufferInfo.width = swapChainExtent.width;
1540 framebufferInfo.height = swapChainExtent.height;
1541 framebufferInfo.layers = 1;
1542
1543 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
1544 throw runtime_error("failed to create framebuffer!");
1545 }
1546 }
1547}
1548
1549void VulkanGame::createCommandBuffers() {
1550 commandBuffers.resize(swapChainImages.size());
1551
1552 VkCommandBufferAllocateInfo allocInfo = {};
1553 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1554 allocInfo.commandPool = commandPool;
1555 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1556 allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
1557
1558 if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
1559 throw runtime_error("failed to allocate command buffers!");
1560 }
1561
1562 for (size_t i = 0; i < commandBuffers.size(); i++) {
1563 VkCommandBufferBeginInfo beginInfo = {};
1564 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1565 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
1566 beginInfo.pInheritanceInfo = nullptr;
1567
1568 if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
1569 throw runtime_error("failed to begin recording command buffer!");
1570 }
1571
1572 VkRenderPassBeginInfo renderPassInfo = {};
1573 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1574 renderPassInfo.renderPass = renderPass;
1575 renderPassInfo.framebuffer = swapChainFramebuffers[i];
1576 renderPassInfo.renderArea.offset = { 0, 0 };
1577 renderPassInfo.renderArea.extent = swapChainExtent;
1578
1579 array<VkClearValue, 2> clearValues = {};
1580 clearValues[0].color = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
1581 clearValues[1].depthStencil = { 1.0f, 0 };
1582
1583 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
1584 renderPassInfo.pClearValues = clearValues.data();
1585
1586 vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1587
1588 modelPipeline.createRenderCommands(commandBuffers[i], i);
1589 shipPipeline.createRenderCommands(commandBuffers[i], i);
1590 asteroidPipeline.createRenderCommands(commandBuffers[i], i);
1591 laserPipeline.createRenderCommands(commandBuffers[i], i);
1592 explosionPipeline.createRenderCommands(commandBuffers[i], i);
1593
1594 // Always render this pipeline last
1595 overlayPipeline.createRenderCommands(commandBuffers[i], i);
1596
1597 vkCmdEndRenderPass(commandBuffers[i]);
1598
1599 if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
1600 throw runtime_error("failed to record command buffer!");
1601 }
1602 }
1603}
1604
1605void VulkanGame::createSyncObjects() {
1606 imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1607 renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1608 inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
1609
1610 VkSemaphoreCreateInfo semaphoreInfo = {};
1611 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1612
1613 VkFenceCreateInfo fenceInfo = {};
1614 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1615 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1616
1617 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
1618 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
1619 vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
1620 vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
1621 throw runtime_error("failed to create synchronization objects for a frame!");
1622 }
1623 }
1624}
1625
1626void VulkanGame::addLaser(vec3 start, vec3 end, vec3 color, float width) {
1627 vec3 ray = end - start;
1628 float length = glm::length(ray);
1629
1630 SceneObject<LaserVertex, SSBO_Laser>& laser = addObject(
1631 laserObjects, laserPipeline,
1632 addObjectIndex<LaserVertex>(laserObjects.size(), {
1633 {{ width / 2, 0.0f, -width / 2 }, {1.0f, 0.5f }},
1634 {{-width / 2, 0.0f, -width / 2 }, {0.0f, 0.5f }},
1635 {{-width / 2, 0.0f, 0.0f }, {0.0f, 0.0f }},
1636 {{ width / 2, 0.0f, 0.0f }, {1.0f, 0.0f }},
1637 {{ width / 2, 0.0f, -length + width / 2}, {1.0f, 0.51f}},
1638 {{-width / 2, 0.0f, -length + width / 2}, {0.0f, 0.51f}},
1639 {{ width / 2, 0.0f, -length, }, {1.0f, 1.0f }},
1640 {{-width / 2, 0.0f, -length }, {0.0f, 1.0f }}
1641 }), {
1642 0, 1, 2, 0, 2, 3,
1643 4, 5, 1, 4, 1, 0,
1644 6, 7, 5, 6, 5, 4
1645 }, {
1646 mat4(1.0f),
1647 color,
1648 false
1649 }, true);
1650
1651 float xAxisRotation = asin(ray.y / length);
1652 float yAxisRotation = atan2(-ray.x, -ray.z);
1653
1654 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
1655 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
1656 vec4(0.0f, 1.0f, 0.0f, 1.0f));
1657
1658 // To project point P onto line AB:
1659 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
1660 vec3 projOnLaser = start + glm::dot(this->cam_pos - start, ray) / (length * length) * ray;
1661 vec3 laserToCam = this->cam_pos - projOnLaser;
1662
1663 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
1664
1665 laser.targetAsteroid = nullptr;
1666
1667 laser.model_base =
1668 rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
1669
1670 laser.model_transform =
1671 translate(mat4(1.0f), start) *
1672 rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
1673 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f));
1674
1675 laser.modified = true;
1676}
1677
1678void VulkanGame::translateLaser(size_t index, const vec3& translation) {
1679 SceneObject<LaserVertex, SSBO_Laser>& laser = this->laserObjects[index];
1680
1681 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
1682 // and then re-used here
1683
1684 vec3 start = vec3(laser.model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
1685 vec3 end = vec3(laser.model_transform * vec4(0.0f, 0.0f, laser.vertices[6].pos.z, 1.0f));
1686
1687 vec3 ray = end - start;
1688 float length = glm::length(ray);
1689
1690 float xAxisRotation = asin(ray.y / length);
1691 float yAxisRotation = atan2(-ray.x, -ray.z);
1692
1693 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
1694 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
1695 vec4(0.0f, 1.0f, 0.0f, 1.0f));
1696
1697 // To project point P onto line AB:
1698 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
1699 vec3 projOnLaser = start + glm::dot(cam_pos - start, ray) / (length*length) * ray;
1700 vec3 laserToCam = cam_pos - projOnLaser;
1701
1702 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
1703
1704 laser.model_base = rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
1705 laser.model_transform = translate(mat4(1.0f), translation) * laser.model_transform;
1706
1707 laser.modified = true;
1708}
1709
1710void VulkanGame::updateLaserTarget(size_t index) {
1711 SceneObject<LaserVertex, SSBO_Laser>& laser = this->laserObjects[index];
1712
1713 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
1714 // and then re-used here
1715
1716 vec3 start = vec3(laser.model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
1717 vec3 end = vec3(laser.model_transform * vec4(0.0f, 0.0f, laser.vertices[6].pos.z, 1.0f));
1718
1719 vec3 intersection(0.0f), closestIntersection(0.0f);
1720 SceneObject<AsteroidVertex, SSBO_Asteroid>* closestAsteroid = nullptr;
1721 unsigned int closestAsteroidIndex = -1;
1722
1723 for (int i = 0; i < this->asteroidObjects.size(); i++) {
1724 if (!this->asteroidObjects[i].ssbo.deleted &&
1725 this->getLaserAndAsteroidIntersection(this->asteroidObjects[i], start, end, intersection)) {
1726 // TODO: Implement a more generic algorithm for testing the closest object by getting the distance between the points
1727 // TODO: Also check which intersection is close to the start of the laser. This would make the algorithm work
1728 // regardless of which way -Z is pointing
1729 if (closestAsteroid == nullptr || intersection.z > closestIntersection.z) {
1730 // TODO: At this point, find the real intersection of the laser with one of the asteroid's sides
1731 closestAsteroid = &asteroidObjects[i];
1732 closestIntersection = intersection;
1733 closestAsteroidIndex = i;
1734 }
1735 }
1736 }
1737
1738 float width = laser.vertices[0].pos.x - laser.vertices[1].pos.x;
1739
1740 if (laser.targetAsteroid != closestAsteroid) {
1741 if (laser.targetAsteroid != nullptr) {
1742 if (index == leftLaserIdx && leftLaserEffect != nullptr) {
1743 leftLaserEffect->deleted = true;
1744 } else if (index == rightLaserIdx && rightLaserEffect != nullptr) {
1745 rightLaserEffect->deleted = true;
1746 }
1747 }
1748
1749 EffectOverTime<AsteroidVertex, SSBO_Asteroid>* eot = nullptr;
1750
1751 if (closestAsteroid != nullptr) {
1752 // TODO: Use some sort of smart pointer instead
1753 eot = new EffectOverTime(asteroidPipeline, asteroidObjects, closestAsteroidIndex,
1754 offset_of(&SSBO_Asteroid::hp), -20.0f);
1755 effects.push_back(eot);
1756 }
1757
1758 if (index == leftLaserIdx) {
1759 leftLaserEffect = eot;
1760 } else if (index == rightLaserIdx) {
1761 rightLaserEffect = eot;
1762 }
1763
1764 laser.targetAsteroid = closestAsteroid;
1765 }
1766
1767 // Make the laser go past the end of the screen if it doesn't hit anything
1768 float length = closestAsteroid == nullptr ? 5.24f : glm::length(closestIntersection - start);
1769
1770 laser.vertices[4].pos.z = -length + width / 2;
1771 laser.vertices[5].pos.z = -length + width / 2;
1772 laser.vertices[6].pos.z = -length;
1773 laser.vertices[7].pos.z = -length;
1774
1775 // TODO: Consider if I want to set a flag and do this update in in updateScene() instead
1776 updateObjectVertices(this->laserPipeline, laser, index);
1777}
1778
1779// TODO: Determine if I should pass start and end by reference or value since they don't get changed
1780// Probably use const reference
1781bool VulkanGame::getLaserAndAsteroidIntersection(SceneObject<AsteroidVertex, SSBO_Asteroid>& asteroid,
1782 vec3& start, vec3& end, vec3& intersection) {
1783 /*
1784 ### LINE EQUATIONS ###
1785 x = x1 + u * (x2 - x1)
1786 y = y1 + u * (y2 - y1)
1787 z = z1 + u * (z2 - z1)
1788
1789 ### SPHERE EQUATION ###
1790 (x - x3)^2 + (y - y3)^2 + (z - z3)^2 = r^2
1791
1792 ### QUADRATIC EQUATION TO SOLVE ###
1793 a*u^2 + b*u + c = 0
1794 WHERE THE CONSTANTS ARE
1795 a = (x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2
1796 b = 2*( (x2 - x1)*(x1 - x3) + (y2 - y1)*(y1 - y3) + (z2 - z1)*(z1 - z3) )
1797 c = x3^2 + y3^2 + z3^2 + x1^2 + y1^2 + z1^2 - 2(x3*x1 + y3*y1 + z3*z1) - r^2
1798
1799 u = (-b +- sqrt(b^2 - 4*a*c)) / 2a
1800
1801 If the value under the root is >= 0, we got an intersection
1802 If the value > 0, there are two solutions. Take the one closer to 0, since that's the
1803 one closer to the laser start point
1804 */
1805
1806 vec3& center = asteroid.center;
1807
1808 float a = pow(end.x - start.x, 2) + pow(end.y - start.y, 2) + pow(end.z - start.z, 2);
1809 float b = 2 * ((start.x - end.x) * (start.x - center.x) + (end.y - start.y) * (start.y - center.y) +
1810 (end.z - start.z) * (start.z - center.z));
1811 float c = pow(center.x, 2) + pow(center.y, 2) + pow(center.z, 2) + pow(start.x, 2) + pow(start.y, 2) +
1812 pow(start.z, 2) - 2 * (center.x * start.x + center.y * start.y + center.z * start.z) -
1813 pow(asteroid.radius, 2);
1814 float discriminant = pow(b, 2) - 4 * a * c;
1815
1816 if (discriminant >= 0.0f) {
1817 // In this case, the negative root will always give the point closer to the laser start point
1818 float u = (-b - sqrt(discriminant)) / (2 * a);
1819
1820 // Check that the intersection is within the line segment corresponding to the laser
1821 if (0.0f <= u && u <= 1.0f) {
1822 intersection = start + u * (end - start);
1823 return true;
1824 }
1825 }
1826
1827 return false;
1828}
1829
1830void VulkanGame::createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags flags,
1831 vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory, vector<VkDescriptorBufferInfo>& bufferInfoList) {
1832 buffers.resize(swapChainImages.size());
1833 buffersMemory.resize(swapChainImages.size());
1834 bufferInfoList.resize(swapChainImages.size());
1835
1836 for (size_t i = 0; i < swapChainImages.size(); i++) {
1837 VulkanUtils::createBuffer(device, physicalDevice, bufferSize, flags,
1838 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1839 buffers[i], buffersMemory[i]);
1840
1841 bufferInfoList[i].buffer = buffers[i];
1842 bufferInfoList[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
1843 bufferInfoList[i].range = bufferSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
1844 }
1845}
1846
1847void VulkanGame::addExplosion(mat4 model_mat, float duration, float cur_time) {
1848 cout << "Adding explosion..." << endl;
1849
1850 vector<ExplosionVertex> vertices;
1851 vertices.reserve(EXPLOSION_PARTICLE_COUNT);
1852
1853 float particlestart_time = 0.0f;
1854
1855 for (int i = 0; i < EXPLOSION_PARTICLE_COUNT; i++) {
1856 float randx = ((float)rand() / (float)RAND_MAX) - 0.5f;
1857 float randy = ((float)rand() / (float)RAND_MAX) - 0.5f;
1858
1859 vertices.push_back({ vec3(randx, randy, 0.0f), particlestart_time});
1860
1861 particlestart_time += 0.01f;
1862 }
1863
1864 // Fill the indices with the the first EXPLOSION_PARTICLE_COUNT ints
1865 vector<uint16_t> indices(EXPLOSION_PARTICLE_COUNT);
1866 iota(indices.begin(), indices.end(), 0);
1867
1868 SceneObject<ExplosionVertex, SSBO_Explosion>& explosion = addObject(
1869 explosionObjects, explosionPipeline,
1870 addObjectIndex(explosionObjects.size(), vertices),
1871 indices, {
1872 mat4(1.0f),
1873 cur_time,
1874 duration,
1875 false
1876 }, true);
1877
1878 explosion.model_base = model_mat;
1879 explosion.model_transform = mat4(1.0f);
1880
1881 explosion.modified = true;
1882}
1883
1884// TODO: Fix the crash that happens when alt-tabbing
1885void VulkanGame::recreateSwapChain() {
1886 cout << "Recreating swap chain" << endl;
1887 gui->refreshWindowSize();
1888
1889 while (gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0 ||
1890 (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0) {
1891 SDL_WaitEvent(nullptr);
1892 gui->refreshWindowSize();
1893 }
1894
1895 vkDeviceWaitIdle(device);
1896
1897 cleanupSwapChain();
1898
1899 createSwapChain();
1900 createImageViews();
1901 createRenderPass();
1902
1903 VulkanUtils::createDepthImage(device, physicalDevice, commandPool, findDepthFormat(), swapChainExtent,
1904 depthImage, graphicsQueue);
1905 createFramebuffers();
1906
1907 // TODO: Move UBO creation/management into GraphicsPipeline_Vulkan, like I did with SSBOs
1908
1909 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1910 uniformBuffers_modelPipeline, uniformBuffersMemory_modelPipeline, uniformBufferInfoList_modelPipeline);
1911
1912 modelPipeline.updateRenderPass(renderPass);
1913 modelPipeline.createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
1914 modelPipeline.createDescriptorPool(swapChainImages);
1915 modelPipeline.createDescriptorSets(swapChainImages);
1916
1917 overlayPipeline.updateRenderPass(renderPass);
1918 overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
1919 overlayPipeline.createDescriptorPool(swapChainImages);
1920 overlayPipeline.createDescriptorSets(swapChainImages);
1921
1922 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1923 uniformBuffers_shipPipeline, uniformBuffersMemory_shipPipeline, uniformBufferInfoList_shipPipeline);
1924
1925 shipPipeline.updateRenderPass(renderPass);
1926 shipPipeline.createPipeline("shaders/ship-vert.spv", "shaders/ship-frag.spv");
1927 shipPipeline.createDescriptorPool(swapChainImages);
1928 shipPipeline.createDescriptorSets(swapChainImages);
1929
1930 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1931 uniformBuffers_asteroidPipeline, uniformBuffersMemory_asteroidPipeline, uniformBufferInfoList_asteroidPipeline);
1932
1933 asteroidPipeline.updateRenderPass(renderPass);
1934 asteroidPipeline.createPipeline("shaders/asteroid-vert.spv", "shaders/asteroid-frag.spv");
1935 asteroidPipeline.createDescriptorPool(swapChainImages);
1936 asteroidPipeline.createDescriptorSets(swapChainImages);
1937
1938 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1939 uniformBuffers_laserPipeline, uniformBuffersMemory_laserPipeline, uniformBufferInfoList_laserPipeline);
1940
1941 laserPipeline.updateRenderPass(renderPass);
1942 laserPipeline.createPipeline("shaders/laser-vert.spv", "shaders/laser-frag.spv");
1943 laserPipeline.createDescriptorPool(swapChainImages);
1944 laserPipeline.createDescriptorSets(swapChainImages);
1945
1946 createBufferSet(sizeof(UBO_Explosion), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1947 uniformBuffers_explosionPipeline, uniformBuffersMemory_explosionPipeline, uniformBufferInfoList_explosionPipeline);
1948
1949 explosionPipeline.updateRenderPass(renderPass);
1950 explosionPipeline.createPipeline("shaders/explosion-vert.spv", "shaders/explosion-frag.spv");
1951 explosionPipeline.createDescriptorPool(swapChainImages);
1952 explosionPipeline.createDescriptorSets(swapChainImages);
1953
1954 createCommandBuffers();
1955}
1956
1957void VulkanGame::cleanupSwapChain() {
1958 VulkanUtils::destroyVulkanImage(device, depthImage);
1959
1960 for (VkFramebuffer framebuffer : swapChainFramebuffers) {
1961 vkDestroyFramebuffer(device, framebuffer, nullptr);
1962 }
1963
1964 vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
1965
1966 overlayPipeline.cleanup();
1967 modelPipeline.cleanup();
1968 shipPipeline.cleanup();
1969 asteroidPipeline.cleanup();
1970 laserPipeline.cleanup();
1971 explosionPipeline.cleanup();
1972
1973 for (size_t i = 0; i < uniformBuffers_modelPipeline.size(); i++) {
1974 vkDestroyBuffer(device, uniformBuffers_modelPipeline[i], nullptr);
1975 vkFreeMemory(device, uniformBuffersMemory_modelPipeline[i], nullptr);
1976 }
1977
1978 for (size_t i = 0; i < uniformBuffers_shipPipeline.size(); i++) {
1979 vkDestroyBuffer(device, uniformBuffers_shipPipeline[i], nullptr);
1980 vkFreeMemory(device, uniformBuffersMemory_shipPipeline[i], nullptr);
1981 }
1982
1983 for (size_t i = 0; i < uniformBuffers_asteroidPipeline.size(); i++) {
1984 vkDestroyBuffer(device, uniformBuffers_asteroidPipeline[i], nullptr);
1985 vkFreeMemory(device, uniformBuffersMemory_asteroidPipeline[i], nullptr);
1986 }
1987
1988 for (size_t i = 0; i < uniformBuffers_laserPipeline.size(); i++) {
1989 vkDestroyBuffer(device, uniformBuffers_laserPipeline[i], nullptr);
1990 vkFreeMemory(device, uniformBuffersMemory_laserPipeline[i], nullptr);
1991 }
1992
1993 for (size_t i = 0; i < uniformBuffers_explosionPipeline.size(); i++) {
1994 vkDestroyBuffer(device, uniformBuffers_explosionPipeline[i], nullptr);
1995 vkFreeMemory(device, uniformBuffersMemory_explosionPipeline[i], nullptr);
1996 }
1997
1998 vkDestroyRenderPass(device, renderPass, nullptr);
1999
2000 for (VkImageView imageView : swapChainImageViews) {
2001 vkDestroyImageView(device, imageView, nullptr);
2002 }
2003
2004 vkDestroySwapchainKHR(device, swapChain, nullptr);
2005}
Note: See TracBrowser for help on using the repository browser.