I’m building a video game engine using C++ as the coding language and Vulkan for graphics. I am trying to set up a generic renderer using Vulkan that is flexible and will render objects based on a vector that is supplied to it. The renderer will also handle the creation of the window using GLFW and use GLM for all relevant math calls.

As a start, this engine will render a basic spinning box with uniform color. However, it will then expand to cover the full functionality of the game engine.

I have written the basic code for the engine and it is compiling correctly. I want to verify that there are not noticeable errors in the code or anything that is missing. The basic class structure is as follows:

1. Core:

- class Engine
- This is the main class that will initialize, run, and shut down other subsystems of the engine (e.g., Window, Renderer, Input, etc.)

2. Window:

- class Window
- This class will handle window creation, resizing, and destruction using GLFW. It should also handle keyboard and mouse input events.

3. Renderer:

- class Renderer
- This class will manage Vulkan objects (e.g., instance, device, swapchain, command buffers) and rendering pipelines.
- class Shader
- To handle shader modules creation and destruction.
- class Texture
- To handle texture loading and destruction.
- class Pipeline
- To encapsulate the description and creation of a Vulkan graphics or compute pipeline.

4. Scene:

- class Scene
- This class will manage the list of objects in the vector, update their transforms, and send them to the Renderer for rendering.
- class GameObject
- To represent individual objects in the scene, store their transformation matrices, and bind the relevant data like vertices and indices.
- class Camera
- To store and manage camera properties such as position, rotation, view, and projection matrices.
- class Mesh
- To handle mesh data (e.g., vertices, indices, etc.) for individual objects.
- class Material
- To store and manage material properties such as colors, shaders, and textures.

5. Math:

- Utilize GLM library to handle vector and matrix math operations.


Here is the code for each of the header files:

BufferUtils.h:

#pragma once

#include <vulkan/vulkan.h>
#include <stdint.h>

namespace BufferUtils
{
    void CreateBuffer(
        VkDevice device, VkPhysicalDevice physicalDevice,
        VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties,
        VkBuffer& buffer, VkDeviceMemory& bufferMemory);

    uint32_t FindMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags properties);

    void CopyBuffer(
        VkDevice device, VkCommandPool commandPool, VkQueue graphicsQueue,
        VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
}


Camera.h:

#pragma once

#include <glm/glm.hpp>

class Camera
{
public:
	Camera();
	~Camera();

	void Initialize(float aspectRatio);
	void Shutdown();

	void SetPosition(const glm::vec3& position);
	void SetRotation(const glm::vec3& rotation);

	const glm::mat4& GetViewMatrix() const;
	const glm::mat4& GetProjectionMatrix() const;

private:
	glm::vec3 position;
	glm::vec3 rotation;
	glm::mat4 viewMatrix;
	glm::mat4 projectionMatrix;

	void UpdateViewMatrix();
};

Engine.h:

#pragma once

#include "Window.h"
#include "Renderer.h"
#include "Scene.h"

class Engine
{
public:
	static Engine& Instance(); // Singleton pattern
	~Engine();

	void Run();
	void Shutdown();

private:
	Engine(); // Singleton pattern

	void Initialize();
	void MainLoop();
	void Update(float deltaTime);
	void Render();

	Window window;
	Renderer renderer;
	Scene scene;
};

GameObject.h:

#pragma once

#include <glm/glm.hpp>
#include "Mesh.h"
#include "Material.h"
#include "Camera.h"
#include "Renderer.h"

class GameObject
{
public:
	GameObject();
	~GameObject();

	void Initialize(const Mesh& mesh, const Material& material);
	void Update(float deltaTime);
	void Render(Renderer& renderer, const Camera& camera);
	void Shutdown();

	void SetPosition(const glm::vec3& position);
	void SetRotation(const glm::vec3& rotation);
	void SetScale(const glm::vec3& scale);

private:
	glm::mat4 modelMatrix;
	glm::vec3 position;
	glm::vec3 rotation;
	glm::vec3 scale;

	Mesh mesh;
	Material material;

	void UpdateModelMatrix();
};

Material.h:

#pragma once

#include <vulkan/vulkan.h>
#include "Texture.h"
#include "Shader.h"

class Material
{
public:
	Material();
	~Material();

	void Initialize(const Shader& vertexShader, const Shader& fragmentShader, const Texture& texture, VkDevice device, VkDescriptorSetLayout descriptorSetLayout, VkDescriptorPool descriptorPool);
	void Cleanup();

	VkDescriptorSet GetDescriptorSet() const;
	VkPipelineLayout GetPipelineLayout() const;

private:
	VkDevice device;
	Shader vertexShader;
	Shader fragmentShader;
	Texture texture;

	VkDescriptorSet descriptorSet;
	VkPipelineLayout pipelineLayout;
};

Mesh.h:

#pragma once

#include <vector>
#include <vulkan/vulkan.h>
#include <glm/glm.hpp>

struct Vertex
{
	glm::vec3 position;
	glm::vec3 color;
};

class Mesh
{
public:
	Mesh();
	~Mesh();

	void Initialize(std::vector<Vertex> vertices, std::vector<uint32_t> indices, VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue graphicsQueue);
	void Cleanup();

	const std::vector<Vertex>& GetVertices() const;
	const std::vector<uint32_t>& GetIndices() const;
	VkBuffer GetVertexBuffer() const;
	VkBuffer GetIndexBuffer() const;

private:
	VkDevice device;
	std::vector<Vertex> vertices;
	std::vector<uint32_t> indices;

	VkBuffer vertexBuffer;
	VkDeviceMemory vertexBufferMemory;
	VkBuffer indexBuffer;
	VkDeviceMemory indexBufferMemory;
};

Pipeline.h:

#pragma once

#include <vulkan/vulkan.h>
#include <vector>
#include <array>
#include <stdexcept>
#include "Shader.h"

class Pipeline
{
public:
	Pipeline();
	~Pipeline();

	void CreateGraphicsPipeline(const std::vector<VkVertexInputBindingDescription>& vertexBindingDescriptions,
		const std::vector<VkVertexInputAttributeDescription>& vertexAttributeDescriptions,
		VkExtent2D swapchainExtent,
		const std::vector<Shader*>& shaders,
		VkRenderPass renderPass,
		VkPipelineLayout pipelineLayout,
		VkDevice device);

	void Cleanup();

	VkPipeline GetPipeline() const;

private:
	VkDevice device;
	VkPipeline pipeline;

	void CreateShaderStages(const std::vector<Shader*>& shaders, std::vector<VkPipelineShaderStageCreateInfo>& shaderStages);
};

Renderer.h:

#pragma once

#include <vulkan/vulkan.h>
#include "Window.h"
#include <vector>
#include <stdexcept>
#include <set>
#include <optional>

struct QueueFamilyIndices
{
	std::optional<uint32_t> graphicsFamily;
	std::optional<uint32_t> presentFamily;

	bool IsComplete()
	{
		return graphicsFamily.has_value() && presentFamily.has_value();
	}
};

struct SwapChainSupportDetails {
	VkSurfaceCapabilitiesKHR capabilities;
	std::vector<VkSurfaceFormatKHR> formats;
	std::vector<VkPresentModeKHR> presentModes;
};



class Renderer
{
public:
	Renderer();
	~Renderer();

	void Initialize(GLFWwindow* window);
	void Shutdown();

	void BeginFrame();
	void EndFrame();

private:

	std::vector<VkImage> swapChainImages;
	std::vector<VkImageView> swapChainImageViews;
	VkExtent2D swapChainExtent;
	VkRenderPass renderPass;

	VkFormat swapChainImageFormat;

	std::vector<VkCommandBuffer> commandBuffers;

	void CreateImageViews();
	void CleanupImageViews();
	void CreateRenderPass();
	void CleanupRenderPass();

	void CreateSurface();
	void DestroySurface();

	void CreateInstance();
	void CleanupInstance();
	void ChoosePhysicalDevice();
	void CreateDevice();
	void CleanupDevice();
	void CreateSwapchain();
	void CleanupSwapchain();
	void CreateCommandPool();
	void CleanupCommandPool();
	void CreateFramebuffers();
	void CleanupFramebuffers();

	void CreateCommandBuffers();
	void CleanupCommandBuffers();

	GLFWwindow* window;
	VkInstance instance;
	VkPhysicalDevice physicalDevice;
	VkDevice device;

	VkSurfaceKHR surface;
	VkSwapchainKHR swapchain;
	VkCommandPool commandPool;
	VkCommandBuffer currentCommandBuffer;

	std::vector<VkFramebuffer> framebuffers;

	// Additional Vulkan objects needed for rendering…

	const uint32_t kMaxFramesInFlight = 2;
	std::vector<VkSemaphore> imageAvailableSemaphores;
	std::vector<VkSemaphore> renderFinishedSemaphores;
	std::vector<VkFence> inFlightFences;
	size_t currentFrame;

	VkQueue graphicsQueue;
	VkQueue presentQueue;

	void CreateSyncObjects();
	void CleanupSyncObjects();

	SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device, VkSurfaceKHR surface);
	VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
	VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes);
	VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, GLFWwindow* window);

	std::vector<const char*> deviceExtensions = {
		VK_KHR_SWAPCHAIN_EXTENSION_NAME
	};

	std::vector<const char*> CheckPhysicalDeviceExtensionSupport(VkPhysicalDevice physicalDevice);
	QueueFamilyIndices GetQueueFamilyIndices(VkPhysicalDevice physicalDevice);
};

Scene.h:

#pragma once

#include <vector>
#include "GameObject.h"
#include "Camera.h"
#include "Renderer.h"

class Scene
{
public:
	Scene();
	~Scene();

	void Initialize();
	void Update(float deltaTime);
	void Render(Renderer& renderer);
	void Shutdown();

	void AddGameObject(GameObject* gameObject);
	Camera& GetCamera();

private:
	std::vector<GameObject*> gameObjects;
	Camera camera;
};

Shader.h:

#pragma once

#include <vulkan/vulkan.h>
#include <string>

class Shader
{
public:
	Shader();
	~Shader();

	void LoadFromFile(const std::string& filename, VkDevice device, VkShaderStageFlagBits stage);
	void Cleanup();
	VkPipelineShaderStageCreateInfo GetPipelineShaderStageCreateInfo() const;

private:
	VkDevice device;
	VkShaderModule shaderModule;
	VkShaderStageFlagBits stage;
};

Texture.h:

#pragma once

#include <vulkan/vulkan.h>

#include "stb_image.h" // Include the stb_image header
#include "BufferUtils.h"
#include <string>

class Texture
{
public:
	Texture();
	~Texture();

	void LoadFromFile(const std::string& filename, VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue graphicsQueue);
	void Cleanup();

	VkImageView GetImageView() const;
	VkSampler GetSampler() const;

private:
	VkDevice device;
	VkImage image;
	VkDeviceMemory imageMemory;
	VkImageView imageView;
	VkSampler sampler;
	VkPhysicalDevice physicalDevice;
	VkCommandPool commandPool;
	VkQueue graphicsQueue;

	void CreateImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties);
	void TransitionImageLayout(VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels);
	void CreateImageView(VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels);
	void CreateSampler(uint32_t mipLevels);
	void CopyBufferToImage(VkBuffer buffer, uint32_t width, uint32_t height);

	// Additional helper functions for texture loading…
};

Window.h:

#pragma once

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

class Window
{
public:
	Window(int width = 800, int height = 600, const char* title = "Game Engine");
	~Window();

	void Initialize();
	void PollEvents();
	void Shutdown();

	bool ShouldClose() const;
	GLFWwindow* GetWindow() const;

	float GetDeltaTime();

private:
	static void FramebufferResizeCallback(GLFWwindow* window, int width, int height);

	int width;
	int height;
	const char* title;
	GLFWwindow* window;
	double lastFrameTime;
};

Is there anything that is missing or might need to be changed?