lotsa things

This commit is contained in:
Mars 2024-10-19 00:41:14 -04:00
parent f99ff172db
commit 31f772eb2d
Signed by: pupbrained
GPG key ID: 0FF5B8826803F895
6 changed files with 390 additions and 206 deletions

View file

@ -23,9 +23,10 @@
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
// Include custom utility headers // Include custom utility headers
#include "util/types.h" // Custom type definitions #include "util/shaders.hpp" // Compiled shader code
#include "util/unique_image.h" // Custom image handling utilities #include "util/types.hpp" // Custom type definitions
#include "util/vertex.h" // Custom vertex structure definition #include "util/unique_image.hpp" // Custom image handling utilities
#include "util/vertex.hpp" // Custom vertex structure definition
// ImGui headers for GUI // ImGui headers for GUI
#include <imgui.h> #include <imgui.h>
@ -96,14 +97,20 @@ constexpr std::array<const char*, 2> deviceExtensions = { vk::KHRSwapchainExtens
constexpr std::array<const char*, 1> deviceExtensions = { vk::KHRSwapchainExtensionName }; constexpr std::array<const char*, 1> deviceExtensions = { vk::KHRSwapchainExtensionName };
#endif #endif
// Main application class /**
* @brief The Vulkan application class.
*
* This class encapsulates the entire Vulkan application, managing the Vulkan
* instance, window, rendering loop, and resource cleanup. It handles the
* initialization of Vulkan, manages resources, and orchestrates rendering.
*/
class VulkanApp { class VulkanApp {
public: public:
/** /**
* @brief Runs the Vulkan application. * @brief Runs the Vulkan application.
* *
* This function initializes the application window, sets up Vulkan, and enters the main rendering loop. * This function initializes the application window, sets up Vulkan, and enters
* It also cleans up resources when the application is closed. * the main rendering loop. It also cleans up resources when the application is closed.
*/ */
fn run() -> void { fn run() -> void {
initWindow(); // Initialize the application window initWindow(); // Initialize the application window
@ -125,154 +132,113 @@ class VulkanApp {
} }
private: private:
// GLFW and Vulkan instance handles vkfw::UniqueInstance mVKFWInstance; ///< GLFW instance
vkfw::UniqueInstance mVKFWInstance; // GLFW instance vkfw::UniqueWindow mWindow; ///< Application window
vkfw::UniqueWindow mWindow; // Application window vk::UniqueInstance mInstance; ///< Vulkan instance
vk::UniqueInstance mInstance; // Vulkan instance
// Debug messenger for validation layers vk::UniqueDebugUtilsMessengerEXT mDebugMessenger; ///< Debug messenger
vk::UniqueDebugUtilsMessengerEXT mDebugMessenger;
// Vulkan surface for rendering vk::UniqueSurfaceKHR mSurface; ///< Vulkan surface for rendering
vk::UniqueSurfaceKHR mSurface;
// Vulkan devices and queues vk::PhysicalDevice mPhysicalDevice; ///< Physical GPU
vk::PhysicalDevice mPhysicalDevice; // Physical GPU vk::SampleCountFlagBits mMsaaSamples; ///< Multisampling count
vk::SampleCountFlagBits mMsaaSamples; // Multisampling count vk::UniqueDevice mDevice; ///< Logical Vulkan device
vk::UniqueDevice mDevice; // Logical Vulkan device
vk::Queue mGraphicsQueue; // Queue for graphics commands vk::Queue mGraphicsQueue; ///< Queue for graphics commands
vk::Queue mPresentQueue; // Queue for presentation commands vk::Queue mPresentQueue; ///< Queue for presentation commands
// Swap chain and related resources vk::UniqueSwapchainKHR mSwapChain; ///< Swap chain for frame buffering
vk::UniqueSwapchainKHR mSwapChain; // Swap chain for frame buffering std::vector<vk::Image> mSwapChainImages; ///< Images in the swap chain
std::vector<vk::Image> mSwapChainImages; // Images in the swap chain vk::Format mSwapChainImageFormat; ///< Format of swap chain images
vk::Format mSwapChainImageFormat; // Format of swap chain images vk::Extent2D mSwapChainExtent; ///< Dimensions of swap chain images
vk::Extent2D mSwapChainExtent; // Dimensions of swap chain images std::vector<vk::UniqueImageView> mSwapChainImageViews; ///< Image views for swap chain images
std::vector<vk::UniqueImageView> mSwapChainImageViews; // Image views for swap chain images std::vector<vk::UniqueFramebuffer> mSwapChainFramebuffers; ///< Framebuffers for rendering
std::vector<vk::UniqueFramebuffer> mSwapChainFramebuffers; // Framebuffers for rendering
// Render pass and pipeline objects vk::UniqueRenderPass mRenderPass; ///< Render pass definition
vk::UniqueRenderPass mRenderPass; // Render pass definition vk::UniqueDescriptorSetLayout mDescriptorSetLayout; ///< Descriptor set layout
vk::UniqueDescriptorSetLayout mDescriptorSetLayout; // Descriptor set layout vk::UniquePipelineLayout mPipelineLayout; ///< Pipeline layout
vk::UniquePipelineLayout mPipelineLayout; // Pipeline layout vk::UniquePipeline mGraphicsPipeline; ///< Graphics pipeline
vk::UniquePipeline mGraphicsPipeline; // Graphics pipeline
// Command pool for allocating command buffers vk::UniqueCommandPool mCommandPool; ///< Command pool for allocating command buffers
vk::UniqueCommandPool mCommandPool;
// Multisampling color resources vk::UniqueImage mColorImage; ///< Color image
vk::UniqueImage mColorImage; vk::UniqueDeviceMemory mColorImageMemory; ///< Memory for color image
vk::UniqueDeviceMemory mColorImageMemory; vk::UniqueImageView mColorImageView; ///< Image view for color image
vk::UniqueImageView mColorImageView;
// Depth buffer resources vk::UniqueImage mDepthImage; ///< Depth image
vk::UniqueImage mDepthImage; vk::UniqueDeviceMemory mDepthImageMemory; ///< Memory for depth image
vk::UniqueDeviceMemory mDepthImageMemory; vk::UniqueImageView mDepthImageView; ///< Image view for depth image
vk::UniqueImageView mDepthImageView;
// Texture resources u32 mMipLevels; ///< Number of mipmap levels
u32 mMipLevels; // Number of mipmap levels vk::UniqueImage mTextureImage; ///< Texture image
vk::UniqueImage mTextureImage; // Texture image vk::UniqueDeviceMemory mTextureImageMemory; ///< Memory for texture image
vk::UniqueDeviceMemory mTextureImageMemory; // Memory for texture image vk::UniqueImageView mTextureImageView; ///< Image view for texture
vk::UniqueImageView mTextureImageView; // Image view for texture vk::UniqueSampler mTextureSampler; ///< Sampler for texture
vk::UniqueSampler mTextureSampler; // Sampler for texture
// Vertex and index data std::vector<Vertex> mVertices; ///< Vertex data for the model
std::vector<Vertex> mVertices; // Vertex data for the model std::vector<u32> mIndices; ///< Index data for the model
std::vector<u32> mIndices; // Index data for the model vk::UniqueBuffer mVertexBuffer; ///< Buffer for vertex data
vk::UniqueBuffer mVertexBuffer; // Buffer for vertex data vk::UniqueDeviceMemory mVertexBufferMemory; ///< Memory for vertex buffer
vk::UniqueDeviceMemory mVertexBufferMemory; // Memory for vertex buffer vk::UniqueBuffer mIndexBuffer; ///< Buffer for index data
vk::UniqueBuffer mIndexBuffer; // Buffer for index data vk::UniqueDeviceMemory mIndexBufferMemory; ///< Memory for index buffer
vk::UniqueDeviceMemory mIndexBufferMemory; // Memory for index buffer
// Uniform buffers for shader parameters std::vector<vk::UniqueBuffer> mUniformBuffers; ///< Uniform buffers for shader parameters
std::vector<vk::UniqueBuffer> mUniformBuffers; std::vector<vk::UniqueDeviceMemory> mUniformBuffersMemory; ///< Memory for uniform buffers
std::vector<vk::UniqueDeviceMemory> mUniformBuffersMemory; std::vector<void*> mUniformBuffersMapped; ///< Mapped uniform buffers
std::vector<void*> mUniformBuffersMapped;
// Descriptor pools and sets vk::UniqueDescriptorPool mDescriptorPool; ///< Descriptor pool for the application
vk::UniqueDescriptorPool mDescriptorPool; // Descriptor pool for the application vk::UniqueDescriptorPool mImGuiDescriptorPool; ///< Separate descriptor pool for ImGui
vk::UniqueDescriptorPool mImGuiDescriptorPool; // Separate descriptor pool for ImGui std::vector<vk::DescriptorSet> mDescriptorSets; ///< Descriptor sets for binding resources
std::vector<vk::DescriptorSet> mDescriptorSets; // Descriptor sets for binding resources
// Command buffers for recording drawing commands std::vector<vk::UniqueCommandBuffer> mCommandBuffers; ///< Command buffers for drawing
std::vector<vk::UniqueCommandBuffer> mCommandBuffers;
// Synchronization primitives
std::vector<vk::UniqueSemaphore> std::vector<vk::UniqueSemaphore>
mImageAvailableSemaphores; // Signals that an image is available for rendering mImageAvailableSemaphores; ///< Signals that an image is available for rendering
std::vector<vk::UniqueSemaphore> mRenderFinishedSemaphores; // Signals that rendering has finished std::vector<vk::UniqueSemaphore> mRenderFinishedSemaphores; ///< Signals that rendering has finished
std::vector<vk::UniqueFence> mInFlightFences; // Ensures CPU-GPU synchronization std::vector<vk::UniqueFence> mInFlightFences; ///< Ensures CPU-GPU synchronization
// State variables bool mFramebufferResized = false; ///< Flag indicating if the framebuffer was resized
bool mFramebufferResized = false; // Flag indicating if the framebuffer was resized u32 mCurrentFrame = 0; ///< Index of the current frame being rendered
u32 mCurrentFrame = 0; // Index of the current frame being rendered
// Helper struct to store queue family indices /**
* @brief Struct to store queue family indices.
*
* This struct contains the indices of the graphics and presentation queue families.
*/
struct QueueFamilyIndices { struct QueueFamilyIndices {
std::optional<u32> graphics_family; // Index of graphics queue family std::optional<u32> graphics_family; ///< Index of graphics queue family
std::optional<u32> present_family; // Index of presentation queue family std::optional<u32> present_family; ///< Index of presentation queue family
// Check if all required queue families are found /**
* @brief Check if all required queue families are found.
*
* @return True if both graphics and presentation families are found, false otherwise.
*/
fn isComplete() -> bool { return graphics_family.has_value() && present_family.has_value(); } fn isComplete() -> bool { return graphics_family.has_value() && present_family.has_value(); }
}; };
// Helper struct to store swap chain support details /**
* @brief Struct to hold swap chain support details.
*
* This struct contains information about the surface capabilities,
* supported formats, and presentation modes.
*/
struct SwapChainSupportDetails { struct SwapChainSupportDetails {
vk::SurfaceCapabilitiesKHR capabilities; // Surface capabilities vk::SurfaceCapabilitiesKHR capabilities; ///< Surface capabilities
std::vector<vk::SurfaceFormatKHR> formats; // Supported surface formats std::vector<vk::SurfaceFormatKHR> formats; ///< Supported surface formats
std::vector<vk::PresentModeKHR> present_modes; // Supported presentation modes std::vector<vk::PresentModeKHR> present_modes; ///< Supported presentation modes
}; };
// Struct to hold uniform buffer object data
struct UniformBufferObject {
alignas(16) glm::mat4 model; // Model transformation matrix
alignas(16) glm::mat4 view; // View transformation matrix
alignas(16) glm::mat4 proj; // Projection matrix
};
static fn compileShader(const std::string& source, const shaderc_shader_kind& kind)
-> std::vector<uint32_t> {
shaderc::Compiler compiler;
shaderc::CompileOptions options;
shaderc::CompilationResult<u32> result =
compiler.CompileGlslToSpv(source, kind, "shader.glsl", "main", options);
if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
fmt::println(stderr, "Shader compilation failed: {}", result.GetErrorMessage());
return {};
}
return { result.cbegin(), result.cend() };
}
/** /**
* @brief Reads the contents of a file into a vector of chars. * @brief Struct representing a uniform buffer object.
* *
* @param filename The path to the file to be read. * This struct holds the model, view, and projection matrices for use in shaders.
* @return std::vector<char> A vector containing the file's contents.
* @throws std::runtime_error if the file cannot be opened.
*
* This function opens the specified file in binary mode, reads its entire contents
* into a vector of chars, and returns that vector. It's commonly used for reading
* shader files or other binary data needed by the application.
*/ */
static fn readFile(const std::string& filename) -> std::vector<char> { struct UniformBufferObject {
// Open the file in binary mode alignas(16) glm::mat4 model; ///< Model transformation matrix
std::ifstream file(filename, std::ios::binary); alignas(16) glm::mat4 view; ///< View transformation matrix
alignas(16) glm::mat4 proj; ///< Projection matrix
// Check if the file was successfully opened };
if (!file)
throw std::runtime_error("Failed to open file: " + filename);
// Read the entire file content into a vector
// This constructor reads from the start (first iterator) to the end (default constructor) of the stream
std::vector<char> buffer((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
return buffer;
}
/** /**
* @brief Update the FPS counter in the window title. * @brief Update the FPS counter in the window title.
@ -282,13 +248,13 @@ class VulkanApp {
* *
* This function updates the window title with the current frames per second. * This function updates the window title with the current frames per second.
*/ */
static fn updateFPS(const vkfw::Window& window, const std::string& baseTitle) -> void { static fn updateFPS(const vkfw::Window& window, const string& baseTitle) -> void {
static int FrameCount = 0; static u32 FrameCount = 0;
static double LastTime = glfwGetTime(); static f64 LastTime = glfwGetTime();
static double Fps = 0.0; static f64 Fps = 0.0;
// Get the current time // Get the current time
double currentTime = glfwGetTime(); f64 currentTime = glfwGetTime();
FrameCount++; FrameCount++;
// If one second has passed, calculate the FPS // If one second has passed, calculate the FPS
@ -298,7 +264,7 @@ class VulkanApp {
FrameCount = 0; FrameCount = 0;
// Update window title // Update window title
std::string newTitle = format("{} - {:.0F} FPS", baseTitle, Fps); string newTitle = format("{} - {:.0F} FPS", baseTitle, Fps);
glfwSetWindowTitle(window, newTitle.c_str()); glfwSetWindowTitle(window, newTitle.c_str());
} }
} }
@ -428,7 +394,7 @@ class VulkanApp {
.DescriptorPool = mImGuiDescriptorPool.get(), .DescriptorPool = mImGuiDescriptorPool.get(),
.RenderPass = mRenderPass.get(), .RenderPass = mRenderPass.get(),
.MinImageCount = MAX_FRAMES_IN_FLIGHT, .MinImageCount = MAX_FRAMES_IN_FLIGHT,
.ImageCount = static_cast<uint32_t>(mSwapChainImages.size()), .ImageCount = static_cast<u32>(mSwapChainImages.size()),
.MSAASamples = static_cast<VkSampleCountFlagBits>(mMsaaSamples), .MSAASamples = static_cast<VkSampleCountFlagBits>(mMsaaSamples),
.PipelineCache = VK_NULL_HANDLE, .PipelineCache = VK_NULL_HANDLE,
.Subpass = 0, .Subpass = 0,
@ -525,7 +491,7 @@ class VulkanApp {
.applicationVersion = 1, .applicationVersion = 1,
.pEngineName = "No Engine", .pEngineName = "No Engine",
.engineVersion = 1, .engineVersion = 1,
.apiVersion = vk::ApiVersion12, .apiVersion = vk::ApiVersion13,
}; };
// Get the required extensions // Get the required extensions
@ -898,9 +864,9 @@ class VulkanApp {
*/ */
fn createGraphicsPipeline() -> void { fn createGraphicsPipeline() -> void {
std::vector<u32> vertShaderCode = std::vector<u32> vertShaderCode =
compileShader(vertShaderSrc, shaderc_shader_kind::shaderc_vertex_shader); ShaderCompiler::getCompiledShader(vertShaderSrc, shaderc_shader_kind::shaderc_vertex_shader, "vert");
std::vector<u32> fragShaderCode = std::vector<u32> fragShaderCode =
compileShader(fragShaderSrc, shaderc_shader_kind::shaderc_fragment_shader); ShaderCompiler::getCompiledShader(fragShaderSrc, shaderc_shader_kind::shaderc_fragment_shader, "frag");
vk::UniqueShaderModule vertShaderModule = createShaderModule(vertShaderCode); vk::UniqueShaderModule vertShaderModule = createShaderModule(vertShaderCode);
vk::UniqueShaderModule fragShaderModule = createShaderModule(fragShaderCode); vk::UniqueShaderModule fragShaderModule = createShaderModule(fragShaderCode);
@ -1582,7 +1548,7 @@ class VulkanApp {
tinyobj::attrib_t attrib; tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes; std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials; std::vector<tinyobj::material_t> materials;
std::string warn, err; string warn, err;
std::filesystem::path modelPath = std::filesystem::current_path() / "models" / "viking_room.obj"; std::filesystem::path modelPath = std::filesystem::current_path() / "models" / "viking_room.obj";

166
src/util/shaders.hpp Normal file
View file

@ -0,0 +1,166 @@
#pragma once
#include <filesystem>
#include <fmt/base.h>
#include <fstream>
#include <shaderc/shaderc.hpp>
#include <string>
#include <vector>
#include "types.hpp"
class ShaderCompiler {
public:
ShaderCompiler() = default;
/**
* @brief Compiles or loads a cached SPIR-V shader from a file.
*
* @param shaderSource The source code of the shader.
* @param kind The type of shader being compiled (vertex, fragment, etc.).
* @param shaderName The name used for caching the compiled shader.
* @return std::vector<u32> A vector containing the compiled SPIR-V code.
* @throws std::runtime_error if shader compilation fails.
*
* This function attempts to load a shader from the cache. If the shader
* is not found in the cache, it compiles the shader from the source code
* and saves the result to the cache for future use.
*/
static fn getCompiledShader(
const char* shaderSource,
const shaderc_shader_kind& kind,
const string& shaderName
) -> std::vector<u32> {
using namespace std;
const string cacheFile = getCacheFilePath(shaderName);
// Try loading from cache first
vector<u32> spirvCode = loadCachedShader(cacheFile);
if (!spirvCode.empty()) {
fmt::println("Loaded shader from cache: {}", cacheFile);
return spirvCode;
}
// Cache miss, compile the shader
spirvCode = compileShader(shaderSource, kind);
if (spirvCode.empty())
throw runtime_error("Shader compilation failed for: " + shaderName);
// Cache the compiled SPIR-V binary
saveCompiledShader(spirvCode, cacheFile);
return spirvCode;
}
private:
/**
* @brief Generates the cache file path based on the operating system.
*
* @param shaderName The name used for the shader file.
* @return string The full path to the cache file.
*
* This function determines the appropriate directory for caching shaders
* based on the operating system and returns the full path for the specified shader name.
*/
static fn getCacheFilePath(const string& shaderName) -> string {
using namespace std::filesystem;
#ifdef _WIN32
path cacheDir = path(getenv("LOCALAPPDATA")) / "VulkanApp" / "Shaders";
#elif defined(__APPLE__)
path cacheDir = path(getenv("HOME")) / "Library" / "Application Support" / "VulkanApp" / "Shaders";
#else // Assume Linux or other UNIX-like systems
path cacheDir = path(getenv("HOME")) / ".local" / "share" / "VulkanApp" / "Shaders";
#endif
if (!exists(cacheDir))
create_directories(cacheDir); // Create the directory if it doesn't exist
return (cacheDir / (shaderName + ".spv")).string();
}
/**
* @brief Compiles GLSL code to SPIR-V.
*
* @param source The GLSL source code to compile.
* @param kind The type of shader being compiled.
* @return std::vector<u32> A vector containing the compiled SPIR-V code.
*
* This function uses the shaderc library to compile GLSL source code into
* SPIR-V binary format. If the compilation fails, an empty vector is returned.
*/
static fn compileShader(const char* source, const shaderc_shader_kind& kind) -> std::vector<u32> {
using namespace shaderc;
Compiler compiler;
CompileOptions options;
SpvCompilationResult result = compiler.CompileGlslToSpv(source, kind, "shader.glsl", "main", options);
if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
fmt::println(stderr, "Shader compilation failed: {}", result.GetErrorMessage());
return {};
}
return { result.cbegin(), result.cend() };
}
/**
* @brief Loads compiled SPIR-V shader code from a cache file.
*
* @param cacheFile The path to the cached SPIR-V file.
* @return std::vector<u32> A vector containing the loaded SPIR-V code.
* @throws std::runtime_error if the file cannot be read.
*
* This function checks if the specified cache file exists and reads its
* contents into a vector. If the file cannot be opened, an exception is thrown.
*/
static fn loadCachedShader(const string& cacheFile) -> std::vector<u32> {
using namespace std;
if (!filesystem::exists(cacheFile))
return {}; // No cached file
ifstream file(cacheFile, ios::binary);
// Check if the file was successfully opened
if (!file)
throw runtime_error("Failed to open cached shader file: " + cacheFile);
usize fileSize = filesystem::file_size(cacheFile);
vector<u32> spirvCode(fileSize / sizeof(u32));
// Read entire file content into the vector
if (!file.read(bit_cast<char*>(spirvCode.data()), static_cast<streamsize>(fileSize)))
throw runtime_error("Failed to read cached shader file: " + cacheFile);
return spirvCode;
}
/**
* @brief Saves compiled SPIR-V binary to a cache file.
*
* @param spirvCode The SPIR-V code to save.
* @param cacheFile The path to the file where the SPIR-V code will be saved.
* @throws std::runtime_error if the file cannot be written.
*
* This function writes the compiled SPIR-V binary to the specified file.
* If the file cannot be opened or written, an exception is thrown.
*/
static fn saveCompiledShader(const std::vector<u32>& spirvCode, const string& cacheFile) -> void {
using namespace std;
ofstream file(cacheFile, ios::binary);
// Check if the file was successfully opened
if (!file)
throw runtime_error("Failed to open file for saving shader: " + cacheFile);
if (!file.write(
bit_cast<const char*>(spirvCode.data()), static_cast<streamsize>(spirvCode.size() * sizeof(u32))
))
throw runtime_error("Failed to save shader to cache: " + cacheFile);
}
};

View file

@ -1,69 +0,0 @@
#include <filesystem>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include "types.h"
namespace stb {
class UniqueImage {
public:
// Constructor
UniqueImage(const std::filesystem::path& path) { load(path.string().c_str()); }
// Deleted copy constructor and assignment operator
UniqueImage(const UniqueImage&) = delete;
fn operator=(const UniqueImage&)->UniqueImage& = delete;
// Move constructor and assignment operator
UniqueImage(UniqueImage&& other) noexcept
: mData(other.mData), mWidth(other.mWidth), mHeight(other.mHeight), mChannels(other.mChannels) {
other.mData = nullptr; // Set the other's data to nullptr
}
fn operator=(UniqueImage&& other) noexcept -> UniqueImage& {
if (this != &other) {
// Free existing resource
if (mData)
stbi_image_free(mData);
// Move the resource
mData = other.mData;
mWidth = other.mWidth;
mHeight = other.mHeight;
mChannels = other.mChannels;
other.mData = nullptr; // Set the other's data to nullptr
}
return *this;
}
// Destructor
~UniqueImage() {
if (mData)
stbi_image_free(mData);
}
// Accessors for image data and dimensions
[[nodiscard]] fn getData() const -> unsigned char* { return mData; }
[[nodiscard]] fn getWidth() const -> int { return mWidth; }
[[nodiscard]] fn getHeight() const -> int { return mHeight; }
[[nodiscard]] fn getChannels() const -> int { return mChannels; }
private:
unsigned char* mData = nullptr;
int mWidth = 0;
int mHeight = 0;
int mChannels = 0;
// Load function
void load(const char* filename) {
mData = stbi_load(filename, &mWidth, &mHeight, &mChannels, STBI_rgb_alpha);
if (!mData)
throw std::runtime_error("Failed to load image: " + std::string(stbi_failure_reason()));
}
};
}

121
src/util/unique_image.hpp Normal file
View file

@ -0,0 +1,121 @@
#include <filesystem>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include "types.hpp"
namespace stb {
/**
* @brief A class that handles loading and managing image data.
*
* This class uses the stb_image library to load images from the filesystem
* and provides access to the image data, dimensions, and channel count.
*/
class UniqueImage {
public:
/**
* @brief Constructs a UniqueImage object and loads an image from the specified path.
*
* @param path The filesystem path to the image file to load.
*/
UniqueImage(const std::filesystem::path& path) { load(path.c_str()); }
// Deleted copy constructor to prevent copying.
UniqueImage(const UniqueImage&) = delete;
// Deleted copy assignment operator to prevent copying.
fn operator=(const UniqueImage&)->UniqueImage& = delete;
/**
* @brief Move constructor for UniqueImage.
*
* Transfers ownership of resources from another UniqueImage object.
*
* @param other The UniqueImage object from which to move resources.
*/
UniqueImage(UniqueImage&& other) noexcept
: mData(other.mData), mWidth(other.mWidth), mHeight(other.mHeight), mChannels(other.mChannels) {
other.mData = nullptr;
}
/**
* @brief Move assignment operator for UniqueImage.
*
* Transfers ownership of resources from another UniqueImage object.
*
* @param other The UniqueImage object from which to move resources.
* @return Reference to this object.
*/
fn operator=(UniqueImage&& other) noexcept -> UniqueImage& {
if (this != &other) {
if (mData)
stbi_image_free(mData);
mData = other.mData;
mWidth = other.mWidth;
mHeight = other.mHeight;
mChannels = other.mChannels;
other.mData = nullptr;
}
return *this;
}
/**
* @brief Destructor for UniqueImage.
*
* Frees the image data if it is allocated.
*/
~UniqueImage() {
if (mData)
stbi_image_free(mData);
}
/**
* @brief Retrieves the image data.
*
* @return Pointer to the image data in memory.
*/
[[nodiscard]] fn getData() const -> u8* { return mData; }
/**
* @brief Retrieves the width of the image.
*
* @return The width of the image in pixels.
*/
[[nodiscard]] fn getWidth() const -> i32 { return mWidth; }
/**
* @brief Retrieves the height of the image.
*
* @return The height of the image in pixels.
*/
[[nodiscard]] fn getHeight() const -> i32 { return mHeight; }
/**
* @brief Retrieves the number of channels in the image.
*
* @return The number of channels in the image (e.g., 3 for RGB, 4 for RGBA).
*/
[[nodiscard]] fn getChannels() const -> i32 { return mChannels; }
private:
u8* mData = nullptr; ///< Pointer to the image data.
i32 mWidth = 0; ///< Width of the image in pixels.
i32 mHeight = 0; ///< Height of the image in pixels.
i32 mChannels = 0; ///< Number of channels in the image.
/**
* @brief Loads an image from a file.
*
* @param filename The name of the file from which to load the image.
* @throws std::runtime_error If the image fails to load.
*/
fn load(const char* filename) -> void {
mData = stbi_load(filename, &mWidth, &mHeight, &mChannels, STBI_rgb_alpha);
if (!mData)
throw std::runtime_error("Failed to load image: " + string(stbi_failure_reason()));
}
};
}

View file

@ -6,7 +6,7 @@
#define VULKAN_HPP_NO_CONSTRUCTORS #define VULKAN_HPP_NO_CONSTRUCTORS
#include <vulkan/vulkan.hpp> #include <vulkan/vulkan.hpp>
#include "types.h" #include "types.hpp"
// Vertex data for the model // Vertex data for the model
struct Vertex { struct Vertex {