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

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

Rename all BufferSets named storageBuffers_* to objectBuffers_*

  • Property mode set to 100644
File size: 108.5 KB
RevLine 
[99d44b2]1#include "vulkan-game.hpp"
[850e84c]2
[6fc24c7]3#include <array>
[0df3c9a]4#include <iostream>
[4a9416a]5#include <numeric>
[c1c2021]6#include <set>
[c324d6a]7#include <stdexcept>
[0df3c9a]8
[6a39266]9#include "IMGUI/imgui_impl_sdl.h"
[301c90a]10#include "IMGUI/imgui_internal.h" // For CalcItemSize
[6a39266]11
[c559904]12#include "logger.hpp"
[c1d9b2a]13
[301c90a]14#include "gui/imgui/button-imgui.hpp"
15
[0df3c9a]16using namespace std;
[b794178]17
[3f32dfd]18// TODO: Update all instances of the "... != VK_SUCCESS" check to something similar to
19// the agp error checking function, which prints an appropriate error message based on the error code
20
[c324d6a]21// TODO: Update all occurances of instance variables to use this-> (Actually, not sure if I really want to do this)
22
23/* TODO: Try doing the following tasks based on the Vulkan implementation of IMGUI (Also maybe looks at Sascha Willems' code to see how he does these things)
24 *
25 * - When recreating the swapchain, pass the old one in and destroy the old one after the new one is created
26 * - Recreate semaphores when recreating the swapchain
27 * - imgui uses one image acquired and one render complete sem and once fence per frame\
28 * - IMGUI creates one command pool per framebuffer
29 */
[87c8f1a]30
[3b7d497]31/* NOTES WHEN ADDING IMGUI
32 *
33 * Possibly cleanup the imgui pipeline in cleanupSwapchain or call some imgui function that does this for me
34 * call ImGui_ImplVulkan_RenderDrawData, without passing in a pipeline, to do the rendering
35 */
36
[8b823e7]37static void check_imgui_vk_result(VkResult res) {
38 if (res == VK_SUCCESS) {
[3b7d497]39 return;
[8b823e7]40 }
41
42 ostringstream oss;
43 oss << "[imgui] Vulkan error! VkResult is \"" << VulkanUtils::resultString(res) << "\"" << __LINE__;
44 if (res < 0) {
45 throw runtime_error("Fatal: " + oss.str());
46 } else {
47 cerr << oss.str();
48 }
[3b7d497]49}
50
[7865c5b]51VulkanGame::VulkanGame()
52 : swapChainImageCount(0)
[cefdf23]53 , swapChainMinImageCount(0)
[7865c5b]54 , swapChainSurfaceFormat({})
55 , swapChainPresentMode(VK_PRESENT_MODE_MAX_ENUM_KHR)
56 , swapChainExtent{ 0, 0 }
57 , swapChain(VK_NULL_HANDLE)
58 , vulkanSurface(VK_NULL_HANDLE)
59 , sdlVersion({ 0, 0, 0 })
60 , instance(VK_NULL_HANDLE)
61 , physicalDevice(VK_NULL_HANDLE)
62 , device(VK_NULL_HANDLE)
63 , debugMessenger(VK_NULL_HANDLE)
64 , resourceCommandPool(VK_NULL_HANDLE)
65 , renderPass(VK_NULL_HANDLE)
66 , graphicsQueue(VK_NULL_HANDLE)
67 , presentQueue(VK_NULL_HANDLE)
68 , depthImage({})
69 , shouldRecreateSwapChain(false)
70 , frameCount(0)
[cefdf23]71 , currentFrame(0)
[7865c5b]72 , imageIndex(0)
73 , fpsStartTime(0.0f)
74 , curTime(0.0f)
75 , done(false)
76 , currentRenderScreenFn(nullptr)
77 , gui(nullptr)
78 , window(nullptr)
[2f4ff8c]79 , uniforms_modelPipeline()
[a3cefaa]80 , objects_modelPipeline()
[2f4ff8c]81 , uniforms_shipPipeline()
[a3cefaa]82 , objects_shipPipeline()
[2f4ff8c]83 , uniforms_asteroidPipeline()
[a3cefaa]84 , objects_asteroidPipeline()
[2f4ff8c]85 , uniforms_laserPipeline()
[a3cefaa]86 , objects_laserPipeline()
[90880fb]87 , uniforms_explosionPipeline()
[a3cefaa]88 , objects_explosionPipeline()
[7865c5b]89 , score(0)
90 , fps(0.0f) {
[0df3c9a]91}
92
[99d44b2]93VulkanGame::~VulkanGame() {
[0df3c9a]94}
95
[b6e60b4]96void VulkanGame::run(int width, int height, unsigned char guiFlags) {
[0807aeb]97 seedRandomNums();
98
[2e77b3f]99 cout << "Vulkan Game" << endl;
100
[e8445f0]101 cout << "DEBUGGING IS " << (ENABLE_VALIDATION_LAYERS ? "ON" : "OFF") << endl;
102
[4e705d6]103 if (initUI(width, height, guiFlags) == RTWO_ERROR) {
104 return;
105 }
106
[20e4c2b]107 initVulkan();
[e1f88a9]108
[a3cefaa]109 VkPhysicalDeviceProperties deviceProperties;
110 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
111
[2f4ff8c]112 uniforms_modelPipeline = VulkanBuffer<UBO_VP_mats>(1, deviceProperties.limits.maxUniformBufferRange,
113 deviceProperties.limits.minUniformBufferOffsetAlignment);
[90880fb]114
[b7fc3c2]115 objects_modelPipeline = VulkanBuffer<SSBO_ModelObject>(10, deviceProperties.limits.maxStorageBufferRange,
116 deviceProperties.limits.minStorageBufferOffsetAlignment);
[90880fb]117
[2f4ff8c]118 uniforms_shipPipeline = VulkanBuffer<UBO_VP_mats>(1, deviceProperties.limits.maxUniformBufferRange,
119 deviceProperties.limits.minUniformBufferOffsetAlignment);
[90880fb]120
[b7fc3c2]121 objects_shipPipeline = VulkanBuffer<SSBO_ModelObject>(10, deviceProperties.limits.maxStorageBufferRange,
122 deviceProperties.limits.minStorageBufferOffsetAlignment);
[90880fb]123
[2f4ff8c]124 uniforms_asteroidPipeline = VulkanBuffer<UBO_VP_mats>(1, deviceProperties.limits.maxUniformBufferRange,
125 deviceProperties.limits.minUniformBufferOffsetAlignment);
[90880fb]126
[b7fc3c2]127 objects_asteroidPipeline = VulkanBuffer<SSBO_Asteroid>(10, deviceProperties.limits.maxStorageBufferRange,
128 deviceProperties.limits.minStorageBufferOffsetAlignment);
[90880fb]129
[2f4ff8c]130 uniforms_laserPipeline = VulkanBuffer<UBO_VP_mats>(1, deviceProperties.limits.maxUniformBufferRange,
131 deviceProperties.limits.minUniformBufferOffsetAlignment);
[90880fb]132
[b7fc3c2]133 objects_laserPipeline = VulkanBuffer<SSBO_Laser>(2, deviceProperties.limits.maxStorageBufferRange,
134 deviceProperties.limits.minStorageBufferOffsetAlignment);
[90880fb]135
136 uniforms_explosionPipeline = VulkanBuffer<UBO_Explosion>(1, deviceProperties.limits.maxUniformBufferRange,
137 deviceProperties.limits.minUniformBufferOffsetAlignment);
138
[b7fc3c2]139 objects_explosionPipeline = VulkanBuffer<SSBO_Explosion>(2, deviceProperties.limits.maxStorageBufferRange,
140 deviceProperties.limits.minStorageBufferOffsetAlignment);
[a3cefaa]141
[cefdf23]142 initImGuiOverlay();
[60578ce]143
[f97c5e7]144 // TODO: Figure out how much of ubo creation and associated variables should be in the pipeline class
145 // Maybe combine the ubo-related objects into a new class
146
147 initGraphicsPipelines();
148
[cefdf23]149 initMatrices();
150
151 cout << "INITIALIZING OBJECTS" << endl;
152
[f97c5e7]153 modelPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::pos));
154 modelPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::color));
155 modelPipeline.addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&ModelVertex::texCoord));
[a00eb06]156 modelPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::normal));
[5a1ace0]157 modelPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ModelVertex::objIndex));
[f97c5e7]158
[2f4ff8c]159 createBufferSet(uniforms_modelPipeline.memorySize(),
[6486ba8]160 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
[2f4ff8c]161 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[6486ba8]162 uniformBuffers_modelPipeline);
163
[2f4ff8c]164 uniforms_modelPipeline.map(uniformBuffers_modelPipeline.memory, device);
165
[6486ba8]166 createBufferSet(objects_modelPipeline.memorySize(),
167 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
168 | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
[2f4ff8c]169 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[b01b50c]170 objectBuffers_modelPipeline);
[f97c5e7]171
[b01b50c]172 objects_modelPipeline.map(objectBuffers_modelPipeline.memory, device);
[2f4ff8c]173
[f97c5e7]174 modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
[c163d81]175 VK_SHADER_STAGE_VERTEX_BIT, &uniformBuffers_modelPipeline.infoSet);
[9d21aac]176 modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
[b01b50c]177 VK_SHADER_STAGE_VERTEX_BIT, &objectBuffers_modelPipeline.infoSet);
[f97c5e7]178 modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
179 VK_SHADER_STAGE_FRAGMENT_BIT, &floorTextureImageDescriptor);
[5a0242e]180
[6486ba8]181 SceneObject<ModelVertex>* texturedSquare = nullptr;
[4994692]182
183 texturedSquare = &addObject(modelObjects, modelPipeline,
[a00eb06]184 addObjectIndex<ModelVertex>(modelObjects.size(),
185 addVertexNormals<ModelVertex>({
186 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
187 {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
188 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
189 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
190 {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
191 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}}
[1abebc1]192 })),
193 {
194 0, 1, 2, 3, 4, 5
[8dcbf62]195 }, objects_modelPipeline, {
[1abebc1]196 mat4(1.0f)
197 });
[996dd3e]198
[4994692]199 texturedSquare->model_base =
[1add0ed]200 translate(mat4(1.0f), vec3(0.0f, 0.0f, -2.0f));
201
[4994692]202 texturedSquare = &addObject(modelObjects, modelPipeline,
[a00eb06]203 addObjectIndex<ModelVertex>(modelObjects.size(),
204 addVertexNormals<ModelVertex>({
205 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
206 {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
207 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
208 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
209 {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
210 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}}
[1abebc1]211 })),
212 {
213 0, 1, 2, 3, 4, 5
[8dcbf62]214 }, objects_modelPipeline, {
[1abebc1]215 mat4(1.0f)
216 });
[996dd3e]217
[4994692]218 texturedSquare->model_base =
[1add0ed]219 translate(mat4(1.0f), vec3(0.0f, 0.0f, -1.5f));
220
[b8777b7]221 modelPipeline.createDescriptorSetLayout();
[aa7e5f0]222 modelPipeline.createPipeline("shaders/model-vert.spv", "shaders/model-frag.spv");
[58453c3]223 modelPipeline.createDescriptorPool(swapChainImages.size());
224 modelPipeline.createDescriptorSets(swapChainImages.size());
[7d2b0b9]225
[8d92284]226 // START UNREVIEWED SECTION
227 shipPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::pos));
228 shipPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::color));
229 shipPipeline.addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&ModelVertex::texCoord));
230 shipPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::normal));
231 shipPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ModelVertex::objIndex));
[3782d66]232
[2f4ff8c]233 createBufferSet(uniforms_shipPipeline.memorySize(),
[6486ba8]234 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
[2f4ff8c]235 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[6486ba8]236 uniformBuffers_shipPipeline);
237
[2f4ff8c]238 uniforms_shipPipeline.map(uniformBuffers_shipPipeline.memory, device);
239
[6486ba8]240 createBufferSet(objects_shipPipeline.memorySize(),
241 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
242 | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
[2f4ff8c]243 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[b01b50c]244 objectBuffers_shipPipeline);
[3782d66]245
[b01b50c]246 objects_shipPipeline.map(objectBuffers_shipPipeline.memory, device);
[2f4ff8c]247
[3782d66]248 shipPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
[c163d81]249 VK_SHADER_STAGE_VERTEX_BIT, &uniformBuffers_shipPipeline.infoSet);
[9d21aac]250 shipPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
[b01b50c]251 VK_SHADER_STAGE_VERTEX_BIT, &objectBuffers_shipPipeline.infoSet);
[3782d66]252
[e1308e8]253 // TODO: With the normals, indexing basically becomes pointless since no vertices will have exactly
254 // the same data. Add an option to make some pipelines not use indexing
[6486ba8]255 SceneObject<ModelVertex>& ship = addObject(shipObjects, shipPipeline,
[8d92284]256 addObjectIndex<ModelVertex>(shipObjects.size(),
257 addVertexNormals<ModelVertex>({
[cefdf23]258
259 //back
260 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
261 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
262 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
263 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
264 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
265 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
266
267 // left back
268 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
269 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
270 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
271 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
272 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
273 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
274
275 // right back
276 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
277 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
278 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
279 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
280 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
281 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
282
283 // left mid
284 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
285 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
286 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
287 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
288 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
289 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
290
291 // right mid
292 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
293 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
294 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
295 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
296 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
297 {{ 0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
298
299 // left front
300 {{ 0.0f, 0.0f, -3.5f}, {0.0f, 0.0f, 1.0f}},
301 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
302 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
303
304 // right front
305 {{ 0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
306 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
307 {{ 0.0f, 0.0f, -3.5f}, {0.0f, 0.0f, 1.0f}},
308
309 // top back
310 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
311 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 1.0f}},
312 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 1.0f}},
313 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
314 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 1.0f}},
315 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
316
317 // bottom back
318 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
319 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
320 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
321 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
322 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
323 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
324
325 // top mid
326 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
327 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
328 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
329 {{ -0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
330 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
331 {{ 0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
332
333 // bottom mid
334 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
335 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
336 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
337 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
338 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
339 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
340
341 // top front
342 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
343 {{ 0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
344 {{ 0.0f, 0.0f, -3.5f}, {0.0f, 0.0f, 0.3f}},
345
346 // bottom front
347 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
348 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
349 {{ 0.0f, 0.0f, -3.5f}, {0.0f, 0.0f, 0.3f}},
350
351 // left wing start back
352 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
353 {{ -1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
354 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
355 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
356 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
357 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
358
359 // left wing start top
360 {{ -0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
361 {{ -1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
362 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
363 {{ -0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
364 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
365 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
366
367 // left wing start front
368 {{ -0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
369 {{ -0.5f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
370 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
371 {{ -0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
372 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
373 {{ -1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
374
375 // left wing start bottom
376 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
377 {{ -1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
378 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
379 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
380 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
381 {{ -0.5f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
382
383 // left wing end outside
384 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
385 {{ -2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
386 {{ -1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
387
388 // left wing end top
389 {{ -1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
390 {{ -2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
391 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
392
393 // left wing end front
394 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
395 {{ -2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
396 {{ -1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
397
398 // left wing end bottom
399 {{ -1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
400 {{ -2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
401 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
402
403 // right wing start back
404 {{ 1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
405 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
406 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
407 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
408 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
409 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
410
411 // right wing start top
412 {{ 1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
413 {{ 0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
414 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
415 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
416 {{ 0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
417 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
418
419 // right wing start front
420 {{ 0.5f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
421 {{ 0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
422 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
423 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
424 {{ 0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
425 {{ 1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
426
427 // right wing start bottom
428 {{ 1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
429 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
430 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
431 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
432 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
433 {{ 0.5f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
434
435 // right wing end outside
436 {{ 2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
437 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
438 {{ 1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
439
440 // right wing end top
441 {{ 2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
442 {{ 1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
443 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
444
445 // right wing end front
446 {{ 2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
447 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
448 {{ 1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
449
450 // right wing end bottom
451 {{ 2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
452 {{ 1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
453 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
[1abebc1]454 })),
455 {
456 0, 1, 2, 3, 4, 5,
457 6, 7, 8, 9, 10, 11,
458 12, 13, 14, 15, 16, 17,
459 18, 19, 20, 21, 22, 23,
460 24, 25, 26, 27, 28, 29,
461 30, 31, 32,
462 33, 34, 35,
463 36, 37, 38, 39, 40, 41,
464 42, 43, 44, 45, 46, 47,
465 48, 49, 50, 51, 52, 53,
466 54, 55, 56, 57, 58, 59,
467 60, 61, 62,
468 63, 64, 65,
469 66, 67, 68, 69, 70, 71,
470 72, 73, 74, 75, 76, 77,
471 78, 79, 80, 81, 82, 83,
472 84, 85, 86, 87, 88, 89,
473 90, 91, 92,
474 93, 94, 95,
475 96, 97, 98,
476 99, 100, 101,
477 102, 103, 104, 105, 106, 107,
478 108, 109, 110, 111, 112, 113,
479 114, 115, 116, 117, 118, 119,
480 120, 121, 122, 123, 124, 125,
481 126, 127, 128,
482 129, 130, 131,
483 132, 133, 134,
484 135, 136, 137,
[8dcbf62]485 }, objects_shipPipeline, {
[2d87297]486 mat4(1.0f)
[1abebc1]487 });
[996dd3e]488
[52a02e6]489 ship.model_base =
490 translate(mat4(1.0f), vec3(0.0f, -1.2f, 1.65f)) *
491 scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
492
[3782d66]493 shipPipeline.createDescriptorSetLayout();
494 shipPipeline.createPipeline("shaders/ship-vert.spv", "shaders/ship-frag.spv");
[58453c3]495 shipPipeline.createDescriptorPool(swapChainImages.size());
496 shipPipeline.createDescriptorSets(swapChainImages.size());
[3782d66]497
[b8efa56]498 asteroidPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::pos));
499 asteroidPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::color));
500 asteroidPipeline.addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&ModelVertex::texCoord));
501 asteroidPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::normal));
502 asteroidPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ModelVertex::objIndex));
[3e8cc8b]503
[2f4ff8c]504 createBufferSet(uniforms_asteroidPipeline.memorySize(),
[6486ba8]505 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
[2f4ff8c]506 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[6486ba8]507 uniformBuffers_asteroidPipeline);
508
[2f4ff8c]509 uniforms_asteroidPipeline.map(uniformBuffers_asteroidPipeline.memory, device);
510
[6486ba8]511 createBufferSet(objects_asteroidPipeline.memorySize(),
512 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
513 | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
[2f4ff8c]514 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[b01b50c]515 objectBuffers_asteroidPipeline);
[3e8cc8b]516
[b01b50c]517 objects_asteroidPipeline.map(objectBuffers_asteroidPipeline.memory, device);
[2f4ff8c]518
[3e8cc8b]519 asteroidPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
[c163d81]520 VK_SHADER_STAGE_VERTEX_BIT, &uniformBuffers_asteroidPipeline.infoSet);
[9d21aac]521 asteroidPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
[b01b50c]522 VK_SHADER_STAGE_VERTEX_BIT, &objectBuffers_asteroidPipeline.infoSet);
[3e8cc8b]523
524 asteroidPipeline.createDescriptorSetLayout();
525 asteroidPipeline.createPipeline("shaders/asteroid-vert.spv", "shaders/asteroid-frag.spv");
[58453c3]526 asteroidPipeline.createDescriptorPool(swapChainImages.size());
527 asteroidPipeline.createDescriptorSets(swapChainImages.size());
[3e8cc8b]528
[237cbec]529 laserPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&LaserVertex::pos));
530 laserPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&LaserVertex::texCoord));
531 laserPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&LaserVertex::objIndex));
532
[2f4ff8c]533 createBufferSet(uniforms_laserPipeline.memorySize(),
[6486ba8]534 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
[2f4ff8c]535 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[6486ba8]536 uniformBuffers_laserPipeline);
537
[2f4ff8c]538 uniforms_laserPipeline.map(uniformBuffers_laserPipeline.memory, device);
539
[6486ba8]540 createBufferSet(objects_laserPipeline.memorySize(),
541 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
542 | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
[2f4ff8c]543 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[b01b50c]544 objectBuffers_laserPipeline);
[237cbec]545
[b01b50c]546 objects_laserPipeline.map(objectBuffers_laserPipeline.memory, device);
[2f4ff8c]547
[237cbec]548 laserPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
[c163d81]549 VK_SHADER_STAGE_VERTEX_BIT, &uniformBuffers_laserPipeline.infoSet);
[9d21aac]550 laserPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
[b01b50c]551 VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, &objectBuffers_laserPipeline.infoSet);
[237cbec]552 laserPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
553 VK_SHADER_STAGE_FRAGMENT_BIT, &laserTextureImageDescriptor);
554
555 laserPipeline.createDescriptorSetLayout();
556 laserPipeline.createPipeline("shaders/laser-vert.spv", "shaders/laser-frag.spv");
[58453c3]557 laserPipeline.createDescriptorPool(swapChainImages.size());
558 laserPipeline.createDescriptorSets(swapChainImages.size());
[237cbec]559
[4a9416a]560 explosionPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ExplosionVertex::particleStartVelocity));
561 explosionPipeline.addAttribute(VK_FORMAT_R32_SFLOAT, offset_of(&ExplosionVertex::particleStartTime));
562 explosionPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ExplosionVertex::objIndex));
563
[90880fb]564 createBufferSet(uniforms_explosionPipeline.memorySize(),
[6486ba8]565 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
[2f4ff8c]566 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[6486ba8]567 uniformBuffers_explosionPipeline);
568
[2f4ff8c]569 uniforms_explosionPipeline.map(uniformBuffers_explosionPipeline.memory, device);
570
[6486ba8]571 createBufferSet(objects_explosionPipeline.memorySize(),
572 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
573 | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
[2f4ff8c]574 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[b01b50c]575 objectBuffers_explosionPipeline);
[4a9416a]576
[b01b50c]577 objects_explosionPipeline.map(objectBuffers_explosionPipeline.memory, device);
[2f4ff8c]578
[4a9416a]579 explosionPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
[c163d81]580 VK_SHADER_STAGE_VERTEX_BIT, &uniformBuffers_explosionPipeline.infoSet);
[9d21aac]581 explosionPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
[b01b50c]582 VK_SHADER_STAGE_VERTEX_BIT, &objectBuffers_explosionPipeline.infoSet);
[4a9416a]583
584 explosionPipeline.createDescriptorSetLayout();
585 explosionPipeline.createPipeline("shaders/explosion-vert.spv", "shaders/explosion-frag.spv");
[58453c3]586 explosionPipeline.createDescriptorPool(swapChainImages.size());
587 explosionPipeline.createDescriptorSets(swapChainImages.size());
[4a9416a]588
[8d92284]589 // END UNREVIEWED SECTION
590
[cefdf23]591 currentRenderScreenFn = &VulkanGame::renderMainScreen;
[34bdf3a]592
[cefdf23]593 ImGuiIO& io = ImGui::GetIO();
[34bdf3a]594
[cefdf23]595 initGuiValueLists(valueLists);
596
597 valueLists["stats value list"].push_back(UIValue(UIVALUE_INT, "Score", &score));
598 valueLists["stats value list"].push_back(UIValue(UIVALUE_DOUBLE, "FPS", &fps));
599 valueLists["stats value list"].push_back(UIValue(UIVALUE_DOUBLE, "IMGUI FPS", &io.Framerate));
600
601 renderLoop();
602 cleanup();
603
604 close_log();
605}
606
607bool VulkanGame::initUI(int width, int height, unsigned char guiFlags) {
608 // TODO: Create a game-gui function to get the gui version and retrieve it that way
609
610 SDL_VERSION(&sdlVersion); // This gets the compile-time version
611 SDL_GetVersion(&sdlVersion); // This gets the runtime version
612
613 cout << "SDL "<<
614 to_string(sdlVersion.major) << "." <<
615 to_string(sdlVersion.minor) << "." <<
616 to_string(sdlVersion.patch) << endl;
617
618 // TODO: Refactor the logger api to be more flexible,
619 // esp. since gl_log() and gl_log_err() have issues printing anything besides strings
620 restart_gl_log();
621 gl_log("starting SDL\n%s.%s.%s",
622 to_string(sdlVersion.major).c_str(),
623 to_string(sdlVersion.minor).c_str(),
624 to_string(sdlVersion.patch).c_str());
[3b7d497]625
[cefdf23]626 // TODO: Use open_Log() and related functions instead of gl_log ones
627 // TODO: In addition, delete the gl_log functions
628 open_log();
629 get_log() << "starting SDL" << endl;
630 get_log() <<
631 (int)sdlVersion.major << "." <<
632 (int)sdlVersion.minor << "." <<
633 (int)sdlVersion.patch << endl;
634
635 // TODO: Put all fonts, textures, and images in the assets folder
636 gui = new GameGui_SDL();
637
638 if (gui->init() == RTWO_ERROR) {
639 // TODO: Also print these sorts of errors to the log
640 cout << "UI library could not be initialized!" << endl;
641 cout << gui->getError() << endl;
642 // TODO: Rename RTWO_ERROR to something else
643 return RTWO_ERROR;
644 }
645
[a00eb06]646 window = (SDL_Window*)gui->createWindow("Vulkan Game", width, height, guiFlags & GUI_FLAGS_WINDOW_FULLSCREEN);
[cefdf23]647 if (window == nullptr) {
648 cout << "Window could not be created!" << endl;
649 cout << gui->getError() << endl;
650 return RTWO_ERROR;
651 }
652
653 cout << "Target window size: (" << width << ", " << height << ")" << endl;
654 cout << "Actual window size: (" << gui->getWindowWidth() << ", " << gui->getWindowHeight() << ")" << endl;
655
656 return RTWO_SUCCESS;
657}
658
659void VulkanGame::initVulkan() {
660 const vector<const char*> validationLayers = {
661 "VK_LAYER_KHRONOS_validation"
662 };
663 const vector<const char*> deviceExtensions = {
664 VK_KHR_SWAPCHAIN_EXTENSION_NAME
665 };
666
667 createVulkanInstance(validationLayers);
668 setupDebugMessenger();
669 createVulkanSurface();
670 pickPhysicalDevice(deviceExtensions);
671 createLogicalDevice(validationLayers, deviceExtensions);
672 chooseSwapChainProperties();
673 createSwapChain();
674 createImageViews();
675
676 createResourceCommandPool();
677 createImageResources();
678
679 createRenderPass();
680 createCommandPools();
681 createFramebuffers();
682 createCommandBuffers();
683 createSyncObjects();
[0df3c9a]684}
685
[f97c5e7]686void VulkanGame::initGraphicsPipelines() {
[9d21aac]687 modelPipeline = GraphicsPipeline_Vulkan<ModelVertex>(
[52a02e6]688 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
[58453c3]689 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, 24, 24);
[2d87297]690
[9d21aac]691 shipPipeline = GraphicsPipeline_Vulkan<ModelVertex>(
[52a02e6]692 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
[58453c3]693 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, 138, 138);
[3e8cc8b]694
[9d21aac]695 asteroidPipeline = GraphicsPipeline_Vulkan<ModelVertex>(
[52a02e6]696 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
[58453c3]697 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, 24, 36);
[237cbec]698
[9d21aac]699 laserPipeline = GraphicsPipeline_Vulkan<LaserVertex>(
[52a02e6]700 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, physicalDevice, device, renderPass,
[58453c3]701 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, 8, 18);
[4a9416a]702
[9d21aac]703 explosionPipeline = GraphicsPipeline_Vulkan<ExplosionVertex>(
[4a9416a]704 VK_PRIMITIVE_TOPOLOGY_POINT_LIST, physicalDevice, device, renderPass,
705 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height },
[58453c3]706 EXPLOSION_PARTICLE_COUNT, EXPLOSION_PARTICLE_COUNT);
[f97c5e7]707}
708
[683dd55]709// TODO: Maybe changes the name to initScene() or something similar
[15104a8]710void VulkanGame::initMatrices() {
[cefdf23]711 cam_pos = vec3(0.0f, 0.0f, 2.0f);
[15104a8]712
713 float cam_yaw = 0.0f;
[a79be34]714 float cam_pitch = -50.0f;
[15104a8]715
716 mat4 yaw_mat = rotate(mat4(1.0f), radians(-cam_yaw), vec3(0.0f, 1.0f, 0.0f));
717 mat4 pitch_mat = rotate(mat4(1.0f), radians(-cam_pitch), vec3(1.0f, 0.0f, 0.0f));
718
[f97c5e7]719 mat4 R_view = pitch_mat * yaw_mat;
[cefdf23]720 mat4 T_view = translate(mat4(1.0f), vec3(-cam_pos.x, -cam_pos.y, -cam_pos.z));
[22217d4]721 viewMat = R_view * T_view;
[15104a8]722
[22217d4]723 projMat = perspective(radians(FOV_ANGLE), (float)swapChainExtent.width / (float)swapChainExtent.height, NEAR_CLIP, FAR_CLIP);
724 projMat[1][1] *= -1; // flip the y-axis so that +y is up
[15104a8]725}
726
[20e4c2b]727void VulkanGame::renderLoop() {
[187b0f5]728 startTime = steady_clock::now();
729 curTime = duration<float, seconds::period>(steady_clock::now() - startTime).count();
[4ece3bf]730
[20e4c2b]731 fpsStartTime = curTime;
732 frameCount = 0;
[f809ae6]733
[6104594]734 lastSpawn_asteroid = curTime;
[4ece3bf]735
[6053b24]736 ImGuiIO& io = ImGui::GetIO();
737
[c6f0793]738 done = false;
739 while (!done) {
[4ece3bf]740
[cefdf23]741 prevTime = curTime;
742 curTime = duration<float, seconds::period>(steady_clock::now() - startTime).count();
743 elapsedTime = curTime - prevTime;
[4ece3bf]744
[cefdf23]745 if (curTime - fpsStartTime >= 1.0f) {
746 fps = (float)frameCount / (curTime - fpsStartTime);
[f809ae6]747
[cefdf23]748 frameCount = 0;
749 fpsStartTime = curTime;
[f809ae6]750 }
751
[cefdf23]752 frameCount++;
[f809ae6]753
[27c40ce]754 gui->processEvents();
755
[d8cf709]756 UIEvent uiEvent;
757 while (gui->pollEvent(&uiEvent)) {
758 GameEvent& e = uiEvent.event;
[6053b24]759 SDL_Event sdlEvent = uiEvent.rawEvent.sdl;
760
761 ImGui_ImplSDL2_ProcessEvent(&sdlEvent);
[5081b9a]762 if ((e.type == UI_EVENT_MOUSEBUTTONDOWN || e.type == UI_EVENT_MOUSEBUTTONUP || e.type == UI_EVENT_UNKNOWN) &&
763 io.WantCaptureMouse) {
764 if (sdlEvent.type == SDL_MOUSEWHEEL || sdlEvent.type == SDL_MOUSEBUTTONDOWN ||
[914bb99]765 sdlEvent.type == SDL_MOUSEBUTTONUP) {
[6053b24]766 continue;
767 }
768 }
[5081b9a]769 if ((e.type == UI_EVENT_KEYDOWN || e.type == UI_EVENT_KEYUP) && io.WantCaptureKeyboard) {
[6053b24]770 if (sdlEvent.type == SDL_KEYDOWN || sdlEvent.type == SDL_KEYUP) {
771 continue;
772 }
773 }
774 if (io.WantTextInput) {
775 // show onscreen keyboard if on mobile
776 }
[d8cf709]777
[5192672]778 switch (e.type) {
[f6521fb]779 case UI_EVENT_QUIT:
780 cout << "Quit event detected" << endl;
[c6f0793]781 done = true;
[f6521fb]782 break;
[0e09340]783 case UI_EVENT_WINDOWRESIZE:
784 cout << "Window resize event detected" << endl;
[28ea92f]785 shouldRecreateSwapChain = true;
[0e09340]786 break;
[e3bef3a]787 case UI_EVENT_KEYDOWN:
[4994692]788 if (e.key.repeat) {
789 break;
790 }
791
[f6521fb]792 if (e.key.keycode == SDL_SCANCODE_ESCAPE) {
[c6f0793]793 done = true;
[e3bef3a]794 } else if (e.key.keycode == SDL_SCANCODE_SPACE) {
795 cout << "Adding a plane" << endl;
[0fe8433]796 float zOffset = -2.0f + (0.5f * modelObjects.size());
[5a0242e]797
[6486ba8]798 SceneObject<ModelVertex>& texturedSquare = addObject(modelObjects, modelPipeline,
799 addObjectIndex<ModelVertex>(modelObjects.size(),
800 addVertexNormals<ModelVertex>({
801 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
802 {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
803 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
804 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
805 {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
806 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}}
807 })),
808 {
809 0, 1, 2, 3, 4, 5
810 }, objects_modelPipeline, {
811 mat4(1.0f)
812 });
[996dd3e]813
[4994692]814 texturedSquare.model_base =
[1add0ed]815 translate(mat4(1.0f), vec3(0.0f, 0.0f, zOffset));
[8d92284]816 // START UNREVIEWED SECTION
[1f81ecc]817 } else if (e.key.keycode == SDL_SCANCODE_Z && leftLaserIdx == -1) {
818 // TODO: When I start actually removing objects from the object vectors,
819 // I will need to update the indices since they might become incorrect
820 // or invalid as objects get moved around
821
822 vec3 offset(shipObjects[0].model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
823
824 addLaser(
825 vec3(-0.21f, -1.19f, 1.76f) + offset,
826 vec3(-0.21f, -1.19f, -3.0f) + offset,
[4a9416a]827 LASER_COLOR, 0.03f);
[1f81ecc]828
829 leftLaserIdx = laserObjects.size() - 1;
830 } else if (e.key.keycode == SDL_SCANCODE_X && rightLaserIdx == -1) {
831 vec3 offset(shipObjects[0].model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
832
833 addLaser(
834 vec3(0.21f, -1.19f, 1.76f) + offset,
835 vec3(0.21f, -1.19f, -3.0f) + offset,
[4a9416a]836 LASER_COLOR, 0.03f);
[1f81ecc]837
838 rightLaserIdx = laserObjects.size() - 1;
[8d92284]839 // END UNREVIEWED SECTION
[f6521fb]840 } else {
841 cout << "Key event detected" << endl;
842 }
843 break;
[5a0242e]844 case UI_EVENT_KEYUP:
[8d92284]845 // START UNREVIEWED SECTION
[1f81ecc]846 if (e.key.keycode == SDL_SCANCODE_Z && leftLaserIdx != -1) {
[6486ba8]847 objects_laserPipeline.get(leftLaserIdx).deleted = true;
[1f81ecc]848 leftLaserIdx = -1;
[7297892]849
850 if (leftLaserEffect != nullptr) {
851 leftLaserEffect->deleted = true;
852 leftLaserEffect = nullptr;
853 }
[1f81ecc]854 } else if (e.key.keycode == SDL_SCANCODE_X && rightLaserIdx != -1) {
[6486ba8]855 objects_laserPipeline.get(rightLaserIdx).deleted = true;
[1f81ecc]856 rightLaserIdx = -1;
[7297892]857
858 if (rightLaserEffect != nullptr) {
859 rightLaserEffect->deleted = true;
860 rightLaserEffect = nullptr;
861 }
[1f81ecc]862 }
[8d92284]863 // END UNREVIEWED SECTION
[5a0242e]864 break;
[5081b9a]865 case UI_EVENT_WINDOW:
[f6521fb]866 case UI_EVENT_MOUSEBUTTONDOWN:
867 case UI_EVENT_MOUSEBUTTONUP:
868 case UI_EVENT_MOUSEMOTION:
869 break;
[5081b9a]870 case UI_EVENT_UNHANDLED:
871 cout << "Unhandled event type: 0x" << hex << sdlEvent.type << dec << endl;
[a0da009]872 break;
[5081b9a]873 case UI_EVENT_UNKNOWN:
[c61323a]874 default:
[5081b9a]875 cout << "Unknown event type: 0x" << hex << sdlEvent.type << dec << endl;
876 break;
[0df3c9a]877 }
[e1f88a9]878
[301c90a]879 // This was left ovedr from the previous SDL UI implementation.
880 // Might need something like this again when I start processing screen-specific UI events not related
881 // to the IMGUI ui, such as arrow keys for movement and other buttons for shotting or something
[20e4c2b]882 // currentScreen->handleEvent(e);
[0df3c9a]883 }
[c1d9b2a]884
[cd1cb0f]885 // Check which keys are held down
886
[6486ba8]887 SceneObject<ModelVertex>& ship = shipObjects[0];
[4994692]888
[cd1cb0f]889 if (gui->keyPressed(SDL_SCANCODE_LEFT)) {
[4994692]890 float distance = -this->shipSpeed * this->elapsedTime;
891
892 ship.model_transform = translate(mat4(1.0f), vec3(distance, 0.0f, 0.0f))
[2da64ef]893 * shipObjects[0].model_transform;
[1f81ecc]894
895 if (leftLaserIdx != -1) {
896 translateLaser(leftLaserIdx, vec3(distance, 0.0f, 0.0f));
897 }
898 if (rightLaserIdx != -1) {
899 translateLaser(rightLaserIdx, vec3(distance, 0.0f, 0.0f));
900 }
[cd1cb0f]901 } else if (gui->keyPressed(SDL_SCANCODE_RIGHT)) {
[4994692]902 float distance = this->shipSpeed * this->elapsedTime;
903
904 ship.model_transform = translate(mat4(1.0f), vec3(distance, 0.0f, 0.0f))
[2da64ef]905 * shipObjects[0].model_transform;
[1f81ecc]906
907 if (leftLaserIdx != -1) {
908 translateLaser(leftLaserIdx, vec3(distance, 0.0f, 0.0f));
909 }
910 if (rightLaserIdx != -1) {
911 translateLaser(rightLaserIdx, vec3(distance, 0.0f, 0.0f));
912 }
[4ece3bf]913 }
[3e8cc8b]914
[484334e]915 if (shouldRecreateSwapChain) {
916 gui->refreshWindowSize();
[8d96e95]917 const bool isMinimized = gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0;
[484334e]918
[8d96e95]919 if (!isMinimized) {
[484334e]920 // TODO: This should be used if the min image count changes, presumably because a new surface was created
921 // with a different image count or something like that. Maybe I want to add code to query for a new min image count
922 // during swapchain recreation to take advantage of this
923 ImGui_ImplVulkan_SetMinImageCount(swapChainMinImageCount);
924
925 recreateSwapChain();
926
927 shouldRecreateSwapChain = false;
928 }
[8d92284]929 }// REVIEWED TO THIS POINT
[484334e]930
[ea2b4dc]931 updateScene();
932
[cefdf23]933 // TODO: Move this into a renderImGuiOverlay() function
[ea2b4dc]934 ImGui_ImplVulkan_NewFrame();
[8d96e95]935 ImGui_ImplSDL2_NewFrame(window);
[ea2b4dc]936 ImGui::NewFrame();
937
[cb6fabb]938 int w, h;
939 SDL_GetWindowSize(((GameGui_SDL*)gui)->window, &w, &h);
940
941 // Probably a retina display
942 // TODO: Find a better fix for this. Maybe I should use SDL_Vulkan_GetWindowSize here instead
943 // of SDL_Vulkan_GetDrawableSize
944 if (w < gui->getWindowWidth() && h < gui->getWindowHeight()) {
945 (this->*currentRenderScreenFn)(w, h);
946 } else {
947 (this->*currentRenderScreenFn)(gui->getWindowWidth(), gui->getWindowHeight());
948 }
[ea2b4dc]949
950 ImGui::Render();
951
[81869ef]952 gui->refreshWindowSize();
[8d96e95]953 const bool isMinimized = gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0;
[81869ef]954
955 if (!isMinimized) {
956 renderFrame(ImGui::GetDrawData());
957 presentFrame();
958 }
[0df3c9a]959 }
960}
961
[2da64ef]962// TODO: The only updates that need to happen once per Vulkan image are the SSBO ones,
963// which are already handled by updateObject(). Move this code to a different place,
964// where it will run just once per frame
[3f32dfd]965void VulkanGame::updateScene() {
[90880fb]966
[2f4ff8c]967 // TODO: I can probably use one buffer to store the view and projection mats
968 // and share it across all the shader pipelines
969
970 uniforms_modelPipeline.data()->view = viewMat;
971 uniforms_modelPipeline.data()->proj = projMat;
[90880fb]972
[2f4ff8c]973 uniforms_shipPipeline.data()->view = viewMat;
974 uniforms_shipPipeline.data()->proj = projMat;
[90880fb]975
[2f4ff8c]976 uniforms_asteroidPipeline.data()->view = viewMat;
977 uniforms_asteroidPipeline.data()->proj = projMat;
[90880fb]978
[2f4ff8c]979 uniforms_laserPipeline.data()->view = viewMat;
980 uniforms_laserPipeline.data()->proj = projMat;
[90880fb]981
982 uniforms_explosionPipeline.data()->view = viewMat;
983 uniforms_explosionPipeline.data()->proj = projMat;
984 uniforms_explosionPipeline.data()->cur_time = curTime;
985
986 for (vector<BaseEffectOverTime*>::iterator it = effects.begin(); it != effects.end(); ) {
987 if ((*it)->deleted) {
988 delete *it;
989 it = effects.erase(it);
990 } else {
991 BaseEffectOverTime* eot = *it;
992
993 eot->applyEffect(curTime);
994
995 it++;
996 }
997 }
998
[6486ba8]999 if (curTime - lastSpawn_asteroid > spawnRate_asteroid) {
1000 lastSpawn_asteroid = curTime;
1001
1002 SceneObject<ModelVertex>& asteroid = addObject(asteroidObjects, asteroidPipeline,
1003 addObjectIndex<ModelVertex>(asteroidObjects.size(),
1004 addVertexNormals<ModelVertex>({
1005
1006 // front
1007 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1008 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1009 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1010 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1011 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1012 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1013
1014 // top
1015 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1016 {{-1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1017 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1018 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1019 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1020 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1021
1022 // bottom
1023 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1024 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1025 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1026 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1027 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1028 {{ 1.0f, -1.0f, -1.0}, {0.4f, 0.4f, 0.4f}},
1029
1030 // back
1031 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1032 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1033 {{-1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1034 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1035 {{ 1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1036 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1037
1038 // right
1039 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1040 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1041 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1042 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1043 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1044 {{ 1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1045
1046 // left
1047 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1048 {{-1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1049 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1050 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1051 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
1052 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
1053 })),
1054 {
1055 0, 1, 2, 3, 4, 5,
1056 6, 7, 8, 9, 10, 11,
1057 12, 13, 14, 15, 16, 17,
1058 18, 19, 20, 21, 22, 23,
1059 24, 25, 26, 27, 28, 29,
1060 30, 31, 32, 33, 34, 35,
1061 }, objects_asteroidPipeline, {
1062 mat4(1.0f),
1063 10.0f,
1064 false
1065 });
[996dd3e]1066
[2ba5617]1067 // This accounts for the scaling in model_base.
1068 // Dividing by 8 instead of 10 since the bounding radius algorithm
1069 // under-calculates the true value.
1070 // TODO: Figure out the best way to take scaling into account when calculating the radius
1071 // Keep in mind that the main complicating factor is the currently poor radius calculation
[4994692]1072 asteroid.radius /= 8.0f;
[2ba5617]1073
[4994692]1074 asteroid.model_base =
[2ba5617]1075 translate(mat4(1.0f), vec3(getRandomNum(-1.3f, 1.3f), -1.2f, getRandomNum(-5.5f, -4.5f))) *
[0807aeb]1076 rotate(mat4(1.0f), radians(60.0f), vec3(1.0f, 1.0f, -1.0f)) *
1077 scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
[5ba732a]1078 }
1079
[b7fc3c2]1080 // TODO: Probably move the resizing to the VulkanBuffer class
[bb76950]1081 // TODO: Figure out a way to make updateDescriptorInfo easier to use, maybe store the binding index in the buffer set
1082
[6bac215]1083 if (objects_modelPipeline.resized) {
[b01b50c]1084 objects_modelPipeline.unmap(objectBuffers_modelPipeline.memory, device);
[2f4ff8c]1085
[b01b50c]1086 resizeBufferSet(objectBuffers_modelPipeline, objects_modelPipeline.memorySize(), resourceCommandPool,
[b7fc3c2]1087 graphicsQueue, true);
[6bac215]1088
[b01b50c]1089 objects_modelPipeline.map(objectBuffers_modelPipeline.memory, device);
[2f4ff8c]1090
[6bac215]1091 objects_modelPipeline.resize();
[bb76950]1092
[b01b50c]1093 modelPipeline.updateDescriptorInfo(1, &objectBuffers_modelPipeline.infoSet, swapChainImages.size());
[5ba732a]1094 }
[0807aeb]1095
[5ba732a]1096 for (size_t i = 0; i < modelObjects.size(); i++) {
[6486ba8]1097 SceneObject<ModelVertex>& obj = modelObjects[i];
1098 SSBO_ModelObject& objData = objects_modelPipeline.get(i);
[b7fc3c2]1099
1100 // Rotate the textured squares
1101 obj.model_transform =
1102 translate(mat4(1.0f), vec3(0.0f, -2.0f, -0.0f)) *
1103 rotate(mat4(1.0f), curTime * radians(90.0f), vec3(0.0f, 0.0f, 1.0f));
1104
[c1ec4f6]1105 objData.model = obj.model_transform * obj.model_base;
1106 obj.center = vec3(objData.model * vec4(0.0f, 0.0f, 0.0f, 1.0f));
[5ba732a]1107 }
1108
[2f4ff8c]1109 VulkanUtils::copyDataToMappedMemory(device, uniforms_modelPipeline.data(), uniforms_modelPipeline.mapped(imageIndex),
1110 uniformBuffers_modelPipeline.memory[imageIndex],
1111 uniforms_modelPipeline.memorySize(), true);
[90880fb]1112
[2f4ff8c]1113 VulkanUtils::copyDataToMappedMemory(device, objects_modelPipeline.data(), objects_modelPipeline.mapped(imageIndex),
[b01b50c]1114 objectBuffers_modelPipeline.memory[imageIndex],
[2f4ff8c]1115 objects_modelPipeline.memorySize(), true);
[5ea0a37]1116
[6bac215]1117 if (objects_shipPipeline.resized) {
[b01b50c]1118 objects_shipPipeline.unmap(objectBuffers_shipPipeline.memory, device);
[2f4ff8c]1119
[b01b50c]1120 resizeBufferSet(objectBuffers_shipPipeline, objects_shipPipeline.memorySize(), resourceCommandPool,
[b7fc3c2]1121 graphicsQueue, true);
[6bac215]1122
[b01b50c]1123 objects_shipPipeline.map(objectBuffers_shipPipeline.memory, device);
[2f4ff8c]1124
[6bac215]1125 objects_shipPipeline.resize();
[bb76950]1126
[b01b50c]1127 shipPipeline.updateDescriptorInfo(1, &objectBuffers_shipPipeline.infoSet, swapChainImages.size());
[a3cefaa]1128 }
1129
[b7fc3c2]1130 // TODO: Move ship position updates from the ui event handling code into this function
[a3cefaa]1131 for (size_t i = 0; i < shipObjects.size(); i++) {
[6486ba8]1132 SceneObject<ModelVertex>& obj = shipObjects[i];
1133 SSBO_ModelObject& objData = objects_shipPipeline.get(i);
[b7fc3c2]1134
[c1ec4f6]1135 objData.model = obj.model_transform * obj.model_base;
1136 obj.center = vec3(objData.model * vec4(0.0f, 0.0f, 0.0f, 1.0f));
[a3cefaa]1137 }
1138
[2f4ff8c]1139 VulkanUtils::copyDataToMappedMemory(device, uniforms_shipPipeline.data(), uniforms_shipPipeline.mapped(imageIndex),
1140 uniformBuffers_shipPipeline.memory[imageIndex],
1141 uniforms_shipPipeline.memorySize(), true);
[90880fb]1142
[2f4ff8c]1143 VulkanUtils::copyDataToMappedMemory(device, objects_shipPipeline.data(), objects_shipPipeline.mapped(imageIndex),
[b01b50c]1144 objectBuffers_shipPipeline.memory[imageIndex],
[2f4ff8c]1145 objects_shipPipeline.memorySize(), true);
[5ea0a37]1146
[6bac215]1147 if (objects_asteroidPipeline.resized) {
[b01b50c]1148 objects_asteroidPipeline.unmap(objectBuffers_asteroidPipeline.memory, device);
[2f4ff8c]1149
[b01b50c]1150 resizeBufferSet(objectBuffers_asteroidPipeline, objects_asteroidPipeline.memorySize(), resourceCommandPool,
[b7fc3c2]1151 graphicsQueue, true);
[6bac215]1152
[b01b50c]1153 objects_asteroidPipeline.map(objectBuffers_asteroidPipeline.memory, device);
[2f4ff8c]1154
[6bac215]1155 objects_asteroidPipeline.resize();
[bb76950]1156
[b01b50c]1157 asteroidPipeline.updateDescriptorInfo(1, &objectBuffers_asteroidPipeline.infoSet, swapChainImages.size());
[a3cefaa]1158 }
1159
[5ba732a]1160 for (size_t i = 0; i < asteroidObjects.size(); i++) {
[6486ba8]1161 SceneObject<ModelVertex>& obj = asteroidObjects[i];
1162 SSBO_Asteroid& objData = objects_asteroidPipeline.get(i);
[b7fc3c2]1163
1164 if (!objData.deleted) {
1165 vec3 objCenter = vec3(viewMat * vec4(obj.center, 1.0f));
1166
1167 if (objData.hp <= 0.0f) {
1168 objData.deleted = true;
1169
1170 // TODO: Optimize this so I don't recalculate the camera rotation every time
1171 // TODO: Also, avoid re-declaring cam_pitch
1172 float cam_pitch = -50.0f;
1173 mat4 pitch_mat = rotate(mat4(1.0f), radians(cam_pitch), vec3(1.0f, 0.0f, 0.0f));
1174 mat4 model_mat = translate(mat4(1.0f), obj.center) * pitch_mat;
1175
1176 addExplosion(model_mat, 0.5f, curTime);
1177
1178 this->score++;
1179 } else if ((objCenter.z - obj.radius) > -NEAR_CLIP) {
1180 objData.deleted = true;
1181 } else {
1182 obj.model_transform =
1183 translate(mat4(1.0f), vec3(0.0f, 0.0f, this->asteroidSpeed * this->elapsedTime)) *
1184 obj.model_transform;
1185 }
1186
1187 objData.model = obj.model_transform * obj.model_base;
1188 obj.center = vec3(objData.model * vec4(0.0f, 0.0f, 0.0f, 1.0f));
[5ba732a]1189 }
[0807aeb]1190 }
1191
[2f4ff8c]1192 VulkanUtils::copyDataToMappedMemory(device, uniforms_asteroidPipeline.data(), uniforms_asteroidPipeline.mapped(imageIndex),
1193 uniformBuffers_asteroidPipeline.memory[imageIndex],
1194 uniforms_asteroidPipeline.memorySize(), true);
[90880fb]1195
[2f4ff8c]1196 VulkanUtils::copyDataToMappedMemory(device, objects_asteroidPipeline.data(), objects_asteroidPipeline.mapped(imageIndex),
[b01b50c]1197 objectBuffers_asteroidPipeline.memory[imageIndex],
[2f4ff8c]1198 objects_asteroidPipeline.memorySize(), true);
[5ea0a37]1199
[6bac215]1200 if (objects_laserPipeline.resized) {
[b01b50c]1201 objects_laserPipeline.unmap(objectBuffers_laserPipeline.memory, device);
[2f4ff8c]1202
[b01b50c]1203 resizeBufferSet(objectBuffers_laserPipeline, objects_laserPipeline.memorySize(), resourceCommandPool,
[b7fc3c2]1204 graphicsQueue, true);
[6bac215]1205
[b01b50c]1206 objects_laserPipeline.map(objectBuffers_laserPipeline.memory, device);
[2f4ff8c]1207
[6bac215]1208 objects_laserPipeline.resize();
[bb76950]1209
[b01b50c]1210 laserPipeline.updateDescriptorInfo(1, &objectBuffers_laserPipeline.infoSet, swapChainImages.size());
[a3cefaa]1211 }
1212
[b7fc3c2]1213 if (leftLaserIdx != -1) {
1214 updateLaserTarget(leftLaserIdx);
1215 }
1216 if (rightLaserIdx != -1) {
1217 updateLaserTarget(rightLaserIdx);
1218 }
1219
[237cbec]1220 for (size_t i = 0; i < laserObjects.size(); i++) {
[6486ba8]1221 SceneObject<LaserVertex>& obj = laserObjects[i];
1222 SSBO_Laser& objData = objects_laserPipeline.get(i);
[b7fc3c2]1223
[c1ec4f6]1224 objData.model = obj.model_transform * obj.model_base;
1225 obj.center = vec3(objData.model * vec4(0.0f, 0.0f, 0.0f, 1.0f));
[237cbec]1226 }
1227
[2f4ff8c]1228 VulkanUtils::copyDataToMappedMemory(device, uniforms_laserPipeline.data(), uniforms_laserPipeline.mapped(imageIndex),
1229 uniformBuffers_laserPipeline.memory[imageIndex],
1230 uniforms_laserPipeline.memorySize(), true);
[90880fb]1231
[2f4ff8c]1232 VulkanUtils::copyDataToMappedMemory(device, objects_laserPipeline.data(), objects_laserPipeline.mapped(imageIndex),
[b01b50c]1233 objectBuffers_laserPipeline.memory[imageIndex],
[2f4ff8c]1234 objects_laserPipeline.memorySize(), true);
[5ea0a37]1235
[6bac215]1236 if (objects_explosionPipeline.resized) {
[b01b50c]1237 objects_explosionPipeline.unmap(objectBuffers_explosionPipeline.memory, device);
[2f4ff8c]1238
[b01b50c]1239 resizeBufferSet(objectBuffers_explosionPipeline, objects_explosionPipeline.memorySize(), resourceCommandPool,
[b7fc3c2]1240 graphicsQueue, true);
[6bac215]1241
[b01b50c]1242 objects_explosionPipeline.map(objectBuffers_explosionPipeline.memory, device);
[2f4ff8c]1243
[6bac215]1244 objects_explosionPipeline.resize();
[bb76950]1245
[b01b50c]1246 explosionPipeline.updateDescriptorInfo(1, &objectBuffers_explosionPipeline.infoSet, swapChainImages.size());
[a3cefaa]1247 }
1248
[4a9416a]1249 for (size_t i = 0; i < explosionObjects.size(); i++) {
[6486ba8]1250 SceneObject<ExplosionVertex>& obj = explosionObjects[i];
1251 SSBO_Explosion& objData = objects_explosionPipeline.get(i);
[b7fc3c2]1252
1253 if (!objData.deleted) {
1254 if (curTime > (objData.explosionStartTime + objData.explosionDuration)) {
1255 objData.deleted = true;
1256 }
1257 }
1258
[c1ec4f6]1259 objData.model = obj.model_transform * obj.model_base;
1260 obj.center = vec3(objData.model * vec4(0.0f, 0.0f, 0.0f, 1.0f));
[4a9416a]1261 }
1262
[2f4ff8c]1263 VulkanUtils::copyDataToMappedMemory(device, uniforms_explosionPipeline.data(), uniforms_explosionPipeline.mapped(imageIndex),
1264 uniformBuffers_explosionPipeline.memory[imageIndex],
1265 uniforms_explosionPipeline.memorySize(), true);
[90880fb]1266
[2f4ff8c]1267 VulkanUtils::copyDataToMappedMemory(device, objects_explosionPipeline.data(), objects_explosionPipeline.mapped(imageIndex),
[b01b50c]1268 objectBuffers_explosionPipeline.memory[imageIndex],
[2f4ff8c]1269 objects_explosionPipeline.memorySize(), true);
[8e02b6b]1270}
1271
[99d44b2]1272void VulkanGame::cleanup() {
[cefdf23]1273 // FIXME: We could wait on the Queue if we had the queue in wd-> (otherwise VulkanH functions can't use globals)
1274 //vkQueueWaitIdle(g_Queue);
[1cb64e6]1275 VKUTIL_CHECK_RESULT(vkDeviceWaitIdle(device), "failed to wait for device!");
[3f32dfd]1276
[cefdf23]1277 cleanupImGuiOverlay();
[3b7d497]1278
[c1c2021]1279 cleanupSwapChain();
1280
[4994692]1281 VulkanUtils::destroyVulkanImage(device, floorTextureImage);
[b8efa56]1282 // START UNREVIEWED SECTION
[237cbec]1283 VulkanUtils::destroyVulkanImage(device, laserTextureImage);
[e83b155]1284
1285 vkDestroySampler(device, textureSampler, nullptr);
1286
[b8777b7]1287 modelPipeline.cleanupBuffers();
[3782d66]1288 shipPipeline.cleanupBuffers();
[3e8cc8b]1289 asteroidPipeline.cleanupBuffers();
[237cbec]1290 laserPipeline.cleanupBuffers();
[4a9416a]1291 explosionPipeline.cleanupBuffers();
[b794178]1292
[b8efa56]1293 // END UNREVIEWED SECTION
1294
[3f32dfd]1295 vkDestroyCommandPool(device, resourceCommandPool, nullptr);
1296
[c1c2021]1297 vkDestroyDevice(device, nullptr);
[7865c5b]1298 vkDestroySurfaceKHR(instance, vulkanSurface, nullptr);
[c1c2021]1299
[c1d9b2a]1300 if (ENABLE_VALIDATION_LAYERS) {
1301 VulkanUtils::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
1302 }
[c1c2021]1303
[c1d9b2a]1304 vkDestroyInstance(instance, nullptr);
1305
[b6e60b4]1306 gui->destroyWindow();
1307 gui->shutdown();
[0df3c9a]1308 delete gui;
[c1d9b2a]1309}
1310
[c324d6a]1311void VulkanGame::createVulkanInstance(const vector<const char*>& validationLayers) {
[c1d9b2a]1312 if (ENABLE_VALIDATION_LAYERS && !VulkanUtils::checkValidationLayerSupport(validationLayers)) {
1313 throw runtime_error("validation layers requested, but not available!");
1314 }
1315
1316 VkApplicationInfo appInfo = {};
1317 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
1318 appInfo.pApplicationName = "Vulkan Game";
1319 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
1320 appInfo.pEngineName = "No Engine";
1321 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
1322 appInfo.apiVersion = VK_API_VERSION_1_0;
1323
1324 VkInstanceCreateInfo createInfo = {};
1325 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
1326 createInfo.pApplicationInfo = &appInfo;
1327
1328 vector<const char*> extensions = gui->getRequiredExtensions();
1329 if (ENABLE_VALIDATION_LAYERS) {
1330 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
1331 }
1332
1333 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
1334 createInfo.ppEnabledExtensionNames = extensions.data();
1335
1336 cout << endl << "Extensions:" << endl;
1337 for (const char* extensionName : extensions) {
1338 cout << extensionName << endl;
1339 }
1340 cout << endl;
1341
1342 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
1343 if (ENABLE_VALIDATION_LAYERS) {
1344 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
1345 createInfo.ppEnabledLayerNames = validationLayers.data();
1346
1347 populateDebugMessengerCreateInfo(debugCreateInfo);
1348 createInfo.pNext = &debugCreateInfo;
1349 } else {
1350 createInfo.enabledLayerCount = 0;
1351
1352 createInfo.pNext = nullptr;
1353 }
1354
1355 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
1356 throw runtime_error("failed to create instance!");
1357 }
1358}
1359
1360void VulkanGame::setupDebugMessenger() {
[c324d6a]1361 if (!ENABLE_VALIDATION_LAYERS) {
1362 return;
1363 }
[c1d9b2a]1364
1365 VkDebugUtilsMessengerCreateInfoEXT createInfo;
1366 populateDebugMessengerCreateInfo(createInfo);
1367
1368 if (VulkanUtils::createDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
1369 throw runtime_error("failed to set up debug messenger!");
1370 }
1371}
1372
1373void VulkanGame::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
1374 createInfo = {};
1375 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
1376 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;
1377 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;
1378 createInfo.pfnUserCallback = debugCallback;
1379}
1380
1381VKAPI_ATTR VkBool32 VKAPI_CALL VulkanGame::debugCallback(
1382 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
1383 VkDebugUtilsMessageTypeFlagsEXT messageType,
1384 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
1385 void* pUserData) {
1386 cerr << "validation layer: " << pCallbackData->pMessage << endl;
1387
[1cb64e6]1388 // TODO: Figure out what the return value means and if it should always be VK_FALSE
[c1d9b2a]1389 return VK_FALSE;
1390}
[90a424f]1391
1392void VulkanGame::createVulkanSurface() {
[7865c5b]1393 if (gui->createVulkanSurface(instance, &vulkanSurface) == RTWO_ERROR) {
[90a424f]1394 throw runtime_error("failed to create window surface!");
1395 }
1396}
1397
[fe5c3ba]1398void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
[90a424f]1399 uint32_t deviceCount = 0;
[c324d6a]1400 // TODO: Check VkResult
[90a424f]1401 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
1402
1403 if (deviceCount == 0) {
1404 throw runtime_error("failed to find GPUs with Vulkan support!");
1405 }
1406
1407 vector<VkPhysicalDevice> devices(deviceCount);
[c324d6a]1408 // TODO: Check VkResult
[90a424f]1409 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
1410
1411 cout << endl << "Graphics cards:" << endl;
1412 for (const VkPhysicalDevice& device : devices) {
[fe5c3ba]1413 if (isDeviceSuitable(device, deviceExtensions)) {
[90a424f]1414 physicalDevice = device;
1415 break;
1416 }
1417 }
1418 cout << endl;
1419
1420 if (physicalDevice == VK_NULL_HANDLE) {
1421 throw runtime_error("failed to find a suitable GPU!");
1422 }
1423}
1424
[6a39266]1425bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice, const vector<const char*>& deviceExtensions) {
[90a424f]1426 VkPhysicalDeviceProperties deviceProperties;
[fa9fa1c]1427 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
[90a424f]1428
1429 cout << "Device: " << deviceProperties.deviceName << endl;
1430
[187b0f5]1431 // TODO: Eventually, maybe let the user pick out of a set of GPUs in case the user does want to use
1432 // an integrated GPU. On my laptop, this function returns TRUE for the integrated GPU, but crashes
1433 // when trying to use it to render. Maybe I just need to figure out which other extensions and features
1434 // to check.
1435 if (deviceProperties.deviceType != VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
1436 return false;
1437 }
1438
[7865c5b]1439 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, vulkanSurface);
[fa9fa1c]1440 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
[90a424f]1441 bool swapChainAdequate = false;
1442
1443 if (extensionsSupported) {
[7865c5b]1444 vector<VkSurfaceFormatKHR> formats = VulkanUtils::querySwapChainFormats(physicalDevice, vulkanSurface);
1445 vector<VkPresentModeKHR> presentModes = VulkanUtils::querySwapChainPresentModes(physicalDevice, vulkanSurface);
[3f32dfd]1446
1447 swapChainAdequate = !formats.empty() && !presentModes.empty();
[90a424f]1448 }
1449
1450 VkPhysicalDeviceFeatures supportedFeatures;
[fa9fa1c]1451 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
[90a424f]1452
1453 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
[c1c2021]1454}
1455
[c324d6a]1456void VulkanGame::createLogicalDevice(const vector<const char*>& validationLayers,
1457 const vector<const char*>& deviceExtensions) {
[7865c5b]1458 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, vulkanSurface);
[c1c2021]1459
[6a39266]1460 if (!indices.isComplete()) {
1461 throw runtime_error("failed to find required queue families!");
1462 }
1463
1464 // TODO: Using separate graphics and present queues currently works, but I should verify that I'm
1465 // using them correctly to get the most benefit out of separate queues
1466
[b794178]1467 vector<VkDeviceQueueCreateInfo> queueCreateInfoList;
[c1c2021]1468 set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1469
1470 float queuePriority = 1.0f;
1471 for (uint32_t queueFamily : uniqueQueueFamilies) {
1472 VkDeviceQueueCreateInfo queueCreateInfo = {};
1473 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1474 queueCreateInfo.queueCount = 1;
[6a39266]1475 queueCreateInfo.queueFamilyIndex = queueFamily;
[c1c2021]1476 queueCreateInfo.pQueuePriorities = &queuePriority;
1477
[b794178]1478 queueCreateInfoList.push_back(queueCreateInfo);
[c1c2021]1479 }
1480
1481 VkPhysicalDeviceFeatures deviceFeatures = {};
1482 deviceFeatures.samplerAnisotropy = VK_TRUE;
1483
1484 VkDeviceCreateInfo createInfo = {};
1485 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
[c324d6a]1486
[b794178]1487 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfoList.size());
1488 createInfo.pQueueCreateInfos = queueCreateInfoList.data();
[c1c2021]1489
1490 createInfo.pEnabledFeatures = &deviceFeatures;
1491
1492 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
1493 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
1494
1495 // These fields are ignored by up-to-date Vulkan implementations,
1496 // but it's a good idea to set them for backwards compatibility
1497 if (ENABLE_VALIDATION_LAYERS) {
1498 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
1499 createInfo.ppEnabledLayerNames = validationLayers.data();
1500 } else {
1501 createInfo.enabledLayerCount = 0;
1502 }
1503
1504 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
1505 throw runtime_error("failed to create logical device!");
1506 }
1507
1508 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
1509 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
[502bd0b]1510}
1511
[3f32dfd]1512void VulkanGame::chooseSwapChainProperties() {
[7865c5b]1513 vector<VkSurfaceFormatKHR> availableFormats = VulkanUtils::querySwapChainFormats(physicalDevice, vulkanSurface);
1514 vector<VkPresentModeKHR> availablePresentModes = VulkanUtils::querySwapChainPresentModes(physicalDevice, vulkanSurface);
[502bd0b]1515
[3f32dfd]1516 swapChainSurfaceFormat = VulkanUtils::chooseSwapSurfaceFormat(availableFormats,
1517 { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM },
1518 VK_COLOR_SPACE_SRGB_NONLINEAR_KHR);
[502bd0b]1519
[3f32dfd]1520 vector<VkPresentModeKHR> presentModes{
1521 VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR
1522 };
1523 //vector<VkPresentModeKHR> presentModes{ VK_PRESENT_MODE_FIFO_KHR };
1524
1525 swapChainPresentMode = VulkanUtils::chooseSwapPresentMode(availablePresentModes, presentModes);
1526
1527 cout << "[vulkan] Selected PresentMode = " << swapChainPresentMode << endl;
1528
[7865c5b]1529 VkSurfaceCapabilitiesKHR capabilities = VulkanUtils::querySwapChainCapabilities(physicalDevice, vulkanSurface);
[3f32dfd]1530
1531 if (swapChainPresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
1532 swapChainMinImageCount = 3;
1533 } else if (swapChainPresentMode == VK_PRESENT_MODE_FIFO_KHR || swapChainPresentMode == VK_PRESENT_MODE_FIFO_RELAXED_KHR) {
1534 swapChainMinImageCount = 2;
1535 } else if (swapChainPresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
1536 swapChainMinImageCount = 1;
1537 } else {
1538 throw runtime_error("unexpected present mode!");
[502bd0b]1539 }
1540
[3f32dfd]1541 if (swapChainMinImageCount < capabilities.minImageCount) {
1542 swapChainMinImageCount = capabilities.minImageCount;
1543 } else if (capabilities.maxImageCount != 0 && swapChainMinImageCount > capabilities.maxImageCount) {
1544 swapChainMinImageCount = capabilities.maxImageCount;
1545 }
1546}
1547
1548void VulkanGame::createSwapChain() {
[7865c5b]1549 VkSurfaceCapabilitiesKHR capabilities = VulkanUtils::querySwapChainCapabilities(physicalDevice, vulkanSurface);
[3f32dfd]1550
1551 swapChainExtent = VulkanUtils::chooseSwapExtent(capabilities, gui->getWindowWidth(), gui->getWindowHeight());
1552
[502bd0b]1553 VkSwapchainCreateInfoKHR createInfo = {};
1554 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
[7865c5b]1555 createInfo.surface = vulkanSurface;
[3f32dfd]1556 createInfo.minImageCount = swapChainMinImageCount;
1557 createInfo.imageFormat = swapChainSurfaceFormat.format;
1558 createInfo.imageColorSpace = swapChainSurfaceFormat.colorSpace;
1559 createInfo.imageExtent = swapChainExtent;
[502bd0b]1560 createInfo.imageArrayLayers = 1;
1561 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1562
[3f32dfd]1563 // TODO: Maybe save this result so I don't have to recalculate it every time
[7865c5b]1564 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, vulkanSurface);
[502bd0b]1565 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1566
1567 if (indices.graphicsFamily != indices.presentFamily) {
1568 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
1569 createInfo.queueFamilyIndexCount = 2;
1570 createInfo.pQueueFamilyIndices = queueFamilyIndices;
[f94eea9]1571 } else {
[502bd0b]1572 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1573 createInfo.queueFamilyIndexCount = 0;
1574 createInfo.pQueueFamilyIndices = nullptr;
1575 }
1576
[3f32dfd]1577 createInfo.preTransform = capabilities.currentTransform;
[502bd0b]1578 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
[3f32dfd]1579 createInfo.presentMode = swapChainPresentMode;
[502bd0b]1580 createInfo.clipped = VK_TRUE;
1581 createInfo.oldSwapchain = VK_NULL_HANDLE;
1582
1583 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
1584 throw runtime_error("failed to create swap chain!");
1585 }
1586
[3f32dfd]1587 if (vkGetSwapchainImagesKHR(device, swapChain, &swapChainImageCount, nullptr) != VK_SUCCESS) {
1588 throw runtime_error("failed to get swap chain image count!");
1589 }
[502bd0b]1590
[3f32dfd]1591 swapChainImages.resize(swapChainImageCount);
1592 if (vkGetSwapchainImagesKHR(device, swapChain, &swapChainImageCount, swapChainImages.data()) != VK_SUCCESS) {
1593 throw runtime_error("failed to get swap chain images!");
1594 }
[f94eea9]1595}
1596
1597void VulkanGame::createImageViews() {
[3f32dfd]1598 swapChainImageViews.resize(swapChainImageCount);
[f94eea9]1599
[3f32dfd]1600 for (size_t i = 0; i < swapChainImageCount; i++) {
1601 swapChainImageViews[i] = VulkanUtils::createImageView(device, swapChainImages[i], swapChainSurfaceFormat.format,
[f94eea9]1602 VK_IMAGE_ASPECT_COLOR_BIT);
1603 }
1604}
1605
[cefdf23]1606void VulkanGame::createResourceCommandPool() {
1607 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, vulkanSurface);
1608
1609 VkCommandPoolCreateInfo poolInfo = {};
1610 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1611 poolInfo.queueFamilyIndex = indices.graphicsFamily.value();
1612 poolInfo.flags = 0;
1613
1614 if (vkCreateCommandPool(device, &poolInfo, nullptr, &resourceCommandPool) != VK_SUCCESS) {
1615 throw runtime_error("failed to create resource command pool!");
1616 }
1617}
1618
1619void VulkanGame::createImageResources() {
1620 VulkanUtils::createDepthImage(device, physicalDevice, resourceCommandPool, findDepthFormat(), swapChainExtent,
[8dcbf62]1621 depthImage, graphicsQueue);
[cefdf23]1622
1623 createTextureSampler();
1624
1625 // TODO: Move all images/textures somewhere into the assets folder
1626
1627 VulkanUtils::createVulkanImageFromFile(device, physicalDevice, resourceCommandPool, "textures/texture.jpg",
1628 floorTextureImage, graphicsQueue);
1629
1630 floorTextureImageDescriptor = {};
1631 floorTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1632 floorTextureImageDescriptor.imageView = floorTextureImage.imageView;
1633 floorTextureImageDescriptor.sampler = textureSampler;
1634
1635 VulkanUtils::createVulkanImageFromFile(device, physicalDevice, resourceCommandPool, "textures/laser.png",
1636 laserTextureImage, graphicsQueue);
1637
1638 laserTextureImageDescriptor = {};
1639 laserTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1640 laserTextureImageDescriptor.imageView = laserTextureImage.imageView;
1641 laserTextureImageDescriptor.sampler = textureSampler;
1642}
1643
1644VkFormat VulkanGame::findDepthFormat() {
1645 return VulkanUtils::findSupportedFormat(
1646 physicalDevice,
[a3cefaa]1647 { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D24_UNORM_S8_UINT },
[cefdf23]1648 VK_IMAGE_TILING_OPTIMAL,
1649 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
1650 );
1651}
1652
[6fc24c7]1653void VulkanGame::createRenderPass() {
1654 VkAttachmentDescription colorAttachment = {};
[3f32dfd]1655 colorAttachment.format = swapChainSurfaceFormat.format;
[6fc24c7]1656 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
1657 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1658 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1659 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1660 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1661 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1662 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1663
1664 VkAttachmentReference colorAttachmentRef = {};
1665 colorAttachmentRef.attachment = 0;
1666 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1667
1668 VkAttachmentDescription depthAttachment = {};
1669 depthAttachment.format = findDepthFormat();
1670 depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
1671 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1672 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1673 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1674 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1675 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1676 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1677
1678 VkAttachmentReference depthAttachmentRef = {};
1679 depthAttachmentRef.attachment = 1;
1680 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1681
1682 VkSubpassDescription subpass = {};
1683 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1684 subpass.colorAttachmentCount = 1;
1685 subpass.pColorAttachments = &colorAttachmentRef;
1686 subpass.pDepthStencilAttachment = &depthAttachmentRef;
1687
1688 VkSubpassDependency dependency = {};
1689 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
1690 dependency.dstSubpass = 0;
1691 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1692 dependency.srcAccessMask = 0;
1693 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1694 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1695
1696 array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
1697 VkRenderPassCreateInfo renderPassInfo = {};
1698 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1699 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1700 renderPassInfo.pAttachments = attachments.data();
1701 renderPassInfo.subpassCount = 1;
1702 renderPassInfo.pSubpasses = &subpass;
1703 renderPassInfo.dependencyCount = 1;
1704 renderPassInfo.pDependencies = &dependency;
1705
1706 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
1707 throw runtime_error("failed to create render pass!");
1708 }
1709}
1710
[9c0a614]1711void VulkanGame::createCommandPools() {
1712 commandPools.resize(swapChainImageCount);
[fa9fa1c]1713
[7865c5b]1714 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, vulkanSurface);
[9c0a614]1715
1716 for (size_t i = 0; i < swapChainImageCount; i++) {
1717 VkCommandPoolCreateInfo poolInfo = {};
1718 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1719 poolInfo.queueFamilyIndex = indices.graphicsFamily.value();
1720 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
[fa9fa1c]1721
[9c0a614]1722 VKUTIL_CHECK_RESULT(vkCreateCommandPool(device, &poolInfo, nullptr, &commandPools[i]),
1723 "failed to create graphics command pool!");
[fa9fa1c]1724 }
1725}
1726
[b794178]1727void VulkanGame::createTextureSampler() {
1728 VkSamplerCreateInfo samplerInfo = {};
1729 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
1730 samplerInfo.magFilter = VK_FILTER_LINEAR;
1731 samplerInfo.minFilter = VK_FILTER_LINEAR;
1732
1733 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1734 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1735 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1736
1737 samplerInfo.anisotropyEnable = VK_TRUE;
1738 samplerInfo.maxAnisotropy = 16;
1739 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
1740 samplerInfo.unnormalizedCoordinates = VK_FALSE;
1741 samplerInfo.compareEnable = VK_FALSE;
1742 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
1743 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
1744 samplerInfo.mipLodBias = 0.0f;
1745 samplerInfo.minLod = 0.0f;
1746 samplerInfo.maxLod = 0.0f;
1747
[cefdf23]1748 VKUTIL_CHECK_RESULT(vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler),
1749 "failed to create texture sampler!");
[b794178]1750}
1751
[603b5bc]1752void VulkanGame::createFramebuffers() {
[3f32dfd]1753 swapChainFramebuffers.resize(swapChainImageCount);
1754
1755 VkFramebufferCreateInfo framebufferInfo = {};
1756 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1757 framebufferInfo.renderPass = renderPass;
1758 framebufferInfo.width = swapChainExtent.width;
1759 framebufferInfo.height = swapChainExtent.height;
1760 framebufferInfo.layers = 1;
[603b5bc]1761
[3f32dfd]1762 for (uint32_t i = 0; i < swapChainImageCount; i++) {
[603b5bc]1763 array<VkImageView, 2> attachments = {
1764 swapChainImageViews[i],
1765 depthImage.imageView
1766 };
1767
1768 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1769 framebufferInfo.pAttachments = attachments.data();
1770
1771 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
1772 throw runtime_error("failed to create framebuffer!");
1773 }
1774 }
1775}
1776
1777void VulkanGame::createCommandBuffers() {
[3f32dfd]1778 commandBuffers.resize(swapChainImageCount);
[603b5bc]1779
[9c0a614]1780 for (size_t i = 0; i < swapChainImageCount; i++) {
1781 VkCommandBufferAllocateInfo allocInfo = {};
1782 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1783 allocInfo.commandPool = commandPools[i];
1784 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1785 allocInfo.commandBufferCount = 1;
1786
1787 if (vkAllocateCommandBuffers(device, &allocInfo, &commandBuffers[i]) != VK_SUCCESS) {
1788 throw runtime_error("failed to allocate command buffer!");
1789 }
[603b5bc]1790 }
1791}
1792
[cefdf23]1793void VulkanGame::createSyncObjects() {
1794 imageAcquiredSemaphores.resize(swapChainImageCount);
1795 renderCompleteSemaphores.resize(swapChainImageCount);
1796 inFlightFences.resize(swapChainImageCount);
1797
1798 VkSemaphoreCreateInfo semaphoreInfo = {};
1799 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1800
1801 VkFenceCreateInfo fenceInfo = {};
1802 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1803 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1804
1805 for (size_t i = 0; i < swapChainImageCount; i++) {
[5081b9a]1806 VKUTIL_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAcquiredSemaphores[i]),
1807 "failed to create image acquired sempahore for a frame!");
[cefdf23]1808
[5081b9a]1809 VKUTIL_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderCompleteSemaphores[i]),
1810 "failed to create render complete sempahore for a frame!");
[cefdf23]1811
[5081b9a]1812 VKUTIL_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]),
1813 "failed to create fence for a frame!");
[cefdf23]1814 }
1815}
1816
[ea2b4dc]1817void VulkanGame::renderFrame(ImDrawData* draw_data) {
[4e2c709]1818 VkResult result = vkAcquireNextImageKHR(device, swapChain, numeric_limits<uint64_t>::max(),
1819 imageAcquiredSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
1820
[7734042]1821 if (result == VK_SUBOPTIMAL_KHR) {
1822 shouldRecreateSwapChain = true;
1823 } else if (result == VK_ERROR_OUT_OF_DATE_KHR) {
1824 shouldRecreateSwapChain = true;
[4e2c709]1825 return;
[7734042]1826 } else {
1827 VKUTIL_CHECK_RESULT(result, "failed to acquire swap chain image!");
[4e2c709]1828 }
1829
[7734042]1830 VKUTIL_CHECK_RESULT(
1831 vkWaitForFences(device, 1, &inFlightFences[imageIndex], VK_TRUE, numeric_limits<uint64_t>::max()),
1832 "failed waiting for fence!");
1833
1834 VKUTIL_CHECK_RESULT(vkResetFences(device, 1, &inFlightFences[imageIndex]),
1835 "failed to reset fence!");
[4e2c709]1836
[1cb64e6]1837 VKUTIL_CHECK_RESULT(vkResetCommandPool(device, commandPools[imageIndex], 0),
1838 "failed to reset command pool!");
1839
1840 VkCommandBufferBeginInfo beginInfo = {};
1841 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
[7734042]1842 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
[1cb64e6]1843
[7734042]1844 VKUTIL_CHECK_RESULT(vkBeginCommandBuffer(commandBuffers[imageIndex], &beginInfo),
1845 "failed to begin recording command buffer!");
[1cb64e6]1846
1847 VkRenderPassBeginInfo renderPassInfo = {};
1848 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1849 renderPassInfo.renderPass = renderPass;
1850 renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
1851 renderPassInfo.renderArea.offset = { 0, 0 };
1852 renderPassInfo.renderArea.extent = swapChainExtent;
1853
1854 array<VkClearValue, 2> clearValues = {};
1855 clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
1856 clearValues[1].depthStencil = { 1.0f, 0 };
1857
1858 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
1859 renderPassInfo.pClearValues = clearValues.data();
1860
1861 vkCmdBeginRenderPass(commandBuffers[imageIndex], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1862
[20e4c2b]1863 // TODO: Find a more elegant, per-screen solution for this
1864 if (currentRenderScreenFn == &VulkanGame::renderGameScreen) {
[567fa88]1865 modelPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex, {});
1866 shipPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex, {});
1867 asteroidPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex, {});
1868 laserPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex, {});
1869 explosionPipeline.createRenderCommands(commandBuffers[imageIndex], imageIndex, {});
[20e4c2b]1870 }
[1cb64e6]1871
[ea2b4dc]1872 ImGui_ImplVulkan_RenderDrawData(draw_data, commandBuffers[imageIndex]);
[1cb64e6]1873
1874 vkCmdEndRenderPass(commandBuffers[imageIndex]);
1875
[7734042]1876 VKUTIL_CHECK_RESULT(vkEndCommandBuffer(commandBuffers[imageIndex]),
1877 "failed to record command buffer!");
[1cb64e6]1878
[4e2c709]1879 VkSemaphore waitSemaphores[] = { imageAcquiredSemaphores[currentFrame] };
1880 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
1881 VkSemaphore signalSemaphores[] = { renderCompleteSemaphores[currentFrame] };
1882
1883 VkSubmitInfo submitInfo = {};
1884 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1885 submitInfo.waitSemaphoreCount = 1;
1886 submitInfo.pWaitSemaphores = waitSemaphores;
1887 submitInfo.pWaitDstStageMask = waitStages;
1888 submitInfo.commandBufferCount = 1;
1889 submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
1890 submitInfo.signalSemaphoreCount = 1;
1891 submitInfo.pSignalSemaphores = signalSemaphores;
1892
[1cb64e6]1893 VKUTIL_CHECK_RESULT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[imageIndex]),
1894 "failed to submit draw command buffer!");
[4e2c709]1895}
1896
1897void VulkanGame::presentFrame() {
1898 VkSemaphore signalSemaphores[] = { renderCompleteSemaphores[currentFrame] };
1899
1900 VkPresentInfoKHR presentInfo = {};
1901 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1902 presentInfo.waitSemaphoreCount = 1;
1903 presentInfo.pWaitSemaphores = signalSemaphores;
1904 presentInfo.swapchainCount = 1;
1905 presentInfo.pSwapchains = &swapChain;
1906 presentInfo.pImageIndices = &imageIndex;
1907 presentInfo.pResults = nullptr;
1908
1909 VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo);
1910
[1cb64e6]1911 if (result == VK_SUBOPTIMAL_KHR) {
1912 shouldRecreateSwapChain = true;
1913 } else if (result == VK_ERROR_OUT_OF_DATE_KHR) {
1914 shouldRecreateSwapChain = true;
1915 return;
1916 } else {
1917 VKUTIL_CHECK_RESULT(result, "failed to present swap chain image!");
[4e2c709]1918 }
1919
1920 currentFrame = (currentFrame + 1) % swapChainImageCount;
1921}
1922
[cefdf23]1923void VulkanGame::initImGuiOverlay() {
[3b7d497]1924 vector<VkDescriptorPoolSize> pool_sizes{
1925 { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
1926 { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
1927 { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
1928 { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
1929 { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
1930 { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
1931 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
1932 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
1933 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
1934 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
1935 { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
1936 };
1937
1938 VkDescriptorPoolCreateInfo pool_info = {};
1939 pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
1940 pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
1941 pool_info.maxSets = 1000 * pool_sizes.size();
1942 pool_info.poolSizeCount = static_cast<uint32_t>(pool_sizes.size());
1943 pool_info.pPoolSizes = pool_sizes.data();
[cefdf23]1944
1945 VKUTIL_CHECK_RESULT(vkCreateDescriptorPool(device, &pool_info, nullptr, &imguiDescriptorPool),
1946 "failed to create IMGUI descriptor pool!");
1947
1948 // TODO: Do this in one place and save it instead of redoing it every time I need a queue family index
1949 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, vulkanSurface);
1950
1951 // Setup Dear ImGui context
1952 IMGUI_CHECKVERSION();
1953 ImGui::CreateContext();
1954 ImGuiIO& io = ImGui::GetIO();
1955 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
1956 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
1957
1958 // Setup Dear ImGui style
1959 ImGui::StyleColorsDark();
1960 //ImGui::StyleColorsClassic();
1961
1962 // Setup Platform/Renderer bindings
1963 ImGui_ImplSDL2_InitForVulkan(window);
1964 ImGui_ImplVulkan_InitInfo init_info = {};
1965 init_info.Instance = instance;
1966 init_info.PhysicalDevice = physicalDevice;
1967 init_info.Device = device;
1968 init_info.QueueFamily = indices.graphicsFamily.value();
1969 init_info.Queue = graphicsQueue;
1970 init_info.DescriptorPool = imguiDescriptorPool;
1971 init_info.Allocator = nullptr;
1972 init_info.MinImageCount = swapChainMinImageCount;
1973 init_info.ImageCount = swapChainImageCount;
1974 init_info.CheckVkResultFn = check_imgui_vk_result;
1975 ImGui_ImplVulkan_Init(&init_info, renderPass);
1976
1977 // Load Fonts
1978 // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
1979 // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
1980 // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
1981 // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
1982 // - Read 'docs/FONTS.md' for more instructions and details.
1983 // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
1984 //io.Fonts->AddFontDefault();
1985 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
1986 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
1987 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
1988 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
1989 //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
1990 //assert(font != NULL);
1991
1992 // Upload Fonts
1993 {
1994 VkCommandBuffer commandBuffer = VulkanUtils::beginSingleTimeCommands(device, resourceCommandPool);
1995
1996 ImGui_ImplVulkan_CreateFontsTexture(commandBuffer);
1997
1998 VulkanUtils::endSingleTimeCommands(device, resourceCommandPool, commandBuffer, graphicsQueue);
1999
2000 ImGui_ImplVulkan_DestroyFontUploadObjects();
[3b7d497]2001 }
2002}
2003
[cefdf23]2004void VulkanGame::cleanupImGuiOverlay() {
2005 ImGui_ImplVulkan_Shutdown();
2006 ImGui_ImplSDL2_Shutdown();
2007 ImGui::DestroyContext();
2008
[3b7d497]2009 vkDestroyDescriptorPool(device, imguiDescriptorPool, nullptr);
2010}
2011
[8aa4888]2012void VulkanGame::createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags usages, VkMemoryPropertyFlags properties,
[c163d81]2013 BufferSet& set) {
[8aa4888]2014 set.usages = usages;
2015 set.properties = properties;
2016
[c163d81]2017 set.buffers.resize(swapChainImageCount);
2018 set.memory.resize(swapChainImageCount);
2019 set.infoSet.resize(swapChainImageCount);
[cefdf23]2020
2021 for (size_t i = 0; i < swapChainImageCount; i++) {
[8aa4888]2022 VulkanUtils::createBuffer(device, physicalDevice, bufferSize, usages, properties, set.buffers[i], set.memory[i]);
[cefdf23]2023
[c163d81]2024 set.infoSet[i].buffer = set.buffers[i];
2025 set.infoSet[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
2026 set.infoSet[i].range = bufferSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
[cefdf23]2027 }
2028}
2029
[bb76950]2030void VulkanGame::resizeBufferSet(BufferSet& set, VkDeviceSize newSize, VkCommandPool commandPool,
2031 VkQueue graphicsQueue, bool copyData) {
2032 for (size_t i = 0; i < set.buffers.size(); i++) {
2033 VkBuffer newBuffer;
2034 VkDeviceMemory newMemory;
2035
2036 VulkanUtils::createBuffer(device, physicalDevice, newSize, set.usages, set.properties, newBuffer, newMemory);
2037
2038 if (copyData) {
2039 VulkanUtils::copyBuffer(device, commandPool, set.buffers[i], newBuffer, 0, 0, set.infoSet[i].range,
2040 graphicsQueue);
2041 }
2042
2043 vkDestroyBuffer(device, set.buffers[i], nullptr);
2044 vkFreeMemory(device, set.memory[i], nullptr);
2045
2046 set.buffers[i] = newBuffer;
2047 set.memory[i] = newMemory;
2048
2049 set.infoSet[i].buffer = set.buffers[i];
2050 set.infoSet[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
2051 set.infoSet[i].range = newSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
2052 }
2053}
2054
[52a02e6]2055void VulkanGame::addLaser(vec3 start, vec3 end, vec3 color, float width) {
[1f81ecc]2056 vec3 ray = end - start;
2057 float length = glm::length(ray);
2058
[6486ba8]2059 SceneObject<LaserVertex>& laser = addObject(laserObjects, laserPipeline,
[1f81ecc]2060 addObjectIndex<LaserVertex>(laserObjects.size(), {
2061 {{ width / 2, 0.0f, -width / 2 }, {1.0f, 0.5f }},
2062 {{-width / 2, 0.0f, -width / 2 }, {0.0f, 0.5f }},
2063 {{-width / 2, 0.0f, 0.0f }, {0.0f, 0.0f }},
2064 {{ width / 2, 0.0f, 0.0f }, {1.0f, 0.0f }},
[3950236]2065 {{ width / 2, 0.0f, -length + width / 2}, {1.0f, 0.51f}},
[1f81ecc]2066 {{-width / 2, 0.0f, -length + width / 2}, {0.0f, 0.51f}},
2067 {{ width / 2, 0.0f, -length, }, {1.0f, 1.0f }},
2068 {{-width / 2, 0.0f, -length }, {0.0f, 1.0f }}
2069 }), {
2070 0, 1, 2, 0, 2, 3,
2071 4, 5, 1, 4, 1, 0,
2072 6, 7, 5, 6, 5, 4
[8dcbf62]2073 }, objects_laserPipeline, {
[1f81ecc]2074 mat4(1.0f),
2075 color,
2076 false
[1abebc1]2077 });
[996dd3e]2078
[1f81ecc]2079 float xAxisRotation = asin(ray.y / length);
2080 float yAxisRotation = atan2(-ray.x, -ray.z);
2081
2082 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
2083 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
2084 vec4(0.0f, 1.0f, 0.0f, 1.0f));
2085
2086 // To project point P onto line AB:
2087 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
2088 vec3 projOnLaser = start + glm::dot(this->cam_pos - start, ray) / (length * length) * ray;
2089 vec3 laserToCam = this->cam_pos - projOnLaser;
2090
2091 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
2092
[3950236]2093 laser.targetAsteroid = nullptr;
2094
[1f81ecc]2095 laser.model_base =
2096 rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
2097
2098 laser.model_transform =
2099 translate(mat4(1.0f), start) *
2100 rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
2101 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f));
2102}
2103
2104void VulkanGame::translateLaser(size_t index, const vec3& translation) {
[6486ba8]2105 SceneObject<LaserVertex>& laser = laserObjects[index];
[1f81ecc]2106
2107 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
2108 // and then re-used here
2109
2110 vec3 start = vec3(laser.model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
2111 vec3 end = vec3(laser.model_transform * vec4(0.0f, 0.0f, laser.vertices[6].pos.z, 1.0f));
2112
2113 vec3 ray = end - start;
2114 float length = glm::length(ray);
2115
2116 float xAxisRotation = asin(ray.y / length);
2117 float yAxisRotation = atan2(-ray.x, -ray.z);
2118
2119 vec3 normal(rotate(mat4(1.0f), yAxisRotation, vec3(0.0f, 1.0f, 0.0f)) *
2120 rotate(mat4(1.0f), xAxisRotation, vec3(1.0f, 0.0f, 0.0f)) *
2121 vec4(0.0f, 1.0f, 0.0f, 1.0f));
2122
2123 // To project point P onto line AB:
2124 // projection = A + dot(AP,AB) / dot(AB,AB) * AB
2125 vec3 projOnLaser = start + glm::dot(cam_pos - start, ray) / (length*length) * ray;
2126 vec3 laserToCam = cam_pos - projOnLaser;
2127
2128 float zAxisRotation = -atan2(glm::dot(glm::cross(normal, laserToCam), glm::normalize(ray)), glm::dot(normal, laserToCam));
2129
2130 laser.model_base = rotate(mat4(1.0f), zAxisRotation, vec3(0.0f, 0.0f, 1.0f));
2131 laser.model_transform = translate(mat4(1.0f), translation) * laser.model_transform;
2132}
2133
[3950236]2134void VulkanGame::updateLaserTarget(size_t index) {
[6486ba8]2135 SceneObject<LaserVertex>& laser = laserObjects[index];
[3950236]2136
2137 // TODO: A lot of the values calculated here can be calculated once and saved when the laser is created,
2138 // and then re-used here
2139
2140 vec3 start = vec3(laser.model_transform * vec4(0.0f, 0.0f, 0.0f, 1.0f));
2141 vec3 end = vec3(laser.model_transform * vec4(0.0f, 0.0f, laser.vertices[6].pos.z, 1.0f));
2142
2143 vec3 intersection(0.0f), closestIntersection(0.0f);
[6486ba8]2144 SceneObject<ModelVertex>* closestAsteroid = nullptr;
[3950236]2145 unsigned int closestAsteroidIndex = -1;
2146
[6486ba8]2147 for (int i = 0; i < asteroidObjects.size(); i++) {
2148 if (!objects_asteroidPipeline.get(i).deleted &&
2149 getLaserAndAsteroidIntersection(asteroidObjects[i], start, end, intersection)) {
[3950236]2150 // TODO: Implement a more generic algorithm for testing the closest object by getting the distance between the points
2151 // TODO: Also check which intersection is close to the start of the laser. This would make the algorithm work
2152 // regardless of which way -Z is pointing
2153 if (closestAsteroid == nullptr || intersection.z > closestIntersection.z) {
2154 // TODO: At this point, find the real intersection of the laser with one of the asteroid's sides
2155 closestAsteroid = &asteroidObjects[i];
2156 closestIntersection = intersection;
2157 closestAsteroidIndex = i;
2158 }
2159 }
2160 }
2161
2162 float width = laser.vertices[0].pos.x - laser.vertices[1].pos.x;
2163
2164 if (laser.targetAsteroid != closestAsteroid) {
[7297892]2165 if (laser.targetAsteroid != nullptr) {
2166 if (index == leftLaserIdx && leftLaserEffect != nullptr) {
2167 leftLaserEffect->deleted = true;
2168 } else if (index == rightLaserIdx && rightLaserEffect != nullptr) {
2169 rightLaserEffect->deleted = true;
2170 }
2171 }
2172
[6486ba8]2173 EffectOverTime<SSBO_Asteroid>* eot = nullptr;
[7297892]2174
2175 if (closestAsteroid != nullptr) {
[6486ba8]2176 // TODO: Figure out if I should use a smart pointer instead
2177 eot = new EffectOverTime(objects_asteroidPipeline, closestAsteroidIndex, offset_of(&SSBO_Asteroid::hp),
2178 curTime, -20.0f);
[7297892]2179 effects.push_back(eot);
2180 }
2181
2182 if (index == leftLaserIdx) {
2183 leftLaserEffect = eot;
2184 } else if (index == rightLaserIdx) {
2185 rightLaserEffect = eot;
2186 }
2187
[3950236]2188 laser.targetAsteroid = closestAsteroid;
2189 }
2190
2191 // Make the laser go past the end of the screen if it doesn't hit anything
2192 float length = closestAsteroid == nullptr ? 5.24f : glm::length(closestIntersection - start);
2193
2194 laser.vertices[4].pos.z = -length + width / 2;
2195 laser.vertices[5].pos.z = -length + width / 2;
2196 laser.vertices[6].pos.z = -length;
2197 laser.vertices[7].pos.z = -length;
2198
[5192672]2199 // TODO: Consider if I want to set a flag and do this update in updateScene() instead
[6486ba8]2200 updateObjectVertices(laserPipeline, laser, index);
[3950236]2201}
2202
2203// TODO: Determine if I should pass start and end by reference or value since they don't get changed
2204// Probably use const reference
[6486ba8]2205bool VulkanGame::getLaserAndAsteroidIntersection(SceneObject<ModelVertex>& asteroid, vec3& start, vec3& end,
2206 vec3& intersection) {
[3950236]2207 /*
2208 ### LINE EQUATIONS ###
2209 x = x1 + u * (x2 - x1)
2210 y = y1 + u * (y2 - y1)
2211 z = z1 + u * (z2 - z1)
2212
2213 ### SPHERE EQUATION ###
2214 (x - x3)^2 + (y - y3)^2 + (z - z3)^2 = r^2
2215
2216 ### QUADRATIC EQUATION TO SOLVE ###
2217 a*u^2 + b*u + c = 0
2218 WHERE THE CONSTANTS ARE
2219 a = (x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2
2220 b = 2*( (x2 - x1)*(x1 - x3) + (y2 - y1)*(y1 - y3) + (z2 - z1)*(z1 - z3) )
2221 c = x3^2 + y3^2 + z3^2 + x1^2 + y1^2 + z1^2 - 2(x3*x1 + y3*y1 + z3*z1) - r^2
2222
2223 u = (-b +- sqrt(b^2 - 4*a*c)) / 2a
2224
2225 If the value under the root is >= 0, we got an intersection
2226 If the value > 0, there are two solutions. Take the one closer to 0, since that's the
2227 one closer to the laser start point
2228 */
2229
2230 vec3& center = asteroid.center;
2231
2232 float a = pow(end.x - start.x, 2) + pow(end.y - start.y, 2) + pow(end.z - start.z, 2);
2233 float b = 2 * ((start.x - end.x) * (start.x - center.x) + (end.y - start.y) * (start.y - center.y) +
2234 (end.z - start.z) * (start.z - center.z));
2235 float c = pow(center.x, 2) + pow(center.y, 2) + pow(center.z, 2) + pow(start.x, 2) + pow(start.y, 2) +
2236 pow(start.z, 2) - 2 * (center.x * start.x + center.y * start.y + center.z * start.z) -
2237 pow(asteroid.radius, 2);
2238 float discriminant = pow(b, 2) - 4 * a * c;
2239
2240 if (discriminant >= 0.0f) {
2241 // In this case, the negative root will always give the point closer to the laser start point
2242 float u = (-b - sqrt(discriminant)) / (2 * a);
2243
2244 // Check that the intersection is within the line segment corresponding to the laser
2245 if (0.0f <= u && u <= 1.0f) {
2246 intersection = start + u * (end - start);
2247 return true;
2248 }
2249 }
2250
2251 return false;
2252}
2253
[4a9416a]2254void VulkanGame::addExplosion(mat4 model_mat, float duration, float cur_time) {
2255 vector<ExplosionVertex> vertices;
2256 vertices.reserve(EXPLOSION_PARTICLE_COUNT);
2257
2258 float particle_start_time = 0.0f;
2259
2260 for (int i = 0; i < EXPLOSION_PARTICLE_COUNT; i++) {
2261 float randx = ((float)rand() / (float)RAND_MAX) - 0.5f;
2262 float randy = ((float)rand() / (float)RAND_MAX) - 0.5f;
2263
2264 vertices.push_back({ vec3(randx, randy, 0.0f), particle_start_time});
2265
2266 particle_start_time += .01f;
2267 // TODO: Get this working
2268 // particle_start_time += 1.0f * EXPLOSION_PARTICLE_COUNT / duration
2269 }
2270
2271 // Fill the indices with the the first EXPLOSION_PARTICLE_COUNT ints
2272 vector<uint16_t> indices(EXPLOSION_PARTICLE_COUNT);
2273 iota(indices.begin(), indices.end(), 0);
2274
[6486ba8]2275 SceneObject<ExplosionVertex>& explosion = addObject(explosionObjects, explosionPipeline,
[8dcbf62]2276 addObjectIndex(explosionObjects.size(), vertices), indices, objects_explosionPipeline, {
[4a9416a]2277 mat4(1.0f),
2278 cur_time,
2279 duration,
2280 false
[1abebc1]2281 });
[996dd3e]2282
[4a9416a]2283 explosion.model_base = model_mat;
2284 explosion.model_transform = mat4(1.0f);
2285}
2286
[d2d9286]2287void VulkanGame::recreateSwapChain() {
[ce9dc9f]2288 if (vkDeviceWaitIdle(device) != VK_SUCCESS) {
2289 throw runtime_error("failed to wait for device!");
2290 }
[d2d9286]2291
[0ae182f]2292 cleanupSwapChain();
2293
2294 createSwapChain();
2295 createImageViews();
[9c0a614]2296
2297 // The depth buffer does need to be recreated with the swap chain since its dimensions depend on the window size
2298 // and resizing the window is a common reason to recreate the swapchain
[3f32dfd]2299 VulkanUtils::createDepthImage(device, physicalDevice, resourceCommandPool, findDepthFormat(), swapChainExtent,
[8dcbf62]2300 depthImage, graphicsQueue);
[cefdf23]2301
2302 createRenderPass();
2303 createCommandPools();
[0ae182f]2304 createFramebuffers();
[cefdf23]2305 createCommandBuffers();
2306 createSyncObjects();
[f97c5e7]2307
[2f4ff8c]2308 createBufferSet(uniforms_modelPipeline.memorySize(),
[6486ba8]2309 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
[2f4ff8c]2310 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[6486ba8]2311 uniformBuffers_modelPipeline);
2312
[2f4ff8c]2313 uniforms_modelPipeline.map(uniformBuffers_modelPipeline.memory, device);
2314
[6486ba8]2315 createBufferSet(objects_modelPipeline.memorySize(),
2316 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
2317 | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
[2f4ff8c]2318 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[b01b50c]2319 objectBuffers_modelPipeline);
[0ae182f]2320
[b01b50c]2321 objects_modelPipeline.map(objectBuffers_modelPipeline.memory, device);
[2f4ff8c]2322
[b8777b7]2323 modelPipeline.updateRenderPass(renderPass);
[aa7e5f0]2324 modelPipeline.createPipeline("shaders/model-vert.spv", "shaders/model-frag.spv");
[58453c3]2325 modelPipeline.createDescriptorPool(swapChainImages.size());
2326 modelPipeline.createDescriptorSets(swapChainImages.size());
[0ae182f]2327
[2f4ff8c]2328 createBufferSet(uniforms_shipPipeline.memorySize(),
[6486ba8]2329 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
[2f4ff8c]2330 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[6486ba8]2331 uniformBuffers_shipPipeline);
2332
[2f4ff8c]2333 uniforms_shipPipeline.map(uniformBuffers_shipPipeline.memory, device);
2334
[6486ba8]2335 createBufferSet(objects_shipPipeline.memorySize(),
2336 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
2337 | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
[2f4ff8c]2338 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[b01b50c]2339 objectBuffers_shipPipeline);
[3782d66]2340
[b01b50c]2341 objects_shipPipeline.map(objectBuffers_shipPipeline.memory, device);
[2f4ff8c]2342
[3782d66]2343 shipPipeline.updateRenderPass(renderPass);
2344 shipPipeline.createPipeline("shaders/ship-vert.spv", "shaders/ship-frag.spv");
[58453c3]2345 shipPipeline.createDescriptorPool(swapChainImages.size());
2346 shipPipeline.createDescriptorSets(swapChainImages.size());
[3782d66]2347
[2f4ff8c]2348 createBufferSet(uniforms_asteroidPipeline.memorySize(),
[6486ba8]2349 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
[2f4ff8c]2350 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[6486ba8]2351 uniformBuffers_asteroidPipeline);
2352
[2f4ff8c]2353 uniforms_asteroidPipeline.map(uniformBuffers_asteroidPipeline.memory, device);
2354
[6486ba8]2355 createBufferSet(objects_asteroidPipeline.memorySize(),
2356 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
2357 | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
[2f4ff8c]2358 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[b01b50c]2359 objectBuffers_asteroidPipeline);
[3e8cc8b]2360
[b01b50c]2361 objects_asteroidPipeline.map(objectBuffers_asteroidPipeline.memory, device);
[2f4ff8c]2362
[3e8cc8b]2363 asteroidPipeline.updateRenderPass(renderPass);
2364 asteroidPipeline.createPipeline("shaders/asteroid-vert.spv", "shaders/asteroid-frag.spv");
[58453c3]2365 asteroidPipeline.createDescriptorPool(swapChainImages.size());
2366 asteroidPipeline.createDescriptorSets(swapChainImages.size());
[3e8cc8b]2367
[2f4ff8c]2368 createBufferSet(uniforms_laserPipeline.memorySize(),
[6486ba8]2369 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
[2f4ff8c]2370 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[6486ba8]2371 uniformBuffers_laserPipeline);
2372
[2f4ff8c]2373 uniforms_laserPipeline.map(uniformBuffers_laserPipeline.memory, device);
2374
[6486ba8]2375 createBufferSet(objects_laserPipeline.memorySize(),
2376 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
2377 | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
[2f4ff8c]2378 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[b01b50c]2379 objectBuffers_laserPipeline);
[237cbec]2380
[b01b50c]2381 objects_laserPipeline.map(objectBuffers_laserPipeline.memory, device);
[2f4ff8c]2382
[237cbec]2383 laserPipeline.updateRenderPass(renderPass);
2384 laserPipeline.createPipeline("shaders/laser-vert.spv", "shaders/laser-frag.spv");
[58453c3]2385 laserPipeline.createDescriptorPool(swapChainImages.size());
2386 laserPipeline.createDescriptorSets(swapChainImages.size());
[237cbec]2387
[90880fb]2388 createBufferSet(uniforms_explosionPipeline.memorySize(),
[6486ba8]2389 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
[2f4ff8c]2390 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[6486ba8]2391 uniformBuffers_explosionPipeline);
2392
[2f4ff8c]2393 uniforms_explosionPipeline.map(uniformBuffers_explosionPipeline.memory, device);
2394
[6486ba8]2395 createBufferSet(objects_explosionPipeline.memorySize(),
2396 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT
2397 | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
[2f4ff8c]2398 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
[b01b50c]2399 objectBuffers_explosionPipeline);
[4a9416a]2400
[b01b50c]2401 objects_explosionPipeline.map(objectBuffers_explosionPipeline.memory, device);
[2f4ff8c]2402
[4a9416a]2403 explosionPipeline.updateRenderPass(renderPass);
2404 explosionPipeline.createPipeline("shaders/explosion-vert.spv", "shaders/explosion-frag.spv");
[58453c3]2405 explosionPipeline.createDescriptorPool(swapChainImages.size());
2406 explosionPipeline.createDescriptorSets(swapChainImages.size());
[4a9416a]2407
[3f32dfd]2408 imageIndex = 0;
[d2d9286]2409}
2410
[f94eea9]2411void VulkanGame::cleanupSwapChain() {
[603b5bc]2412 VulkanUtils::destroyVulkanImage(device, depthImage);
2413
2414 for (VkFramebuffer framebuffer : swapChainFramebuffers) {
2415 vkDestroyFramebuffer(device, framebuffer, nullptr);
2416 }
2417
[9c0a614]2418 for (uint32_t i = 0; i < swapChainImageCount; i++) {
2419 vkFreeCommandBuffers(device, commandPools[i], 1, &commandBuffers[i]);
2420 vkDestroyCommandPool(device, commandPools[i], nullptr);
2421 }
[603b5bc]2422
[860a0da]2423 modelPipeline.cleanup();
[2f4ff8c]2424
2425 uniforms_modelPipeline.unmap(uniformBuffers_modelPipeline.memory, device);
[b794178]2426
[c163d81]2427 for (size_t i = 0; i < uniformBuffers_modelPipeline.buffers.size(); i++) {
2428 vkDestroyBuffer(device, uniformBuffers_modelPipeline.buffers[i], nullptr);
2429 vkFreeMemory(device, uniformBuffers_modelPipeline.memory[i], nullptr);
[055750a]2430 }
2431
[b01b50c]2432 objects_modelPipeline.unmap(objectBuffers_modelPipeline.memory, device);
[2f4ff8c]2433
[b01b50c]2434 for (size_t i = 0; i < objectBuffers_modelPipeline.buffers.size(); i++) {
2435 vkDestroyBuffer(device, objectBuffers_modelPipeline.buffers[i], nullptr);
2436 vkFreeMemory(device, objectBuffers_modelPipeline.memory[i], nullptr);
[6486ba8]2437 }
2438
[2f4ff8c]2439 shipPipeline.cleanup();
2440
2441 uniforms_shipPipeline.unmap(uniformBuffers_shipPipeline.memory, device);
2442
[c163d81]2443 for (size_t i = 0; i < uniformBuffers_shipPipeline.buffers.size(); i++) {
2444 vkDestroyBuffer(device, uniformBuffers_shipPipeline.buffers[i], nullptr);
2445 vkFreeMemory(device, uniformBuffers_shipPipeline.memory[i], nullptr);
[3782d66]2446 }
[055750a]2447
[b01b50c]2448 objects_shipPipeline.unmap(objectBuffers_shipPipeline.memory, device);
[2f4ff8c]2449
[b01b50c]2450 for (size_t i = 0; i < objectBuffers_shipPipeline.buffers.size(); i++) {
2451 vkDestroyBuffer(device, objectBuffers_shipPipeline.buffers[i], nullptr);
2452 vkFreeMemory(device, objectBuffers_shipPipeline.memory[i], nullptr);
[6486ba8]2453 }
2454
[2f4ff8c]2455 asteroidPipeline.cleanup();
2456
2457 uniforms_asteroidPipeline.unmap(uniformBuffers_asteroidPipeline.memory, device);
2458
[c163d81]2459 for (size_t i = 0; i < uniformBuffers_asteroidPipeline.buffers.size(); i++) {
2460 vkDestroyBuffer(device, uniformBuffers_asteroidPipeline.buffers[i], nullptr);
2461 vkFreeMemory(device, uniformBuffers_asteroidPipeline.memory[i], nullptr);
[3e8cc8b]2462 }
2463
[b01b50c]2464 objects_asteroidPipeline.unmap(objectBuffers_asteroidPipeline.memory, device);
[2f4ff8c]2465
[b01b50c]2466 for (size_t i = 0; i < objectBuffers_asteroidPipeline.buffers.size(); i++) {
2467 vkDestroyBuffer(device, objectBuffers_asteroidPipeline.buffers[i], nullptr);
2468 vkFreeMemory(device, objectBuffers_asteroidPipeline.memory[i], nullptr);
[6486ba8]2469 }
2470
[2f4ff8c]2471 laserPipeline.cleanup();
2472
2473 uniforms_laserPipeline.unmap(uniformBuffers_laserPipeline.memory, device);
2474
[c163d81]2475 for (size_t i = 0; i < uniformBuffers_laserPipeline.buffers.size(); i++) {
2476 vkDestroyBuffer(device, uniformBuffers_laserPipeline.buffers[i], nullptr);
2477 vkFreeMemory(device, uniformBuffers_laserPipeline.memory[i], nullptr);
[237cbec]2478 }
2479
[b01b50c]2480 objects_laserPipeline.unmap(objectBuffers_laserPipeline.memory, device);
[2f4ff8c]2481
[b01b50c]2482 for (size_t i = 0; i < objectBuffers_laserPipeline.buffers.size(); i++) {
2483 vkDestroyBuffer(device, objectBuffers_laserPipeline.buffers[i], nullptr);
2484 vkFreeMemory(device, objectBuffers_laserPipeline.memory[i], nullptr);
[6486ba8]2485 }
2486
[2f4ff8c]2487 explosionPipeline.cleanup();
2488
2489 uniforms_explosionPipeline.unmap(uniformBuffers_explosionPipeline.memory, device);
2490
[c163d81]2491 for (size_t i = 0; i < uniformBuffers_explosionPipeline.buffers.size(); i++) {
2492 vkDestroyBuffer(device, uniformBuffers_explosionPipeline.buffers[i], nullptr);
2493 vkFreeMemory(device, uniformBuffers_explosionPipeline.memory[i], nullptr);
[4a9416a]2494 }
2495
[b01b50c]2496 objects_explosionPipeline.unmap(objectBuffers_explosionPipeline.memory, device);
[2f4ff8c]2497
[b01b50c]2498 for (size_t i = 0; i < objectBuffers_explosionPipeline.buffers.size(); i++) {
2499 vkDestroyBuffer(device, objectBuffers_explosionPipeline.buffers[i], nullptr);
2500 vkFreeMemory(device, objectBuffers_explosionPipeline.memory[i], nullptr);
[6486ba8]2501 }
2502
[3f32dfd]2503 for (size_t i = 0; i < swapChainImageCount; i++) {
2504 vkDestroySemaphore(device, imageAcquiredSemaphores[i], nullptr);
2505 vkDestroySemaphore(device, renderCompleteSemaphores[i], nullptr);
2506 vkDestroyFence(device, inFlightFences[i], nullptr);
2507 }
2508
[860a0da]2509 vkDestroyRenderPass(device, renderPass, nullptr);
2510
2511 for (VkImageView imageView : swapChainImageViews) {
2512 vkDestroyImageView(device, imageView, nullptr);
[3e8cc8b]2513 }
[860a0da]2514
2515 vkDestroySwapchainKHR(device, swapChain, nullptr);
[cc4a8b5]2516}
[20e4c2b]2517
[301c90a]2518void VulkanGame::renderMainScreen(int width, int height) {
[20e4c2b]2519 {
2520 int padding = 4;
[301c90a]2521 ImGui::SetNextWindowPos(vec2(-padding, -padding), ImGuiCond_Once);
2522 ImGui::SetNextWindowSize(vec2(width + 2 * padding, height + 2 * padding), ImGuiCond_Always);
[20e4c2b]2523 ImGui::Begin("WndMain", nullptr,
2524 ImGuiWindowFlags_NoTitleBar |
2525 ImGuiWindowFlags_NoResize |
2526 ImGuiWindowFlags_NoMove);
2527
[301c90a]2528 ButtonImGui btn("New Game");
2529
2530 ImGui::InvisibleButton("", vec2(10, height / 6));
2531 if (btn.draw((width - btn.getWidth()) / 2)) {
[20e4c2b]2532 goToScreen(&VulkanGame::renderGameScreen);
2533 }
2534
[301c90a]2535 ButtonImGui btn2("Quit");
2536
2537 ImGui::InvisibleButton("", vec2(10, 15));
2538 if (btn2.draw((width - btn2.getWidth()) / 2)) {
[20e4c2b]2539 quitGame();
2540 }
2541
2542 ImGui::End();
2543 }
2544}
2545
[301c90a]2546void VulkanGame::renderGameScreen(int width, int height) {
[20e4c2b]2547 {
[301c90a]2548 ImGui::SetNextWindowSize(vec2(130, 65), ImGuiCond_Once);
2549 ImGui::SetNextWindowPos(vec2(10, 50), ImGuiCond_Once);
[20e4c2b]2550 ImGui::Begin("WndStats", nullptr,
2551 ImGuiWindowFlags_NoTitleBar |
2552 ImGuiWindowFlags_NoResize |
2553 ImGuiWindowFlags_NoMove);
2554
2555 //ImGui::Text(ImGui::GetIO().Framerate);
2556 renderGuiValueList(valueLists["stats value list"]);
2557
2558 ImGui::End();
2559 }
2560
2561 {
[301c90a]2562 ImGui::SetNextWindowSize(vec2(250, 35), ImGuiCond_Once);
2563 ImGui::SetNextWindowPos(vec2(width - 260, 10), ImGuiCond_Always);
[20e4c2b]2564 ImGui::Begin("WndMenubar", nullptr,
2565 ImGuiWindowFlags_NoTitleBar |
2566 ImGuiWindowFlags_NoResize |
2567 ImGuiWindowFlags_NoMove);
[301c90a]2568 ImGui::InvisibleButton("", vec2(155, 18));
[20e4c2b]2569 ImGui::SameLine();
2570 if (ImGui::Button("Main Menu")) {
2571 goToScreen(&VulkanGame::renderMainScreen);
2572 }
2573 ImGui::End();
2574 }
2575
2576 {
[301c90a]2577 ImGui::SetNextWindowSize(vec2(200, 200), ImGuiCond_Once);
2578 ImGui::SetNextWindowPos(vec2(width - 210, 60), ImGuiCond_Always);
[20e4c2b]2579 ImGui::Begin("WndDebug", nullptr,
2580 ImGuiWindowFlags_NoTitleBar |
2581 ImGuiWindowFlags_NoResize |
2582 ImGuiWindowFlags_NoMove);
2583
2584 renderGuiValueList(valueLists["debug value list"]);
2585
2586 ImGui::End();
2587 }
2588}
2589
2590void VulkanGame::initGuiValueLists(map<string, vector<UIValue>>& valueLists) {
2591 valueLists["stats value list"] = vector<UIValue>();
2592 valueLists["debug value list"] = vector<UIValue>();
2593}
2594
[301c90a]2595// TODO: Probably turn this into a UI widget class
[20e4c2b]2596void VulkanGame::renderGuiValueList(vector<UIValue>& values) {
2597 float maxWidth = 0.0f;
2598 float cursorStartPos = ImGui::GetCursorPosX();
2599
2600 for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
2601 float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
2602
2603 if (maxWidth < textWidth)
2604 maxWidth = textWidth;
2605 }
2606
2607 stringstream ss;
2608
2609 // TODO: Possibly implement this based on gui/ui-value.hpp instead and use templates
2610 // to keep track of the type. This should make it a bit easier to use and maintain
2611 // Also, implement this in a way that's agnostic to the UI renderer.
2612 for (vector<UIValue>::iterator it = values.begin(); it != values.end(); it++) {
2613 ss.str("");
2614 ss.clear();
2615
2616 switch (it->type) {
2617 case UIVALUE_INT:
2618 ss << it->label << ": " << *(unsigned int*)it->value;
2619 break;
2620 case UIVALUE_DOUBLE:
2621 ss << it->label << ": " << *(double*)it->value;
2622 break;
2623 }
2624
2625 float textWidth = ImGui::CalcTextSize(it->label.c_str()).x;
2626
2627 ImGui::SetCursorPosX(cursorStartPos + maxWidth - textWidth);
2628 //ImGui::Text("%s", ss.str().c_str());
2629 ImGui::Text("%s: %.1f", it->label.c_str(), *(float*)it->value);
2630 }
2631}
2632
[301c90a]2633void VulkanGame::goToScreen(void (VulkanGame::* renderScreenFn)(int width, int height)) {
[20e4c2b]2634 currentRenderScreenFn = renderScreenFn;
2635
2636 // TODO: Maybe just set shouldRecreateSwapChain to true instead. Check this render loop logic
2637 // to make sure there'd be no issues
2638 //recreateSwapChain();
2639}
2640
2641void VulkanGame::quitGame() {
2642 done = true;
2643}
Note: See TracBrowser for help on using the repository browser.