source: opengl-game/vulkan-game.hpp@ 6bac215

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

Rewrite a large portion of the VulkanBuffer class, start using it more
to resize buffers, and simplify resizeBufferSet() since the additions to
VulkanBuffer can now do some of what that function used to do

  • Property mode set to 100644
File size: 22.7 KB
RevLine 
[99d44b2]1#ifndef _VULKAN_GAME_H
2#define _VULKAN_GAME_H
[e8ebc76]3
[aa7707d]4#include <algorithm>
[0807aeb]5#include <chrono>
[e1f88a9]6#include <map>
[5192672]7#include <vector>
[0807aeb]8
[20e4c2b]9#include <vulkan/vulkan.h>
10
11#include <SDL2/SDL.h>
12#include <SDL2/SDL_ttf.h>
13
[60578ce]14#define GLM_FORCE_RADIANS
15#define GLM_FORCE_DEPTH_ZERO_TO_ONE // Since, in Vulkan, the depth range is 0 to 1 instead of -1 to 1
[a79be34]16#define GLM_FORCE_RIGHT_HANDED
[60578ce]17
[771b33a]18#include <glm/glm.hpp>
[15104a8]19#include <glm/gtc/matrix_transform.hpp>
[771b33a]20
[ea2b4dc]21#include "IMGUI/imgui_impl_vulkan.h"
22
[6bfd91c]23#include "consts.hpp"
[b8efa56]24#include "utils.hpp"
[8aa4888]25#include "game-gui-sdl.hpp"
[b8efa56]26#include "vulkan-utils.hpp"
[a3cefaa]27#include "graphics-pipeline_vulkan.hpp"
[8aa4888]28#include "vulkan-buffer.hpp"
[b794178]29
[15104a8]30using namespace glm;
[0807aeb]31using namespace std::chrono;
[15104a8]32
[2e77b3f]33#ifdef NDEBUG
34 const bool ENABLE_VALIDATION_LAYERS = false;
35#else
36 const bool ENABLE_VALIDATION_LAYERS = true;
37#endif
38
[cefdf23]39// TODO: Consider if there is a better way of dealing with all the vertex types and ssbo types, maybe
40// by consolidating some and trying to keep new ones to a minimum
41
[5a1ace0]42struct ModelVertex {
[15104a8]43 vec3 pos;
[5a1ace0]44 vec3 color;
[15104a8]45 vec2 texCoord;
[a00eb06]46 vec3 normal;
[5a1ace0]47 unsigned int objIndex;
[15104a8]48};
49
[237cbec]50struct LaserVertex {
51 vec3 pos;
52 vec2 texCoord;
53 unsigned int objIndex;
54};
55
[4a9416a]56struct ExplosionVertex {
57 vec3 particleStartVelocity;
58 float particleStartTime;
59 unsigned int objIndex;
60};
61
[2d87297]62struct SSBO_ModelObject {
[055750a]63 alignas(16) mat4 model;
64};
65
[2d87297]66struct SSBO_Asteroid {
[3e8cc8b]67 alignas(16) mat4 model;
68 alignas(4) float hp;
[4ece3bf]69 alignas(4) unsigned int deleted;
[3e8cc8b]70};
71
[237cbec]72struct SSBO_Laser {
73 alignas(16) mat4 model;
74 alignas(4) vec3 color;
75 alignas(4) unsigned int deleted;
76};
77
[4a9416a]78struct SSBO_Explosion {
79 alignas(16) mat4 model;
80 alignas(4) float explosionStartTime;
81 alignas(4) float explosionDuration;
82 alignas(4) unsigned int deleted;
83};
84
[52a02e6]85struct UBO_VP_mats {
86 alignas(16) mat4 view;
87 alignas(16) mat4 proj;
88};
89
[4a9416a]90struct UBO_Explosion {
91 alignas(16) mat4 view;
92 alignas(16) mat4 proj;
93 alignas(4) float cur_time;
94};
95
[996dd3e]96// TODO: Use this struct for uniform buffers as well and probably combine it with the VulkanBuffer class
97// Also, probably better to make this a vector of structs where each struct
98// has a VkBuffer, VkDeviceMemory, and VkDescriptorBufferInfo
[a3cefaa]99// TODO: Maybe change the structure here since VkDescriptorBufferInfo already stores a reference to the VkBuffer
[1abebc1]100struct BufferSet {
[996dd3e]101 vector<VkBuffer> buffers;
102 vector<VkDeviceMemory> memory;
103 vector<VkDescriptorBufferInfo> infoSet;
[8aa4888]104 VkBufferUsageFlags usages;
105 VkMemoryPropertyFlags properties;
[996dd3e]106};
107
[4994692]108// TODO: Change the index type to uint32_t and check the Vulkan Tutorial loading model section as a reference
109// TODO: Create a typedef for index type so I can easily change uin16_t to something else later
110// TODO: Maybe create a typedef for each of the templated SceneObject types
111template<class VertexType, class SSBOType>
112struct SceneObject {
113 vector<VertexType> vertices;
114 vector<uint16_t> indices;
115 SSBOType ssbo;
116
117 mat4 model_base;
118 mat4 model_transform;
119
[5ba732a]120 bool modified;
121
[4994692]122 // TODO: Figure out if I should make child classes that have these fields instead of putting them in the
123 // parent class
124 vec3 center; // currently only matters for asteroids
125 float radius; // currently only matters for asteroids
[b8efa56]126 SceneObject<ModelVertex, SSBO_Asteroid>* targetAsteroid; // currently only used for lasers
[4994692]127};
128
129// TODO: Have to figure out how to include an optional ssbo parameter for each object
[2da64ef]130// Could probably use the same approach to make indices optional
[4994692]131// Figure out if there are sufficient use cases to make either of these optional or is it fine to make
132// them mamdatory
[2da64ef]133
[7297892]134
135// TODO: Look into using dynamic_cast to check types of SceneObject and EffectOverTime
136
137struct BaseEffectOverTime {
138 bool deleted;
139
[5192672]140 virtual void applyEffect(float curTime) = 0;
[7297892]141
142 BaseEffectOverTime() :
143 deleted(false) {
144 }
145
146 virtual ~BaseEffectOverTime() {
147 }
148};
149
150template<class VertexType, class SSBOType>
151struct EffectOverTime : public BaseEffectOverTime {
[9d21aac]152 GraphicsPipeline_Vulkan<VertexType>& pipeline;
[7297892]153 vector<SceneObject<VertexType, SSBOType>>& objects;
154 unsigned int objectIndex;
155 size_t effectedFieldOffset;
156 float startValue;
157 float startTime;
158 float changePerSecond;
159
[9d21aac]160 EffectOverTime(GraphicsPipeline_Vulkan<VertexType>& pipeline, vector<SceneObject<VertexType, SSBOType>>& objects,
161 unsigned int objectIndex, size_t effectedFieldOffset, float startTime, float changePerSecond)
162 : pipeline(pipeline)
163 , objects(objects)
164 , objectIndex(objectIndex)
165 , effectedFieldOffset(effectedFieldOffset)
166 , startTime(startTime)
167 , changePerSecond(changePerSecond) {
[7297892]168 size_t ssboOffset = offset_of(&SceneObject<VertexType, SSBOType>::ssbo);
169
170 unsigned char* effectedFieldPtr = reinterpret_cast<unsigned char*>(&objects[objectIndex]) +
171 ssboOffset + effectedFieldOffset;
172
173 startValue = *reinterpret_cast<float*>(effectedFieldPtr);
174 }
175
[5192672]176 void applyEffect(float curTime) {
[7297892]177 if (objects[objectIndex].ssbo.deleted) {
178 this->deleted = true;
179 return;
180 }
181
182 size_t ssboOffset = offset_of(&SceneObject<VertexType, SSBOType>::ssbo);
183
184 unsigned char* effectedFieldPtr = reinterpret_cast<unsigned char*>(&objects[objectIndex]) +
185 ssboOffset + effectedFieldOffset;
186
187 *reinterpret_cast<float*>(effectedFieldPtr) = startValue + (curTime - startTime) * changePerSecond;
188
189 objects[objectIndex].modified = true;
190 }
191};
192
[cefdf23]193// TODO: Maybe move this to a different header
194
[20e4c2b]195enum UIValueType {
196 UIVALUE_INT,
197 UIVALUE_DOUBLE,
198};
199
200struct UIValue {
201 UIValueType type;
202 string label;
203 void* value;
204
205 UIValue(UIValueType _type, string _label, void* _value) : type(_type), label(_label), value(_value) {}
206};
207
[e8445f0]208/* TODO: The following syntax (note the const keyword) means the function will not modify
209 * its params. I should use this where appropriate
210 *
211 * [return-type] [func-name](params...) const { ... }
212 */
213
[99d44b2]214class VulkanGame {
[914bb99]215
[e8ebc76]216 public:
[cefdf23]217
[3f32dfd]218 VulkanGame();
[99d44b2]219 ~VulkanGame();
[0df3c9a]220
[b6e60b4]221 void run(int width, int height, unsigned char guiFlags);
[0df3c9a]222
223 private:
[cefdf23]224
[c324d6a]225 static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
226 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
227 VkDebugUtilsMessageTypeFlagsEXT messageType,
228 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
229 void* pUserData);
230
[cefdf23]231 // TODO: Maybe pass these in as parameters to some Camera class
[5ab1b20]232 const float NEAR_CLIP = 0.1f;
233 const float FAR_CLIP = 100.0f;
[cefdf23]234 const float FOV_ANGLE = 67.0f; // means the camera lens goes from -33 deg to 33 deg
[5ab1b20]235
[4a9416a]236 const int EXPLOSION_PARTICLE_COUNT = 300;
237 const vec3 LASER_COLOR = vec3(0.2f, 1.0f, 0.2f);
238
[c6f0793]239 bool done;
[e1f88a9]240
[15104a8]241 vec3 cam_pos;
242
[4e705d6]243 // TODO: Good place to start using smart pointers
[0df3c9a]244 GameGui* gui;
[c559904]245
246 SDL_version sdlVersion;
[b794178]247 SDL_Window* window = nullptr;
[c1d9b2a]248
[301c90a]249 int drawableWidth, drawableHeight;
250
[c1d9b2a]251 VkInstance instance;
252 VkDebugUtilsMessengerEXT debugMessenger;
[7865c5b]253 VkSurfaceKHR vulkanSurface;
[90a424f]254 VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
[c1c2021]255 VkDevice device;
256
257 VkQueue graphicsQueue;
258 VkQueue presentQueue;
[0df3c9a]259
[3f32dfd]260 // TODO: Maybe make a swapchain struct for convenience
261 VkSurfaceFormatKHR swapChainSurfaceFormat;
262 VkPresentModeKHR swapChainPresentMode;
263 VkExtent2D swapChainExtent;
264 uint32_t swapChainMinImageCount;
[c324d6a]265 uint32_t swapChainImageCount;
[502bd0b]266 VkSwapchainKHR swapChain;
267 vector<VkImage> swapChainImages;
[f94eea9]268 vector<VkImageView> swapChainImageViews;
[603b5bc]269 vector<VkFramebuffer> swapChainFramebuffers;
[fa9fa1c]270
[6fc24c7]271 VkRenderPass renderPass;
[3f32dfd]272
273 VkCommandPool resourceCommandPool;
274
[9c0a614]275 vector<VkCommandPool> commandPools;
[603b5bc]276 vector<VkCommandBuffer> commandBuffers;
[502bd0b]277
[603b5bc]278 VulkanImage depthImage;
[b794178]279
[3f32dfd]280 // These are per frame
281 vector<VkSemaphore> imageAcquiredSemaphores;
282 vector<VkSemaphore> renderCompleteSemaphores;
283
284 // These are per swap chain image
[4e705d6]285 vector<VkFence> inFlightFences;
286
[3f32dfd]287 uint32_t imageIndex;
288 uint32_t currentFrame;
[4e705d6]289
[28ea92f]290 bool shouldRecreateSwapChain;
[4e705d6]291
[b794178]292 VkSampler textureSampler;
293
[4994692]294 VulkanImage floorTextureImage;
295 VkDescriptorImageInfo floorTextureImageDescriptor;
296
[237cbec]297 VulkanImage laserTextureImage;
298 VkDescriptorImageInfo laserTextureImageDescriptor;
299
[22217d4]300 mat4 viewMat, projMat;
301
[cefdf23]302 // Maybe at some point create an imgui pipeline class, but I don't think it makes sense right now
303 VkDescriptorPool imguiDescriptorPool;
304
305 // TODO: Probably restructure the GraphicsPipeline_Vulkan class based on what I learned about descriptors and textures
306 // while working on graphics-library. Double-check exactly what this was and note it down here.
307 // Basically, I think the point was that if I have several modesl that all use the same shaders and, therefore,
308 // the same pipeline, but use different textures, the approach I took when initially creating GraphicsPipeline_Vulkan
309 // wouldn't work since the whole pipeline couldn't have a common set of descriptors for the textures
[9d21aac]310 GraphicsPipeline_Vulkan<ModelVertex> modelPipeline;
311 GraphicsPipeline_Vulkan<ModelVertex> shipPipeline;
312 GraphicsPipeline_Vulkan<ModelVertex> asteroidPipeline;
313 GraphicsPipeline_Vulkan<LaserVertex> laserPipeline;
314 GraphicsPipeline_Vulkan<ExplosionVertex> explosionPipeline;
[cefdf23]315
[1abebc1]316 BufferSet storageBuffers_modelPipeline;
[a3cefaa]317 VulkanBuffer<SSBO_ModelObject> objects_modelPipeline;
[c163d81]318 BufferSet uniformBuffers_modelPipeline;
[a3cefaa]319
[1abebc1]320 BufferSet storageBuffers_shipPipeline;
[a3cefaa]321 VulkanBuffer<SSBO_ModelObject> objects_shipPipeline;
[c163d81]322 BufferSet uniformBuffers_shipPipeline;
[a3cefaa]323
[1abebc1]324 BufferSet storageBuffers_asteroidPipeline;
[a3cefaa]325 VulkanBuffer<SSBO_Asteroid> objects_asteroidPipeline;
[c163d81]326 BufferSet uniformBuffers_asteroidPipeline;
[a3cefaa]327
[1abebc1]328 BufferSet storageBuffers_laserPipeline;
[a3cefaa]329 VulkanBuffer<SSBO_Laser> objects_laserPipeline;
[c163d81]330 BufferSet uniformBuffers_laserPipeline;
[a3cefaa]331
[1abebc1]332 BufferSet storageBuffers_explosionPipeline;
[a3cefaa]333 VulkanBuffer<SSBO_Explosion> objects_explosionPipeline;
[c163d81]334 BufferSet uniformBuffers_explosionPipeline;
[996dd3e]335
[860a0da]336 // TODO: Maybe make the ubo objects part of the pipeline class since there's only one ubo
337 // per pipeline.
338 // Or maybe create a higher level wrapper around GraphicsPipeline_Vulkan to hold things like
339 // the objects vector, the ubo, and the ssbo
340
[2ba5617]341 // TODO: Rename *_VP_mats to *_uniforms and possibly use different types for each one
342 // if there is a need to add other uniform variables to one or more of the shaders
343
[2d87297]344 vector<SceneObject<ModelVertex, SSBO_ModelObject>> modelObjects;
[0fe8433]345
346 UBO_VP_mats object_VP_mats;
347
[8d92284]348 vector<SceneObject<ModelVertex, SSBO_ModelObject>> shipObjects;
[0fe8433]349
350 UBO_VP_mats ship_VP_mats;
[0e09340]351
[b8efa56]352 vector<SceneObject<ModelVertex, SSBO_Asteroid>> asteroidObjects;
[3e8cc8b]353
354 UBO_VP_mats asteroid_VP_mats;
355
[237cbec]356 vector<SceneObject<LaserVertex, SSBO_Laser>> laserObjects;
357
358 UBO_VP_mats laser_VP_mats;
359
[4a9416a]360 vector<SceneObject<ExplosionVertex, SSBO_Explosion>> explosionObjects;
361
362 UBO_Explosion explosion_UBO;
363
[7297892]364 vector<BaseEffectOverTime*> effects;
365
[0807aeb]366 float shipSpeed = 0.5f;
367 float asteroidSpeed = 2.0f;
368
369 float spawnRate_asteroid = 0.5;
370 float lastSpawn_asteroid;
[4ece3bf]371
[1f81ecc]372 unsigned int leftLaserIdx = -1;
[b8efa56]373 EffectOverTime<ModelVertex, SSBO_Asteroid>* leftLaserEffect = nullptr;
[1f81ecc]374
375 unsigned int rightLaserIdx = -1;
[b8efa56]376 EffectOverTime<ModelVertex, SSBO_Asteroid>* rightLaserEffect = nullptr;
[1f81ecc]377
[20e4c2b]378 /*** High-level vars ***/
379
[301c90a]380 // TODO: Just typedef the type of this function to RenderScreenFn or something since it's used in a few places
381 void (VulkanGame::* currentRenderScreenFn)(int width, int height);
[20e4c2b]382
383 map<string, vector<UIValue>> valueLists;
384
385 int score;
386 float fps;
387
388 // TODO: Make a separate TImer class
389 time_point<steady_clock> startTime;
390 float fpsStartTime, curTime, prevTime, elapsedTime;
391
392 int frameCount;
393
394 /*** Functions ***/
395
[4e705d6]396 bool initUI(int width, int height, unsigned char guiFlags);
[0df3c9a]397 void initVulkan();
[f97c5e7]398 void initGraphicsPipelines();
[15104a8]399 void initMatrices();
[20e4c2b]400 void renderLoop();
[3f32dfd]401 void updateScene();
[0df3c9a]402 void cleanup();
[c1d9b2a]403
[c324d6a]404 void createVulkanInstance(const vector<const char*>& validationLayers);
[c1d9b2a]405 void setupDebugMessenger();
406 void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo);
[90a424f]407 void createVulkanSurface();
[fe5c3ba]408 void pickPhysicalDevice(const vector<const char*>& deviceExtensions);
[fa9fa1c]409 bool isDeviceSuitable(VkPhysicalDevice physicalDevice, const vector<const char*>& deviceExtensions);
[c324d6a]410 void createLogicalDevice(const vector<const char*>& validationLayers,
[c1c2021]411 const vector<const char*>& deviceExtensions);
[3f32dfd]412 void chooseSwapChainProperties();
[502bd0b]413 void createSwapChain();
[f94eea9]414 void createImageViews();
[3f32dfd]415 void createResourceCommandPool();
[603b5bc]416 void createImageResources();
[cefdf23]417 VkFormat findDepthFormat(); // TODO: Declare/define (in the cpp file) this function in some util functions section
418 void createRenderPass();
419 void createCommandPools();
[603b5bc]420 void createFramebuffers();
421 void createCommandBuffers();
[34bdf3a]422 void createSyncObjects();
[f94eea9]423
[3f32dfd]424 void createTextureSampler();
425
[cefdf23]426 void initImGuiOverlay();
427 void cleanupImGuiOverlay();
428
[9d21aac]429 // TODO: Maybe move these to a different class, possibly VulkanBuffer or some new related class
430
[8aa4888]431 void createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags usages, VkMemoryPropertyFlags properties,
[c163d81]432 BufferSet& set);
[9d21aac]433
434 template<class VertexType, class SSBOType>
[1abebc1]435 void resizeBufferSet(BufferSet& set, VulkanBuffer<SSBOType>& buffer,
436 GraphicsPipeline_Vulkan<VertexType>& pipeline, VkCommandPool commandPool,
437 VkQueue graphicsQueue);
[9d21aac]438
439 template<class SSBOType>
[1abebc1]440 void updateBufferSet(BufferSet& set, size_t objIndex, SSBOType& ssbo);
[3b7d497]441
[4994692]442 // TODO: Since addObject() returns a reference to the new object now,
443 // stop using objects.back() to access the object that was just created
[2d87297]444 template<class VertexType, class SSBOType>
[9d21aac]445 SceneObject<VertexType, SSBOType>& addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
446 GraphicsPipeline_Vulkan<VertexType>& pipeline,
447 const vector<VertexType>& vertices, vector<uint16_t> indices,
[8dcbf62]448 VulkanBuffer<SSBOType>& objectBuffer, SSBOType ssbo);
[0fe8433]449
[cefdf23]450 template<class VertexType>
451 vector<VertexType> addObjectIndex(unsigned int objIndex, vector<VertexType> vertices);
452
453 template<class VertexType>
454 vector<VertexType> addVertexNormals(vector<VertexType> vertices);
455
456 template<class VertexType, class SSBOType>
457 void centerObject(SceneObject<VertexType, SSBOType>& object);
458
[2da64ef]459 template<class VertexType, class SSBOType>
[1abebc1]460 void updateObject(SceneObject<VertexType, SSBOType>& obj);
[2da64ef]461
[1f81ecc]462 template<class VertexType, class SSBOType>
[9d21aac]463 void updateObjectVertices(GraphicsPipeline_Vulkan<VertexType>& pipeline,
[1f81ecc]464 SceneObject<VertexType, SSBOType>& obj, size_t index);
465
[52a02e6]466 void addLaser(vec3 start, vec3 end, vec3 color, float width);
467 void translateLaser(size_t index, const vec3& translation);
468 void updateLaserTarget(size_t index);
[b8efa56]469 bool getLaserAndAsteroidIntersection(SceneObject<ModelVertex, SSBO_Asteroid>& asteroid,
[52a02e6]470 vec3& start, vec3& end, vec3& intersection);
471
[4a9416a]472 void addExplosion(mat4 model_mat, float duration, float cur_time);
473
[ea2b4dc]474 void renderFrame(ImDrawData* draw_data);
[4e2c709]475 void presentFrame();
476
[d2d9286]477 void recreateSwapChain();
478
[c1c2021]479 void cleanupSwapChain();
[20e4c2b]480
481 /*** High-level functions ***/
482
[301c90a]483 void renderMainScreen(int width, int height);
484 void renderGameScreen(int width, int height);
[20e4c2b]485
486 void initGuiValueLists(map<string, vector<UIValue>>& valueLists);
487 void renderGuiValueList(vector<UIValue>& values);
488
[301c90a]489 void goToScreen(void (VulkanGame::* renderScreenFn)(int width, int height));
[20e4c2b]490 void quitGame();
[e8ebc76]491};
492
[4a9416a]493// Start of specialized no-op functions
494
495template<>
496inline void VulkanGame::centerObject(SceneObject<ExplosionVertex, SSBO_Explosion>& object) {
497}
498
499// End of specialized no-op functions
500
[9d21aac]501template<class VertexType, class SSBOType>
[1abebc1]502void VulkanGame::resizeBufferSet(BufferSet& set, VulkanBuffer<SSBOType>& buffer,
503 GraphicsPipeline_Vulkan<VertexType>& pipeline, VkCommandPool commandPool,
504 VkQueue graphicsQueue) {
[6bac215]505 VkDeviceSize newSize = buffer.capacity * sizeof(SSBOType);
[9d21aac]506
507 for (size_t i = 0; i < set.buffers.size(); i++) {
[6bac215]508 VkBuffer newBuffer;
509 VkDeviceMemory newMemory;
[9d21aac]510
[6bac215]511 VulkanUtils::createBuffer(device, physicalDevice, newSize, set.usages, set.properties, newBuffer, newMemory);
[9d21aac]512
[6bac215]513 VulkanUtils::copyBuffer(device, commandPool, set.buffers[i], newBuffer, 0, 0, set.infoSet[i].range,
514 graphicsQueue);
[9d21aac]515
516 vkDestroyBuffer(device, set.buffers[i], nullptr);
517 vkFreeMemory(device, set.memory[i], nullptr);
518
[6bac215]519 set.buffers[i] = newBuffer;
520 set.memory[i] = newMemory;
[9d21aac]521
522 set.infoSet[i].buffer = set.buffers[i];
523 set.infoSet[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
[6bac215]524 set.infoSet[i].range = newSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
[9d21aac]525 }
[a3cefaa]526
527 // Assume the SSBO is always the 2nd binding
528 // TODO: Figure out a way to make this more flexible
[58453c3]529 pipeline.updateDescriptorInfo(1, &set.infoSet, swapChainImages.size());
[9d21aac]530}
531
532// TODO: See if it makes sense to pass in the current swapchain index instead of updating all of them
533template<class SSBOType>
[1abebc1]534void VulkanGame::updateBufferSet(BufferSet& set, size_t objIndex, SSBOType& ssbo) {
535 for (size_t i = 0; i < set.memory.size(); i++) {
[c074f81]536 VulkanUtils::copyDataToMemory(device, &ssbo, set.memory[i], objIndex * sizeof(SSBOType), sizeof(ssbo), false);
[9d21aac]537 }
538}
539
[3b84bb6]540// TODO: Right now, it's basically necessary to pass the identity matrix in for ssbo.model
541// and to change the model matrix later by setting model_transform and then calling updateObject()
[9d21aac]542// Figure out a better way to allow the model matrix to be set during object creation
[2d87297]543template<class VertexType, class SSBOType>
[9d21aac]544SceneObject<VertexType, SSBOType>& VulkanGame::addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
545 GraphicsPipeline_Vulkan<VertexType>& pipeline,
546 const vector<VertexType>& vertices, vector<uint16_t> indices,
[8dcbf62]547 VulkanBuffer<SSBOType>& objectBuffer, SSBOType ssbo) {
[2ba5617]548 // TODO: Use the model field of ssbo to set the object's model_base
549 // currently, the passed in model is useless since it gets overridden in updateObject() anyway
[0fe8433]550 size_t numVertices = pipeline.getNumVertices();
551
552 for (uint16_t& idx : indices) {
553 idx += numVertices;
554 }
555
[5ba732a]556 objects.push_back({ vertices, indices, ssbo, mat4(1.0f), mat4(1.0f), false });
[8dcbf62]557 objectBuffer.add(ssbo);
[3b84bb6]558
[2ba5617]559 SceneObject<VertexType, SSBOType>& obj = objects.back();
[1f81ecc]560
[cefdf23]561 // TODO: Specify whether to center the object outside of this function or, worst case, maybe
562 // with a boolean being passed in here, so that I don't have to rely on checking the specific object
563 // type
[8dcbf62]564 // TODO: Actually, I've already defined a no-op centerObject method for explosions
565 // Maybe I should do the same for lasers and remove this conditional altogether
[4a9416a]566 if (!is_same_v<VertexType, LaserVertex> && !is_same_v<VertexType, ExplosionVertex>) {
[1f81ecc]567 centerObject(obj);
568 }
[2ba5617]569
[9d21aac]570 pipeline.addObject(obj.vertices, obj.indices, resourceCommandPool, graphicsQueue);
571
[4994692]572 return obj;
[0fe8433]573}
574
[cefdf23]575template<class VertexType>
576vector<VertexType> VulkanGame::addObjectIndex(unsigned int objIndex, vector<VertexType> vertices) {
577 for (VertexType& vertex : vertices) {
578 vertex.objIndex = objIndex;
579 }
[2da64ef]580
[cefdf23]581 return vertices;
[1f81ecc]582}
583
[914bb99]584// This function sets all the normals for a face to be parallel
585// This is good for models that should have distinct faces, but bad for models that should appear smooth
586// Maybe add an option to set all copies of a point to have the same normal and have the direction of
587// that normal be the weighted average of all the faces it is a part of, where the weight from each face
588// is its surface area.
589
590// TODO: Since the current approach to normal calculation basicaly makes indexed drawing useless, see if it's
591// feasible to automatically enable/disable indexed drawing based on which approach is used
[06d959f]592template<class VertexType>
593vector<VertexType> VulkanGame::addVertexNormals(vector<VertexType> vertices) {
594 for (unsigned int i = 0; i < vertices.size(); i += 3) {
595 vec3 p1 = vertices[i].pos;
[cefdf23]596 vec3 p2 = vertices[i + 1].pos;
597 vec3 p3 = vertices[i + 2].pos;
[06d959f]598
[a79be34]599 vec3 normal = normalize(cross(p2 - p1, p3 - p1));
[06d959f]600
601 // Add the same normal for all 3 vertices
602 vertices[i].normal = normal;
[cefdf23]603 vertices[i + 1].normal = normal;
604 vertices[i + 2].normal = normal;
[cf727ca]605 }
606
607 return vertices;
608}
609
[3b84bb6]610template<class VertexType, class SSBOType>
611void VulkanGame::centerObject(SceneObject<VertexType, SSBOType>& object) {
612 vector<VertexType>& vertices = object.vertices;
613
[a79be34]614 float min_x = vertices[0].pos.x;
615 float max_x = vertices[0].pos.x;
616 float min_y = vertices[0].pos.y;
617 float max_y = vertices[0].pos.y;
618 float min_z = vertices[0].pos.z;
619 float max_z = vertices[0].pos.z;
620
621 // start from the second point
622 for (unsigned int i = 1; i < vertices.size(); i++) {
[3b84bb6]623 vec3& pos = vertices[i].pos;
624
625 if (min_x > pos.x) {
626 min_x = pos.x;
627 } else if (max_x < pos.x) {
628 max_x = pos.x;
[a79be34]629 }
630
[3b84bb6]631 if (min_y > pos.y) {
632 min_y = pos.y;
633 } else if (max_y < pos.y) {
634 max_y = pos.y;
[a79be34]635 }
636
[3b84bb6]637 if (min_z > pos.z) {
638 min_z = pos.z;
639 } else if (max_z < pos.z) {
640 max_z = pos.z;
[a79be34]641 }
642 }
643
644 vec3 center = vec3(min_x + max_x, min_y + max_y, min_z + max_z) / 2.0f;
645
646 for (unsigned int i = 0; i < vertices.size(); i++) {
647 vertices[i].pos -= center;
648 }
649
[2ba5617]650 object.radius = std::max(max_x - center.x, max_y - center.y);
651 object.radius = std::max(object.radius, max_z - center.z);
652
[3b84bb6]653 object.center = vec3(0.0f, 0.0f, 0.0f);
[a79be34]654}
655
[cefdf23]656// TODO: Just pass in the single object instead of a list of all of them
657template<class VertexType, class SSBOType>
[1abebc1]658void VulkanGame::updateObject(SceneObject<VertexType, SSBOType>& obj) {
[cefdf23]659 obj.ssbo.model = obj.model_transform * obj.model_base;
660 obj.center = vec3(obj.ssbo.model * vec4(0.0f, 0.0f, 0.0f, 1.0f));
661
662 obj.modified = false;
663}
664
665template<class VertexType, class SSBOType>
[9d21aac]666void VulkanGame::updateObjectVertices(GraphicsPipeline_Vulkan<VertexType>& pipeline,
[cefdf23]667 SceneObject<VertexType, SSBOType>& obj, size_t index) {
668 pipeline.updateObjectVertices(index, obj.vertices, resourceCommandPool, graphicsQueue);
669}
670
[3b84bb6]671#endif // _VULKAN_GAME_H
Note: See TracBrowser for help on using the repository browser.