source: opengl-game/vulkan-game.cpp@ 90880fb

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

Start using the VulkanBuffer class for the non-per-object uniforms (this means the view and projection matrices for all pipelines, as well as the current time for the explosion pipeline)

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