source: opengl-game/vulkan-game.hpp@ a3cefaa

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

Move SSBO resizing and pipeline recreation checks out of addObject() and into updateScene() so that those operations are only done at most once per pipeline per frame, using vkUpdateDescriptorSets() instead of recreating the whole graphics pipeline, and create a VulkanBuffer class for managing data related to uniform buffers and shader storage buffers, move objectCapacity and numObjects out of GraphicsPipeline_vulkan and use VulkanBuffer to manage them instead

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