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

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

Make a laser stop when it hits an asteroid

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