From c84c12b9e494f0ea30285691a96be46aa4224ea5 Mon Sep 17 00:00:00 2001 From: "matthias@arch" Date: Mon, 14 Nov 2022 23:00:13 +0100 Subject: [PATCH] updated for design change --- src/.clangd | 2 +- src/.doxygen_config | 4 +- src/main.cpp | 90 ++++++++++++++++++------------- src/renderer/renderer.hpp | 1 + src/renderer/renderer2D.cpp | 51 ++++++++---------- src/renderer/renderer2D.hpp | 14 +++-- src/utility/texture_atlas.cpp | 23 ++++---- src/utility/texture_atlas.hpp | 17 ++++-- src/utility/texture_manager.cpp | 73 +++++++++++++++++++++---- src/utility/texture_manager.hpp | 94 +++++++++++++++++++++++---------- src/vertex.cpp | 2 +- 11 files changed, 240 insertions(+), 131 deletions(-) diff --git a/src/.clangd b/src/.clangd index d740a5e..e4ccfff 100644 --- a/src/.clangd +++ b/src/.clangd @@ -1,3 +1,3 @@ CompileFlags: # Tweak the parse settings - Add: [ -std=c++2a, -Wall, -I/usr/include/freetype2, -I.., -I., -Iexternal, -Irenderer, -Ishader, -Iutility, -I../external, -I../renderer, -I../shader, -I../utility,] + Add: [ -std=c++2a, -Wall, -I/usr/include/freetype2, -I.., -I., -Iexternal, -Irenderer, -Ishader, -Iutility, -Idrawables, -Ivulkan_draw_strategies, -I../external, -I../renderer, -I../shader, -I../utility, -I../drawables, -I../vulkan_draw_strategies ] # https://clangd.llvm.org/config diff --git a/src/.doxygen_config b/src/.doxygen_config index 949b016..257aaa4 100755 --- a/src/.doxygen_config +++ b/src/.doxygen_config @@ -2362,7 +2362,7 @@ UML_LIMIT_NUM_FIELDS = 10 # The default value is: NO. # This tag requires that the tag UML_LOOK is set to YES. -DOT_UML_DETAILS = NO +DOT_UML_DETAILS = YES # The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters # to display on a single line. If the actual line length exceeds this threshold @@ -2569,4 +2569,4 @@ GENERATE_LEGEND = YES # plantuml temporary files. # The default value is: YES. -DOT_CLEANUP = YES +DOT_CLEANUP = NO diff --git a/src/main.cpp b/src/main.cpp index 10aab26..e6fa21f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,11 @@ -#include "shape.hpp" +#include "ds_vk_rectangle.hpp" +#include "print_draw_strategy.hpp" #include "vulkan_instance.hpp" #include "renderer2D.hpp" + #include "renderer3D.hpp" #include "vulkan_settings.hpp" +#include "font.hpp" #include #include @@ -10,14 +13,13 @@ namespace gz::vlk { int mainLoop() { - LogCreateInfo logCI { + Log log(LogCreateInfo { .logfile = "main.log", .storeLog = false, .prefix = "Main", .prefixColor = Color::BG_RED, .timeColor = settings::VULKAN_MESSAGE_TIME_COLOR, - }; - Log log(std::move(logCI)); + }); gz::SettingsManagerCreateInfo smCI { .filepath = settings::CONFIG_FILE, @@ -27,6 +29,7 @@ namespace gz::vlk { .throwExceptionWhenNewValueNotAllowed = true, }; + log("Creating instance"); VulkanInstance vulkanInstance(smCI); vulkanInstance.init(); TextureManager textureManager(vulkanInstance); @@ -34,42 +37,53 @@ namespace gz::vlk { log("Startup complete. Drawing shapes"); - Rectangle rect9(-200, -400, 200, 200, { 0.0f, 1.0f, 0.0f}, "entities/sheep.png"); - Rectangle rect1( 90, 91, 92, 93, { 1.0f, 0.9f, 0.8f}, "blocks/leaves.png"); - Rectangle rect2( 190, 191, 192, 193, { 0.7f, 0.6f, 0.5f}, "blocks/crate.png"); - Rectangle rect3( 32, 120, 400, 400, { 0.0f, 0.0f, 1.0f}, "blocks/dirt.png"); - Rectangle rect5( 90, 91, 92, 93, { 1.0f, 0.9f, 0.8f}, "items/sword.png"); - Rectangle rect6( 190, 191, 192, 193, { 0.7f, 0.6f, 0.5f}, "items/crossbow.png"); - Rectangle rect4(-600, -600, 800, 400, { 1.0f, 0.0f, 0.0f}, "blocks/ice.png"); - Rectangle rect7( 32, 120, 400, 400, { 0.0f, 0.0f, 1.0f}, "special/hotbar.png"); - Rectangle rect8(-600, -600, 800, 400, { 1.0f, 0.0f, 0.0f}, "special/func_nocol.png"); - Rectangle rect10(-400, -400, 800, 800, { 0.0f, 1.0f, 0.0f}, "atlas"); - r2D.drawShape(&rect1); - r2D.drawShape(&rect2); - r2D.drawShape(&rect3); - r2D.drawShape(&rect4); - r2D.drawShape(&rect5); - r2D.drawShape(&rect6); - r2D.drawShape(&rect7); - r2D.drawShape(&rect8); - r2D.drawShape(&rect9); - r2D.drawShape(&rect10); - log("Drawing complete. Filling r2D with shapes."); - r2D.fillVertexBufferWithShapes(); - r2D.fillIndexBufferWithShapes(); + BufferManager bufferManager(vulkanInstance); + auto createRect = [&r2D, &bufferManager](auto posX, auto posY, auto width, auto height, const auto& texture) { + return Rectangle(posX, posY, width, height, std::make_unique(RectangleTexturedVkDS(r2D, bufferManager, texture))); + /* return Rectangle(posX, poxY, width, height, std::make_unique>()); */ + }; + std::vector rects; + rects.emplace_back(createRect( 0, 0, 48, 48, "blocks/leaves.png")); + rects.emplace_back(createRect( -190,-191, 192, 193, "blocks/crate.png")); + rects.emplace_back(createRect( 32, 120, 400, 400, "blocks/dirt.png")); + rects.emplace_back(createRect( 90, 91, 92, 93, "items/sword.png")); + rects.emplace_back(createRect( 190, 191, 192, 193, "items/crossbow.png")); + rects.emplace_back(createRect(-600, -600, 800, 400, "blocks/ice.png")); + rects.emplace_back(createRect( 32, 400, 400, 100, "special/hotbar.png")); + rects.emplace_back(createRect(-600, -600, 800, 400, "special/func_nocol.png")); + rects.emplace_back(createRect(-200, -400, 200, 200, "entities/sheep.png")); + rects.emplace_back(createRect(-600, 0, 600, 600, "atlas_0")); + log("Initializing complete."); - Renderer3D r3D(vulkanInstance, textureManager); - /* gz::FontManager fm("fonts"); */ + /* Renderer3D r3D(vulkanInstance, textureManager); */ + /* gz::vlk::FontManager fm(vulkanInstance, "fonts"); */ /* fm.loadFont("menu.ttf"); */ - /* /1* fm.getFaces().at("menu.ttf"). *1/ */ - try { + /* fm.getFaces().at("menu.ttf"). */ + /* try */ + { uint32_t imageIndex; /* std::chrono::time_point now = std::chrono::system_clock::now(); */ - while (! glfwWindowShouldClose(vulkanInstance.getWindow())) { + bool first2d = false; + int count = 0; + while (!glfwWindowShouldClose(vulkanInstance.getWindow())) { glfwPollEvents(); imageIndex = vulkanInstance.beginFrameDraw(); - r2D.drawFrame(imageIndex); - r3D.drawFrame(imageIndex); + uint32_t currentFrame = vulkanInstance.getCurrentFrame(); + r2D.beginFrameDraw(imageIndex, currentFrame); + for (auto& shape : rects) { + shape.draw(); + } + if (count > 100) { + /* r2D.drawFrame(imageIndex); */ + r2D.endFrameDraw(imageIndex, currentFrame); + /* r3D.drawFrame(imageIndex); */ + } + else { + /* r3D.drawFrame(imageIndex); */ + r2D.endFrameDraw(imageIndex, currentFrame); + } + count = count < 200 ? ++count : 0; + /* first2d = !first2d; */ vulkanInstance.endFrameDraw(imageIndex); auto SLEEP_TIME = std::chrono::milliseconds(1000 / vulkanInstance.getSettings().get("framerate")); std::this_thread::sleep_for(SLEEP_TIME); @@ -80,10 +94,10 @@ namespace gz::vlk { } vulkanInstance.cleanup(); } - catch (const std::exception& e) { - std::cerr << e.what() << std::endl; - return 1; - } + /* catch (const std::exception& e) { */ + /* std::cerr << e.what() << std::endl; */ + /* return 1; */ + /* } */ return 0; } diff --git a/src/renderer/renderer.hpp b/src/renderer/renderer.hpp index 0d61898..992647f 100644 --- a/src/renderer/renderer.hpp +++ b/src/renderer/renderer.hpp @@ -27,6 +27,7 @@ namespace gz::vlk { class Renderer { public: Renderer(VulkanInstance& instance, TextureManager& textureManager) : vk(instance), textureManager(textureManager) {}; + TextureManager& getTextureManager() { return textureManager; }; protected: /** * @brief Cleanup vulkan resources held by this class diff --git a/src/renderer/renderer2D.cpp b/src/renderer/renderer2D.cpp index 61a91ba..a573b4e 100644 --- a/src/renderer/renderer2D.cpp +++ b/src/renderer/renderer2D.cpp @@ -1,7 +1,6 @@ #include "renderer2D.hpp" #include "exceptions.hpp" -#include "shape.hpp" #include "vulkan_allocator.hpp" #include "vulkan_instance.hpp" #include "texture_manager.hpp" @@ -38,7 +37,7 @@ namespace gz::vlk { // INIT & CLEANUP // Renderer2D::Renderer2D(VulkanInstance& instance, TextureManager& textureManager) - : Renderer(instance, textureManager), shapeBufferManager(instance) + : Renderer(instance, textureManager) { LogCreateInfo logCI { .logfile = "renderer2D.log", @@ -87,6 +86,11 @@ namespace gz::vlk { // // SWAPCHAIN DEPENDANT // + const vk::Extent2D& Renderer2D::getScExtent() const { + return vk.getScExtent(); + } + + void Renderer2D::initSwapChainDependantResources() { createRenderPass(); createImageResources(); @@ -311,6 +315,8 @@ namespace gz::vlk { // RENDERING // void Renderer2D::beginFrameDraw(uint32_t imageIndex, uint32_t currentFrame) { + this->currentFrame = currentFrame; + this->imageIndex = imageIndex; commandBuffers[currentFrame].reset(); vk::CommandBufferBeginInfo commandBufferBI { /* .flags = 0, */ @@ -414,48 +420,37 @@ namespace gz::vlk { /* vk.destroyBuffer(stagingBuffer, stagingBufferMemory); */ /* } */ - - - void Renderer2D::drawShape(Shape* shape, uint32_t imageIndex, uint32_t currentFrame) { - /* rLog.log0("drawShape:", shape->getVertexBufferInfo(), shape->getIndexBufferInfo()); */ - commandBuffers[currentFrame].bindPipeline(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].pipeline); + void Renderer2D::drawShape(BufferManager& bufferManager, const BufferInfo& vertexBufferInfo, const BufferInfo& indexBufferInfo, const uint32_t textureAtlasIndex) { + /* rLog.log0("drawShape:", vertexBufferInfo, indexBufferInfo, "textureAtlasIndex:", textureAtlasIndex); */ + auto& cmdBuffer = commandBuffers.at(currentFrame); + cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].pipeline); uint32_t descriptorCount = 1; uint32_t firstSet = 0; uint32_t dynamicOffsetCount = 0; uint32_t* dynamicOffsets = nullptr; - commandBuffers[currentFrame].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].layout, firstSet, descriptorCount, &textureManager.getDescriptorSet(), dynamicOffsetCount, dynamicOffsets); + cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].layout, firstSet, descriptorCount, &textureManager.getDescriptorSet(), dynamicOffsetCount, dynamicOffsets); - - vk::Buffer vertexBuffers[] = { shapeBufferManager.getVertexBuffer(shape->getVertexBufferInfo()) }; - vk::DeviceSize vertexOffsets[] = { shape->getVertexBufferInfo().offset }; + vk::Buffer vertexBuffers[] = { bufferManager.getVertexBuffer(vertexBufferInfo) }; + vk::DeviceSize vertexOffsets[] = { vertexBufferInfo.offset }; uint32_t bindingCount = 1; uint32_t binding = 0; - commandBuffers[currentFrame].bindVertexBuffers(binding, bindingCount, vertexBuffers, vertexOffsets); + cmdBuffer.bindVertexBuffers(binding, bindingCount, vertexBuffers, vertexOffsets); - vk::Buffer indexBuffers = { shapeBufferManager.getIndexBuffer(shape->getIndexBufferInfo()) }; - vk::DeviceSize indexOffset = shape->getIndexBufferInfo().offset; + vk::Buffer indexBuffers = { bufferManager.getIndexBuffer(indexBufferInfo) }; + vk::DeviceSize indexOffset = indexBufferInfo.offset; // TODO use correct index type! - commandBuffers[currentFrame].bindIndexBuffer(indexBuffers, indexOffset, vk::IndexType::eUint32); + cmdBuffer.bindIndexBuffer(indexBuffers, indexOffset, vk::IndexType::eUint32); - uint32_t pushConstant = shape->getTexureAtlasIndex(); + uint32_t pushConstant = textureAtlasIndex; vk::ShaderStageFlags flags(vk::ShaderStageFlagBits::eFragment); - commandBuffers[currentFrame].pushConstants(pipelines[R2Render].layout, flags, NO_OFFSET, sizeof(uint32_t), &pushConstant); + cmdBuffer.pushConstants(pipelines[R2Render].layout, flags, NO_OFFSET, sizeof(uint32_t), &pushConstant); - uint32_t indexCount = shape->getIndexBufferInfo().count; + uint32_t indexCount = indexBufferInfo.count; uint32_t instanceCount = 1; uint32_t firstIndex = 0; uint32_t firstInstance = 0; - commandBuffers[currentFrame].drawIndexed(indexCount, instanceCount, firstIndex, NO_OFFSET, firstInstance); + cmdBuffer.drawIndexed(indexCount, instanceCount, firstIndex, NO_OFFSET, firstInstance); } - - void Renderer2D::initializeShape(Shape* shape) { - // make indices valid - /* shape->setIndexOffset(shapesVerticesCount); */ - shape->normalizeVertices(vk.getScExtent().width, vk.getScExtent().height); - shape->setTextureCoordinates(textureManager); - shape->initalizeBufferInfos(shapeBufferManager); - } - } // namespace gz::vlk diff --git a/src/renderer/renderer2D.hpp b/src/renderer/renderer2D.hpp index 55fc3ab..a4762e5 100644 --- a/src/renderer/renderer2D.hpp +++ b/src/renderer/renderer2D.hpp @@ -1,8 +1,9 @@ #pragma once +#include "buffer_manager.hpp" #include "renderer.hpp" -#include "shape.hpp" +#include "text.hpp" #include "vulkan_allocator.hpp" #include "vulkan_util.hpp" @@ -54,8 +55,8 @@ namespace gz::vlk { */ void endFrameDraw(uint32_t imageIndex, uint32_t currentFrame); - void drawShape(Shape* shape, uint32_t imageIndex, uint32_t currentFrame); - void initializeShape(Shape* shape); + void drawShape(BufferManager& bufferManager, const BufferInfo& vertexBufferInfo, const BufferInfo& indexBufferInfo, const uint32_t textureAtlasIndex); + const vk::Extent2D& getScExtent() const; /** * @brief Copies the vertices from shapes into the vertex buffer, using a staging buffer */ @@ -64,6 +65,8 @@ namespace gz::vlk { /// @} private: + uint32_t imageIndex; + uint32_t currentFrame; /** * @brief Destroy all vulkan objects owned by this object * @details: @@ -74,11 +77,6 @@ namespace gz::vlk { */ void cleanup(); - /* std::vector shapes; */ - /* size_t shapesVerticesCount = 0; */ - /* size_t shapesIndicesCount = 0; */ - BufferManager shapeBufferManager; - /* void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame); */ /** diff --git a/src/utility/texture_atlas.cpp b/src/utility/texture_atlas.cpp index 562514c..52c3d69 100644 --- a/src/utility/texture_atlas.cpp +++ b/src/utility/texture_atlas.cpp @@ -9,8 +9,7 @@ #include #include #include -#include -#include +#include #include #include @@ -23,8 +22,9 @@ std::string TextureImageArea::toString() const { // // INIT & CLEANUP // -TextureAtlas::TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY) +TextureAtlas::TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY, vk::Format imageFormat) : vk(instance), + imageFormat(imageFormat), slotWidth(slotWidth), slotHeight(slotHeight), slotCountX(slotCountX), slotCountY(slotCountY) { @@ -59,12 +59,12 @@ void TextureAtlas::cleanup() { void TextureAtlas::createImageResources() { - vk.createImage(slotWidth * slotCountX, slotHeight * slotCountY, vk::Format::eR8G8B8A8Srgb, vk::ImageTiling::eLinear, + vk.createImage(slotWidth * slotCountX, slotHeight * slotCountY, imageFormat, vk::ImageTiling::eLinear, vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled, vk::MemoryPropertyFlagBits::eDeviceLocal, textureImage, textureImageMemory); vk::CommandBuffer cmdBuffer = vk.beginSingleTimeCommands(POOL_GRAPHICS); - vk.transitionImageLayout(textureImage, vk::Format::eR8G8B8A8Srgb, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, &cmdBuffer); + vk.transitionImageLayout(textureImage, imageFormat, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, &cmdBuffer); vk::ImageSubresourceRange subresourceRange { .aspectMask = vk::ImageAspectFlagBits::eColor, .baseMipLevel = 0, @@ -73,10 +73,10 @@ void TextureAtlas::createImageResources() { .layerCount = 1, }; cmdBuffer.clearColorImage(textureImage, vk::ImageLayout::eTransferDstOptimal, &settings::missingTextureColor, 1, &subresourceRange); - vk.transitionImageLayout(textureImage, vk::Format::eR8G8B8A8Srgb, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal, &cmdBuffer); + vk.transitionImageLayout(textureImage, imageFormat, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal, &cmdBuffer); vk.endSingleTimeCommands(cmdBuffer, POOL_GRAPHICS); - vk.createImageView(vk::Format::eR8G8B8A8Srgb, textureImage, textureImageView, vk::ImageAspectFlagBits::eColor); + vk.createImageView(imageFormat, textureImage, textureImageView, vk::ImageAspectFlagBits::eColor); vk.createTextureSampler(textureSampler); } @@ -84,7 +84,7 @@ void TextureAtlas::createImageResources() { // // ADD TEXTURE // -std::pair TextureAtlas::addTexture(uint8_t* pixels, uint16_t width, uint16_t height) { +std::pair TextureAtlas::addTexture(uint8_t* pixels, uint16_t width, uint16_t height, uint8_t bytesPerPixel) { if (freeAreas.empty()) { throw Exception("No texture slots available", "TextureAtlas::addTexture"); } @@ -151,7 +151,7 @@ std::pair TextureAtlas::addTexture(uint8_t* pixels, uint16 #ifdef LOG_LEVEL_0 tLog.log0("addTexture: Adding texture at position x,y=(", x, y, "), with slotCountX,Y=(", slotsX, slotsY, "), with dimensions w,h=(", width, height, ")"); #endif - blitTextureOnImage(pixels, x, y, width, height); + blitTextureOnImage(pixels, width, height, bytesPerPixel, x, y); // topleft normalized: slot / slotCount // bottomright normalized: (slot + (textureSize/slotCountize)) / slotCount return { glm::vec2(static_cast(x) / slotCountX, static_cast(y) / slotCountY), @@ -160,9 +160,8 @@ std::pair TextureAtlas::addTexture(uint8_t* pixels, uint16 } -void TextureAtlas::blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t slotY, uint16_t textureWidth, uint16_t textureHeight) { - constexpr size_t BYTES_PER_PIXEL = 4; - vk::DeviceSize imageSize = textureWidth * textureHeight * BYTES_PER_PIXEL; +void TextureAtlas::blitTextureOnImage(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight, uint8_t bytesPerPixel, uint16_t slotX, uint16_t slotY) { + vk::DeviceSize imageSize = textureWidth * textureHeight * bytesPerPixel; vk::Buffer stagingBuffer; MemoryInfo stagingBufferMemory; vk.createBuffer(imageSize, vk::BufferUsageFlagBits::eTransferSrc, diff --git a/src/utility/texture_atlas.hpp b/src/utility/texture_atlas.hpp index 01f4d06..62c4367 100644 --- a/src/utility/texture_atlas.hpp +++ b/src/utility/texture_atlas.hpp @@ -3,6 +3,7 @@ #include "vulkan_allocator.hpp" #include #include +#include #define VULKAN_HPP_NO_CONSTRUCTORS #include @@ -52,16 +53,25 @@ namespace gz::vlk { * -# @ref createImageResources "create texture image, view and sampler" * * The textureImage will have the dimensions slotWidth * slotCountX, slotHeight * slotCountY + * + * @param instance Reference to a VulkanInstance + * @param slotWidth Width of a texture slot in pixels + * @param slotHeight Height of a texture slot in pixels + * @param slotCountX Number of slots in a row + * @param slotCountY Number of slots in a column + * @param imageFormat The format of the image. Must match the bytesPerPixel parameter in addTexture() */ - TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY); + TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY, vk::Format imageFormat = vk::Format::eR8G8B8A8Srgb); /** * @brief Add a texture to the atlas + * @param pixels Buffer containing the bytes. Size of the buffer must be `textureWidth * textureHeight * bytesPerPixel` * @param textureWidth Width in pixels * @param textureHeight Height in pixels + * @param bytesPerPixel Number of bytes per pixel. Must be compatible to the imageFormat passed to the constructor * @returns The topleft and bottomright texture coordinates in the image of the atlas * @throws Exception when there is no space in the atlas to add the texture */ - std::pair addTexture(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight); + [[ nodiscard ]] std::pair addTexture(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight, uint8_t bytesPerPixel); const vk::ImageView& getTextureImageView() const { return textureImageView; } const vk::Sampler& getTextureSampler() const { return textureSampler; } @@ -75,13 +85,14 @@ namespace gz::vlk { private: VulkanInstance& vk; - void blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t slotY, uint16_t textureWidth, uint16_t textureHeight); + void blitTextureOnImage(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight, uint8_t bytesPerPixel, uint16_t slotX, uint16_t slotY); /** * @brief Create textureImage, textureImageView and textureSampler * @details * the textureImage is created with missingTextureColor and then transitioned to SHADER_READ_ONLY_OPTIMAL layout. */ void createImageResources(); + vk::Format imageFormat; vk::Image textureImage; MemoryInfo textureImageMemory; vk::ImageView textureImageView; diff --git a/src/utility/texture_manager.cpp b/src/utility/texture_manager.cpp index d2b8089..eafc290 100644 --- a/src/utility/texture_manager.cpp +++ b/src/utility/texture_manager.cpp @@ -1,10 +1,19 @@ #include "texture_manager.hpp" #include "exceptions.hpp" #include "vulkan_instance.hpp" +#include "vulkan_util.hpp" +#include #include +#include +#include namespace gz::vlk { + std::string TextureInfo::toString() const { + return ""; + } + + TextureManager::TextureManager(VulkanInstance& instance) : vk(instance) { @@ -18,7 +27,7 @@ namespace gz::vlk { tLog = Log(std::move(logCI)); vk.registerCleanupCallback(std::bind(&TextureManager::cleanup, this)); - atlases.emplace_back(TextureAtlas(vk, 24, 24, 24, 24)); + addAtlas(24, 24, 24, 24); createDescriptorResources(); VulkanInstance::registerObjectUsingVulkan("TextureManager", &descriptorSetLayout, &descriptorPool, &descriptorSet); @@ -35,31 +44,75 @@ namespace gz::vlk { } - void TextureManager::getTexCoords(const std::string& textureName, glm::vec2& texCoords) { - if (!textureInfos.contains(textureName)) { - loadTextureFromFile(textureName); + TextureAtlasIndex TextureManager::addAtlas(uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY, vk::Format imageFormat) { + TextureAtlasIndex index = atlases.size(); + atlases.emplace_back(TextureAtlas(vk, slotWidth, slotHeight, slotCountX, slotCountY, imageFormat)); + // add special "atlas_X" texture + textures.insert( { std::string("atlas_") + gz::toString(index), index }); + textureInfos.emplace_back(TextureInfo{ .atlas = index, .texCoordTopLeft = glm::vec2(0, 0), .texCoordBottomRight = glm::vec2(1, 1) }); + return index; + } + + + void TextureManager::isValidTexture(const Texture& texture) const { + if (texture < textureInfos.size()) { + return; + } + else { + throw InvalidArgument("Texture handle '" + gz::toString(texture) + "' is invalid.", "TextureManager::isValidTexture"); } - TextureInfo& texI = textureInfos[textureName]; - tLog.log0("getTexCoords", texCoords, "textureInfo: topleft", texI.texCoordTopLeft, "botright", texI.texCoordBottomRight); + } + + + Texture TextureManager::getTexture(const std::string& textureName) { + if (!textures.contains(textureName)) { + return loadTextureFromFile(textureName); + } + else { + return textures.at(textureName); + } + } + + + void TextureManager::setTexCoords(const Texture& texture, glm::vec2& texCoords) { + isValidTexture(texture); + TextureInfo& texI = textureInfos.at(texture); + tLog.log0("setTexCoords", texCoords, "textureInfo: topleft", texI.texCoordTopLeft, "botright", texI.texCoordBottomRight); texCoords = texI.texCoordTopLeft + texCoords * (texI.texCoordBottomRight - texI.texCoordTopLeft); } - void TextureManager::loadTextureFromFile(const std::string& textureName) { +// +// ATLASES +// + const TextureAtlas& TextureManager::getTextureAtlas(const Texture& texture) const { + isValidTexture(texture); + return atlases.at(textureInfos.at(texture).atlas); + } + uint32_t TextureManager::getTextureAtlasIndex(const Texture& texture) const { + isValidTexture(texture); + return textureInfos.at(texture).atlas; + } + + Texture TextureManager::loadTextureFromFile(const std::string& textureName) { int textureWidth, textureHeight, textureChannels; stbi_uc* pixels = stbi_load(("textures/" + textureName).c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha); if (!pixels) { throw Exception("Failed to load texture: textures/" + textureName, "TextureManager::loadTexture"); } - TextureInfo& texI = textureInfos[textureName]; + Texture texture = textureInfos.size(); + textureInfos.emplace_back(); + TextureInfo& texI = textureInfos.back(); texI.atlas = 0; - // TODO - auto texCoords = atlases.at(0).addTexture(pixels, textureWidth, textureHeight); + static constexpr uint8_t BYTES_PER_PIXEL = 4; + auto texCoords = atlases.at(0).addTexture(pixels, textureWidth, textureHeight, BYTES_PER_PIXEL); stbi_image_free(pixels); tLog.log0("TextureManager::loadTextureFromFile: TexCoords of new image:", texCoords.first, texCoords.second); texI.texCoordTopLeft = std::move(texCoords.first), texI.texCoordBottomRight = std::move(texCoords.second), tLog.log0("TextureManager::loadTextureFromFile: After loading:", atlases.at(0)); + tLog.log0("loadTextureFromFile: textureInfos", textureInfos); + return texture; } diff --git a/src/utility/texture_manager.hpp b/src/utility/texture_manager.hpp index bf71533..1e9559f 100644 --- a/src/utility/texture_manager.hpp +++ b/src/utility/texture_manager.hpp @@ -4,20 +4,34 @@ #include "stb_image.h" -#include +#include +#define LOG_SUBLOGS #include #include #include namespace gz::vlk { + // TODO use this texture handle + using Texture = uint32_t; + using TextureAtlasIndex = uint32_t; + struct TextureInfo { - uint32_t atlas; + TextureAtlasIndex atlas; glm::vec2 texCoordTopLeft; glm::vec2 texCoordBottomRight; + std::string toString() const; }; /// Defined in vulkan_instance.hpp class VulkanInstance; + /** + * @details + * Manages multiple @ref TextureAtlas "texture atlases", which each hold one image that contains muiltiple textures + * @section tm_descriptors + * The TextureManager has provides a descriptor set for accessing the texture atlases: + * Bindings: + * -0. Array of combined image samplers (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) of length (getAtlasCount()) + */ class TextureManager { public: /** @@ -27,48 +41,72 @@ namespace gz::vlk { * -# @ref createDescriptor "create descriptor set layout, pool and set */ TextureManager(VulkanInstance& instance); - void getTexCoords(const std::string& textureName, glm::vec2& texCoords); + /** + * @brief Get a texture handle for a texture. Loads the texture from file if not already loaded + */ + Texture getTexture(const std::string& textureName); + + /** + * @brief Set the texture coordiantes for the texture + * @details + * @param texCoords + * The texture coordinates to set. texCoords should be from (0,0) to (1,1) and will be + * set to the according texture coordinate of texture. + */ + void setTexCoords(const Texture& texture, glm::vec2& texCoords); + /* void getTexCoords(const std::string& textureName, glm::vec2& texCoords); */ const vk::DescriptorSet& getDescriptorSet() const { return descriptorSet; } const vk::DescriptorSetLayout& getDescriptorSetLayout() const { return descriptorSetLayout; } - // TODO - /// @todo take texture as argument - const TextureAtlas& getTextureAtlas() const { return atlases.at(0); } + /** + * @brief Get the atlas that texture is in + * @throws InvalidArgument if the texture is not present + */ + const TextureAtlas& getTextureAtlas(const Texture& texture) const; + /* const TextureAtlas& getTextureAtlas(const std::string& texture) const; */ + /** + * @brief Get the index of the atlas that texture is in + * @throws InvalidArgument if the texture is not present + */ + TextureAtlasIndex getTextureAtlasIndex(const Texture& texture) const; + /* TextureAtlasIndex getTextureAtlasIndex(const std::string& texture) const; */ + /** + * @brief Get the number of atlases. + * @details Needed for specialization constant in shaders using sampler arrays + */ uint32_t getAtlasCount() const { return static_cast(atlases.size()); } + /** + * @brief Add a new atlas and a special "atlas_X" texture (where X is the returned index) + */ + TextureAtlasIndex addAtlas(uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY, vk::Format imageFormat = vk::Format::eR8G8B8A8Srgb); + private: void cleanup(); - void loadTextureFromFile(const std::string& textureName); - // TODO index currently has no meaning + /** + * @brief Check if texture handle is valid + * @throws InvalidArgument if texture is not valid + */ + void isValidTexture(const Texture& texture) const; + Texture loadTextureFromFile(const std::string& textureName); std::vector atlases; /** - * @brief Contains textureName : TextureInfo pairs + * @brief Contains textureName : Texture pairs */ - util::unordered_string_map textureInfos; + util::unordered_string_map textures; + std::vector textureInfos; VulkanInstance& vk; Log tLog; - /** - * @name Create desciptors - * @details These functions create a desciptor with bindings for - * -# Array of combined image samplers (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) at binding 0. Array length is atlas count - * @{ - */ + /** + * @name Create desciptors + * @see tm_descriptors + */ + /// @{ vk::DescriptorSetLayout descriptorSetLayout; vk::DescriptorPool descriptorPool; vk::DescriptorSet descriptorSet; - /* std::vector descriptorSets; */ - /** - * @brief Create a descriptor set, layout and pool - * @details - * Bindings: - * -0. texture sampler array COMBINED_IMAGE_SAMPLER[atlasCount] - */ void createDescriptorResources(); - /* const uint32_t bindingCombinedImageSampler = 1; */ - - /** - * @} - */ + /// @} }; diff --git a/src/vertex.cpp b/src/vertex.cpp index 1882631..1764139 100644 --- a/src/vertex.cpp +++ b/src/vertex.cpp @@ -2,7 +2,7 @@ #define VULKAN_HPP_NO_CONSTRUCTORS #include -#include +#include #include