← index
libmx2/libmx/vk_skybox/vk.hpp
Source: libmx2/libmx/vk_skybox/vk.hpp
#ifndef _VK_SKYBOX_H__
#define _VK_SKYBOX_H__

#include "config.h"
#ifndef WITH_MOLTEN
#include "volk.h"
#include <SDL_vulkan.h>
#else
#include <SDL_vulkan.h>
#include <vulkan/vulkan.h>
#endif

#include "exception.hpp"
#include "joystick.hpp"
#include "util.hpp"
#include "vk_model.hpp"

#include <algorithm>
#include <array>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <format>
#include <fstream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <optional>
#include <set>
#include <stdexcept>
#include <vector>

#ifndef VK_CHECK_RESULT
#define VK_CHECK_RESULT(f)                                                                                                              \
    {                                                                                                                                   \
        VkResult res = (f);                                                                                                             \
        if (res != VK_SUCCESS) {                                                                                                        \
            throw mx::Exception(std::format("Fatal : VkResult is \"{}\" in {} at line {}", static_cast<int>(res), __FILE__, __LINE__)); \
        }                                                                                                                               \
    }
#endif

namespace mx {

    using Vertex = VKVertex;

    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;
    };

    struct SkyboxUBO {
        alignas(16) glm::mat4 model;
        alignas(16) glm::mat4 view;
        alignas(16) glm::mat4 proj;
        alignas(16) glm::vec4 params;
    };

    struct ExtPushConstants {
        glm::vec2 screenSize;
        glm::vec2 spritePos;
        glm::vec2 spriteSize;
        glm::vec2 _pad;
        glm::vec4 params;
    };

    struct SpriteExtendedUBO {
        glm::vec4 mouse;
        glm::vec4 u0;
        glm::vec4 u1;
        glm::vec4 u2;
        glm::vec4 u3;
    };

    struct FractalPushConstants {
        double centerX;
        double centerY;
        double zoom;
        int maxIterations;
        float time;
    };

    class SkyboxViewer {
      public:
        SkyboxViewer() = default;
        SkyboxViewer(const std::string &title, int width, int height, bool full = false, const std::string &shaderDir = "");
        virtual ~SkyboxViewer() {}

        void initWindow(const std::string &title, int width, int height, bool full = false);
        void initVulkan();
        void setPath(const std::string &path);
        void setShaderPath(const std::string &path);
        void openFile(const std::string &filename);
        void openCamera(int device, int width, int height);
        void activateFirstShader();
        void loop();
        void cleanup();
        void event(SDL_Event &e);
        void draw();
        void quit();
        void proc();

        int w = 0, h = 0;
        mxUtil util;

      protected:
        bool active = true;

        MXModel model;

        VkInstance instance = VK_NULL_HANDLE;
        VkSurfaceKHR surface = VK_NULL_HANDLE;
        VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
        VkDevice device = VK_NULL_HANDLE;
        VkQueue graphicsQueue = VK_NULL_HANDLE;
        VkQueue presentQueue = VK_NULL_HANDLE;
        VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;

        VkSwapchainKHR swapChain = VK_NULL_HANDLE;
        std::vector<VkImage> swapChainImages;
        VkFormat swapChainImageFormat;
        VkExtent2D swapChainExtent;
        std::vector<VkImageView> swapChainImageViews;

        std::vector<VkBuffer> uniformBuffers;
        std::vector<VkDeviceMemory> uniformBuffersMemory;
        std::vector<void *> uniformBuffersMapped;

        VkRenderPass renderPass = VK_NULL_HANDLE;
        VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
        VkPipeline graphicsPipeline = VK_NULL_HANDLE;
        std::vector<VkFramebuffer> swapChainFramebuffers;

        VkCommandPool commandPool = VK_NULL_HANDLE;
        std::vector<VkCommandBuffer> commandBuffers;

        VkSemaphore imageAvailableSemaphore = VK_NULL_HANDLE;
        VkSemaphore renderFinishedSemaphore = VK_NULL_HANDLE;

        VkImage cubemapImage = VK_NULL_HANDLE;
        VkDeviceMemory cubemapImageMemory = VK_NULL_HANDLE;
        VkImageView cubemapImageView = VK_NULL_HANDLE;
        VkSampler cubemapSampler = VK_NULL_HANDLE;

        // Video texture (used when --filename is supplied)
        cv::VideoCapture cap;
        std::string filename;
        int camera_width = 0, camera_height = 0;
        VkImage textureImage = VK_NULL_HANDLE;
        VkDeviceMemory textureImageMemory = VK_NULL_HANDLE;
        VkImageView textureImageView = VK_NULL_HANDLE;
        VkSampler textureSampler = VK_NULL_HANDLE;

        // External shader support
        bool useExternalShaders = false;
        bool useVideoTexture = false;
        std::string shaderPath;
        std::vector<std::string> shaderFiles;
        std::vector<std::string> effects;
        int effectIndex = 0;
        VkPipelineLayout extPipelineLayout = VK_NULL_HANDLE;
        VkDescriptorSetLayout extFragDescSetLayout = VK_NULL_HANDLE;
        VkDescriptorSetLayout extVertDescSetLayout = VK_NULL_HANDLE;
        VkDescriptorPool extDescPool = VK_NULL_HANDLE;
        std::vector<VkDescriptorSet> extFragDescSets;
        std::vector<VkDescriptorSet> extVertDescSets;
        std::vector<VkBuffer> extSpriteBuffers;
        std::vector<VkDeviceMemory> extSpriteMemory;
        std::vector<void *> extSpriteMapped;

        VkImage depthImage = VK_NULL_HANDLE;
        VkDeviceMemory depthImageMemory = VK_NULL_HANDLE;
        VkImageView depthImageView = VK_NULL_HANDLE;
        VkFormat depthFormat = VK_FORMAT_D32_SFLOAT;

        VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
        std::vector<VkDescriptorSet> descriptorSets;

        float yaw = 0.0f;
        float pitch = 0.0f;
        bool mouseControl = false;
        int lastMouseX = 0, lastMouseY = 0;
        float mouseSensitivity = 0.15f;

        double fractalCenterX = -0.5;
        double fractalCenterY = 0.0;
        double fractalZoom = 0.5;
        int fractalMaxIter = 256;

        mx::Controller controller;
        SDL_Window *window = nullptr;

        void createInstance();
        void createSurface();
        void pickPhysicalDevice();
        void createLogicalDevice();
        void createSwapChain();
        void createImageViews();
        void createRenderPass();
        void createDepthResources();
        void createDescriptorSetLayout();
        void createGraphicsPipeline();
        void createFramebuffers();
        void createCommandPool();
        void createCommandBuffers();
        void createSyncObjects();
        void createUniformBuffers();
        void loadModelData();
        void loadCubemapTexture();
        void createCubemapSampler();
        void createDescriptorPool();
        void createDescriptorSets();
        void cleanupSwapChain();
        void recreateSwapChain();
        void loadShaderIndex(const std::string &shaderDir);
        void swapFragmentShader(size_t shaderIndex);
        void setupTextureImage(int width, int height);
        void createTextureImageView();
        void updateTextureFromFrame(const cv::Mat &frame);
        void updateExtFragDescriptors();

        QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);
        VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR> &availableFormats);
        VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR> &availablePresentModes);
        VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities);
        bool isDeviceSuitable(VkPhysicalDevice device);
        SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device);
        VkShaderModule createShaderModule(const std::vector<char> &code);

        void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer &buffer, VkDeviceMemory &bufferMemory);
        uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
        void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
        void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t layerCount = 1);
        void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t w, uint32_t h, uint32_t layer = 0);
        VkCommandBuffer beginSingleTimeCommands();
        void endSingleTimeCommands(VkCommandBuffer commandBuffer);
        VkFormat findSupportedFormat(const std::vector<VkFormat> &candidates, VkImageTiling tiling, VkFormatFeatureFlags features);
        VkFormat findDepthFormat();
    };
} // namespace mx

#endif