diff --git a/src/main.cpp b/src/main.cpp index a7e454c..7392b32 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,9 +23,10 @@ VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE // Include custom utility headers -#include "util/types.h" // Custom type definitions -#include "util/unique_image.h" // Custom image handling utilities -#include "util/vertex.h" // Custom vertex structure definition +#include "util/shaders.hpp" // Compiled shader code +#include "util/types.hpp" // Custom type definitions +#include "util/unique_image.hpp" // Custom image handling utilities +#include "util/vertex.hpp" // Custom vertex structure definition // ImGui headers for GUI #include @@ -96,14 +97,20 @@ constexpr std::array deviceExtensions = { vk::KHRSwapchainExtens constexpr std::array deviceExtensions = { vk::KHRSwapchainExtensionName }; #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 { public: /** * @brief Runs the Vulkan application. * - * This function initializes the application window, sets up Vulkan, and enters the main rendering loop. - * It also cleans up resources when the application is closed. + * This function initializes the application window, sets up Vulkan, and enters + * the main rendering loop. It also cleans up resources when the application is closed. */ fn run() -> void { initWindow(); // Initialize the application window @@ -125,154 +132,113 @@ class VulkanApp { } private: - // GLFW and Vulkan instance handles - vkfw::UniqueInstance mVKFWInstance; // GLFW instance - vkfw::UniqueWindow mWindow; // Application window - vk::UniqueInstance mInstance; // Vulkan instance + vkfw::UniqueInstance mVKFWInstance; ///< GLFW instance + vkfw::UniqueWindow mWindow; ///< Application window + vk::UniqueInstance mInstance; ///< Vulkan instance - // Debug messenger for validation layers - vk::UniqueDebugUtilsMessengerEXT mDebugMessenger; + vk::UniqueDebugUtilsMessengerEXT mDebugMessenger; ///< Debug messenger - // Vulkan surface for rendering - vk::UniqueSurfaceKHR mSurface; + vk::UniqueSurfaceKHR mSurface; ///< Vulkan surface for rendering - // Vulkan devices and queues - vk::PhysicalDevice mPhysicalDevice; // Physical GPU - vk::SampleCountFlagBits mMsaaSamples; // Multisampling count - vk::UniqueDevice mDevice; // Logical Vulkan device + vk::PhysicalDevice mPhysicalDevice; ///< Physical GPU + vk::SampleCountFlagBits mMsaaSamples; ///< Multisampling count + vk::UniqueDevice mDevice; ///< Logical Vulkan device - vk::Queue mGraphicsQueue; // Queue for graphics commands - vk::Queue mPresentQueue; // Queue for presentation commands + vk::Queue mGraphicsQueue; ///< Queue for graphics commands + vk::Queue mPresentQueue; ///< Queue for presentation commands - // Swap chain and related resources - vk::UniqueSwapchainKHR mSwapChain; // Swap chain for frame buffering - std::vector mSwapChainImages; // Images in the swap chain - vk::Format mSwapChainImageFormat; // Format of swap chain images - vk::Extent2D mSwapChainExtent; // Dimensions of swap chain images - std::vector mSwapChainImageViews; // Image views for swap chain images - std::vector mSwapChainFramebuffers; // Framebuffers for rendering + vk::UniqueSwapchainKHR mSwapChain; ///< Swap chain for frame buffering + std::vector mSwapChainImages; ///< Images in the swap chain + vk::Format mSwapChainImageFormat; ///< Format of swap chain images + vk::Extent2D mSwapChainExtent; ///< Dimensions of swap chain images + std::vector mSwapChainImageViews; ///< Image views for swap chain images + std::vector mSwapChainFramebuffers; ///< Framebuffers for rendering - // Render pass and pipeline objects - vk::UniqueRenderPass mRenderPass; // Render pass definition - vk::UniqueDescriptorSetLayout mDescriptorSetLayout; // Descriptor set layout - vk::UniquePipelineLayout mPipelineLayout; // Pipeline layout - vk::UniquePipeline mGraphicsPipeline; // Graphics pipeline + vk::UniqueRenderPass mRenderPass; ///< Render pass definition + vk::UniqueDescriptorSetLayout mDescriptorSetLayout; ///< Descriptor set layout + vk::UniquePipelineLayout mPipelineLayout; ///< Pipeline layout + vk::UniquePipeline mGraphicsPipeline; ///< Graphics pipeline - // Command pool for allocating command buffers - vk::UniqueCommandPool mCommandPool; + vk::UniqueCommandPool mCommandPool; ///< Command pool for allocating command buffers - // Multisampling color resources - vk::UniqueImage mColorImage; - vk::UniqueDeviceMemory mColorImageMemory; - vk::UniqueImageView mColorImageView; + vk::UniqueImage mColorImage; ///< Color image + vk::UniqueDeviceMemory mColorImageMemory; ///< Memory for color image + vk::UniqueImageView mColorImageView; ///< Image view for color image - // Depth buffer resources - vk::UniqueImage mDepthImage; - vk::UniqueDeviceMemory mDepthImageMemory; - vk::UniqueImageView mDepthImageView; + vk::UniqueImage mDepthImage; ///< Depth image + vk::UniqueDeviceMemory mDepthImageMemory; ///< Memory for depth image + vk::UniqueImageView mDepthImageView; ///< Image view for depth image - // Texture resources - u32 mMipLevels; // Number of mipmap levels - vk::UniqueImage mTextureImage; // Texture image - vk::UniqueDeviceMemory mTextureImageMemory; // Memory for texture image - vk::UniqueImageView mTextureImageView; // Image view for texture - vk::UniqueSampler mTextureSampler; // Sampler for texture + u32 mMipLevels; ///< Number of mipmap levels + vk::UniqueImage mTextureImage; ///< Texture image + vk::UniqueDeviceMemory mTextureImageMemory; ///< Memory for texture image + vk::UniqueImageView mTextureImageView; ///< Image view for texture + vk::UniqueSampler mTextureSampler; ///< Sampler for texture - // Vertex and index data - std::vector mVertices; // Vertex data for the model - std::vector mIndices; // Index data for the model - vk::UniqueBuffer mVertexBuffer; // Buffer for vertex data - vk::UniqueDeviceMemory mVertexBufferMemory; // Memory for vertex buffer - vk::UniqueBuffer mIndexBuffer; // Buffer for index data - vk::UniqueDeviceMemory mIndexBufferMemory; // Memory for index buffer + std::vector mVertices; ///< Vertex data for the model + std::vector mIndices; ///< Index data for the model + vk::UniqueBuffer mVertexBuffer; ///< Buffer for vertex data + vk::UniqueDeviceMemory mVertexBufferMemory; ///< Memory for vertex buffer + vk::UniqueBuffer mIndexBuffer; ///< Buffer for index data + vk::UniqueDeviceMemory mIndexBufferMemory; ///< Memory for index buffer - // Uniform buffers for shader parameters - std::vector mUniformBuffers; - std::vector mUniformBuffersMemory; - std::vector mUniformBuffersMapped; + std::vector mUniformBuffers; ///< Uniform buffers for shader parameters + std::vector mUniformBuffersMemory; ///< Memory for uniform buffers + std::vector mUniformBuffersMapped; ///< Mapped uniform buffers - // Descriptor pools and sets - vk::UniqueDescriptorPool mDescriptorPool; // Descriptor pool for the application - vk::UniqueDescriptorPool mImGuiDescriptorPool; // Separate descriptor pool for ImGui - std::vector mDescriptorSets; // Descriptor sets for binding resources + vk::UniqueDescriptorPool mDescriptorPool; ///< Descriptor pool for the application + vk::UniqueDescriptorPool mImGuiDescriptorPool; ///< Separate descriptor pool for ImGui + std::vector mDescriptorSets; ///< Descriptor sets for binding resources - // Command buffers for recording drawing commands - std::vector mCommandBuffers; + std::vector mCommandBuffers; ///< Command buffers for drawing - // Synchronization primitives std::vector - mImageAvailableSemaphores; // Signals that an image is available for rendering - std::vector mRenderFinishedSemaphores; // Signals that rendering has finished - std::vector mInFlightFences; // Ensures CPU-GPU synchronization + mImageAvailableSemaphores; ///< Signals that an image is available for rendering + std::vector mRenderFinishedSemaphores; ///< Signals that rendering has finished + std::vector mInFlightFences; ///< Ensures CPU-GPU synchronization - // State variables - bool mFramebufferResized = false; // Flag indicating if the framebuffer was resized - u32 mCurrentFrame = 0; // Index of the current frame being rendered + bool mFramebufferResized = false; ///< Flag indicating if the framebuffer was resized + 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 { - std::optional graphics_family; // Index of graphics queue family - std::optional present_family; // Index of presentation queue family + std::optional graphics_family; ///< Index of graphics queue family + std::optional 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(); } }; - // 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 { - vk::SurfaceCapabilitiesKHR capabilities; // Surface capabilities - std::vector formats; // Supported surface formats - std::vector present_modes; // Supported presentation modes + vk::SurfaceCapabilitiesKHR capabilities; ///< Surface capabilities + std::vector formats; ///< Supported surface formats + std::vector 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 { - shaderc::Compiler compiler; - shaderc::CompileOptions options; - - shaderc::CompilationResult 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. - * @return std::vector 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. + * This struct holds the model, view, and projection matrices for use in shaders. */ - static fn readFile(const std::string& filename) -> std::vector { - // Open the file in binary mode - std::ifstream file(filename, std::ios::binary); - - // 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 buffer((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - - return buffer; - } + 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 + }; /** * @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. */ - static fn updateFPS(const vkfw::Window& window, const std::string& baseTitle) -> void { - static int FrameCount = 0; - static double LastTime = glfwGetTime(); - static double Fps = 0.0; + static fn updateFPS(const vkfw::Window& window, const string& baseTitle) -> void { + static u32 FrameCount = 0; + static f64 LastTime = glfwGetTime(); + static f64 Fps = 0.0; // Get the current time - double currentTime = glfwGetTime(); + f64 currentTime = glfwGetTime(); FrameCount++; // If one second has passed, calculate the FPS @@ -298,7 +264,7 @@ class VulkanApp { FrameCount = 0; // Update window title - std::string newTitle = format("{} - {:.0F} FPS", baseTitle, Fps); + string newTitle = format("{} - {:.0F} FPS", baseTitle, Fps); glfwSetWindowTitle(window, newTitle.c_str()); } } @@ -428,7 +394,7 @@ class VulkanApp { .DescriptorPool = mImGuiDescriptorPool.get(), .RenderPass = mRenderPass.get(), .MinImageCount = MAX_FRAMES_IN_FLIGHT, - .ImageCount = static_cast(mSwapChainImages.size()), + .ImageCount = static_cast(mSwapChainImages.size()), .MSAASamples = static_cast(mMsaaSamples), .PipelineCache = VK_NULL_HANDLE, .Subpass = 0, @@ -525,7 +491,7 @@ class VulkanApp { .applicationVersion = 1, .pEngineName = "No Engine", .engineVersion = 1, - .apiVersion = vk::ApiVersion12, + .apiVersion = vk::ApiVersion13, }; // Get the required extensions @@ -898,9 +864,9 @@ class VulkanApp { */ fn createGraphicsPipeline() -> void { std::vector vertShaderCode = - compileShader(vertShaderSrc, shaderc_shader_kind::shaderc_vertex_shader); + ShaderCompiler::getCompiledShader(vertShaderSrc, shaderc_shader_kind::shaderc_vertex_shader, "vert"); std::vector 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 fragShaderModule = createShaderModule(fragShaderCode); @@ -1582,7 +1548,7 @@ class VulkanApp { tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; - std::string warn, err; + string warn, err; std::filesystem::path modelPath = std::filesystem::current_path() / "models" / "viking_room.obj"; diff --git a/src/util/shaders.hpp b/src/util/shaders.hpp new file mode 100644 index 0000000..e5fed9b --- /dev/null +++ b/src/util/shaders.hpp @@ -0,0 +1,166 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#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 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 { + using namespace std; + + const string cacheFile = getCacheFilePath(shaderName); + + // Try loading from cache first + vector 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 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 { + 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 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 { + 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 spirvCode(fileSize / sizeof(u32)); + + // Read entire file content into the vector + if (!file.read(bit_cast(spirvCode.data()), static_cast(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& 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(spirvCode.data()), static_cast(spirvCode.size() * sizeof(u32)) + )) + throw runtime_error("Failed to save shader to cache: " + cacheFile); + } +}; diff --git a/src/util/types.h b/src/util/types.hpp similarity index 100% rename from src/util/types.h rename to src/util/types.hpp diff --git a/src/util/unique_image.h b/src/util/unique_image.h deleted file mode 100644 index 07a2b24..0000000 --- a/src/util/unique_image.h +++ /dev/null @@ -1,69 +0,0 @@ -#include - -#define STB_IMAGE_IMPLEMENTATION -#include - -#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())); - } - }; -} diff --git a/src/util/unique_image.hpp b/src/util/unique_image.hpp new file mode 100644 index 0000000..7c8dcd1 --- /dev/null +++ b/src/util/unique_image.hpp @@ -0,0 +1,121 @@ +#include + +#define STB_IMAGE_IMPLEMENTATION +#include + +#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())); + } + }; +} diff --git a/src/util/vertex.h b/src/util/vertex.hpp similarity index 98% rename from src/util/vertex.h rename to src/util/vertex.hpp index afdecc4..a3a0ef1 100644 --- a/src/util/vertex.h +++ b/src/util/vertex.hpp @@ -6,7 +6,7 @@ #define VULKAN_HPP_NO_CONSTRUCTORS #include -#include "types.h" +#include "types.hpp" // Vertex data for the model struct Vertex {