From c9fb6e991aa27f1d59924f1f2f90097336a4695f Mon Sep 17 00:00:00 2001 From: Bram Verhulst Date: Thu, 3 Sep 2026 00:58:46 +0200 Subject: [PATCH] feat: add 2D rendering pipeline --- destrum/CMakeLists.txt | 2 + destrum/assets_src/shaders/quad.frag | 16 + destrum/assets_src/shaders/quad.vert | 22 ++ .../Graphics/Managers/QuadRenderingManager.h | 45 +++ .../Graphics/Pipelines/QuadRendererPass.h | 47 +++ destrum/include/destrum/Graphics/Renderer.h | 8 + .../Managers/QuadRenderingManager.cpp | 31 ++ .../Graphics/Pipelines/QuadRendererPass.cpp | 334 ++++++++++++++++++ destrum/src/Graphics/Renderer.cpp | 40 ++- lightkeeper/include/Lightkeeper.h | 4 + lightkeeper/src/Lightkeeper.cpp | 11 + 11 files changed, 557 insertions(+), 3 deletions(-) create mode 100644 destrum/assets_src/shaders/quad.frag create mode 100644 destrum/assets_src/shaders/quad.vert create mode 100644 destrum/include/destrum/Graphics/Managers/QuadRenderingManager.h create mode 100644 destrum/include/destrum/Graphics/Pipelines/QuadRendererPass.h create mode 100644 destrum/src/Graphics/Managers/QuadRenderingManager.cpp create mode 100644 destrum/src/Graphics/Pipelines/QuadRendererPass.cpp diff --git a/destrum/CMakeLists.txt b/destrum/CMakeLists.txt index 58f43f9..5a1e96d 100644 --- a/destrum/CMakeLists.txt +++ b/destrum/CMakeLists.txt @@ -38,6 +38,7 @@ set(SRC_FILES "src/Graphics/Managers/FrameManager.cpp" "src/Graphics/Managers/ImageManager.cpp" "src/Graphics/Managers/LineRenderingManager.cpp" + "src/Graphics/Managers/QuadRenderingManager.cpp" "src/Graphics/Resources/GPUImage.cpp" "src/Graphics/Resources/NBuffer.cpp" @@ -45,6 +46,7 @@ set(SRC_FILES "src/Graphics/Pipelines/MeshPipeline.cpp" "src/Graphics/Pipelines/LineRenderingPass.cpp" + "src/Graphics/Pipelines/QuadRendererPass.cpp" "src/Graphics/Pipelines/SkyboxPipeline.cpp" "src/Graphics/Pipelines/SkinningPipeline.cpp" "src/Graphics/Pipelines/ImguiPass.cpp" diff --git a/destrum/assets_src/shaders/quad.frag b/destrum/assets_src/shaders/quad.frag new file mode 100644 index 0000000..9e0c33f --- /dev/null +++ b/destrum/assets_src/shaders/quad.frag @@ -0,0 +1,16 @@ +#version 460 +#extension GL_GOOGLE_include_directive : require +#extension GL_EXT_nonuniform_qualifier : enable + +#include "bindless.glsl" + +layout(location = 0) in vec2 inUV; +layout(location = 1) in vec4 inColor; +layout(location = 2) flat in uint inTexId; + +layout(location = 0) out vec4 outFragColor; + +void main() { + vec4 texColor = sampleTexture2DLinear(inTexId, inUV); + outFragColor = texColor * inColor; +} \ No newline at end of file diff --git a/destrum/assets_src/shaders/quad.vert b/destrum/assets_src/shaders/quad.vert new file mode 100644 index 0000000..c3dbb20 --- /dev/null +++ b/destrum/assets_src/shaders/quad.vert @@ -0,0 +1,22 @@ +#version 460 +#extension GL_GOOGLE_include_directive : require + +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec2 inUV; +layout(location = 2) in vec4 inColor; +layout(location = 3) in uint inTexId; + +layout(location = 0) out vec2 outUV; +layout(location = 1) out vec4 outColor; +layout(location = 2) flat out uint outTexId; + +layout(push_constant) uniform QuadPushConstants { + mat4 viewProjection; +} pcs; + +void main() { + gl_Position = pcs.viewProjection * vec4(inPosition, 0.0, 1.0); + outUV = inUV; + outColor = inColor; + outTexId = inTexId; +} \ No newline at end of file diff --git a/destrum/include/destrum/Graphics/Managers/QuadRenderingManager.h b/destrum/include/destrum/Graphics/Managers/QuadRenderingManager.h new file mode 100644 index 0000000..b454383 --- /dev/null +++ b/destrum/include/destrum/Graphics/Managers/QuadRenderingManager.h @@ -0,0 +1,45 @@ +#ifndef DESTRUM_QUADRENDERINGMANAGER_H +#define DESTRUM_QUADRENDERINGMANAGER_H + +#include + +#include + +#include +#include + +struct QuadSubmission { + glm::vec2 position; + glm::vec2 size; + float rotation; + glm::vec4 color; + ImageID textureId; +}; + +class QuadRenderingManager final : public Singleton { +public: + friend class Singleton; + + void SubmitQuad( + const glm::vec2& position, + const glm::vec2& size, + const glm::vec4& color = glm::vec4{1.0f}, + ImageID textureId = NULL_IMAGE_ID); + + void SubmitQuad( + const glm::vec2& position, + const glm::vec2& size, + float rotation, + const glm::vec4& color = glm::vec4{1.0f}, + ImageID textureId = NULL_IMAGE_ID); + + [[nodiscard]] const std::vector& GetQuads() const { return quads; } + void ClearQuads(); + +private: + QuadRenderingManager() = default; + + std::vector quads; +}; + +#endif // DESTRUM_QUADRENDERINGMANAGER_H \ No newline at end of file diff --git a/destrum/include/destrum/Graphics/Pipelines/QuadRendererPass.h b/destrum/include/destrum/Graphics/Pipelines/QuadRendererPass.h new file mode 100644 index 0000000..a0b8992 --- /dev/null +++ b/destrum/include/destrum/Graphics/Pipelines/QuadRendererPass.h @@ -0,0 +1,47 @@ +#ifndef DESTRUM_QUADRENDERERPASS_H +#define DESTRUM_QUADRENDERERPASS_H + +#include +#include + +#include + +#include + +#include + + +class GfxDevice; +class Pipeline; +struct GPUImage; +class RenderResources; + +class QuadRendererPass final { +public: + void init(GfxDevice& gfxDevice, RenderResources& resources, VkFormat drawImageFormat); + void draw( + VkCommandBuffer cmd, + GfxDevice& gfxDevice, + RenderResources& resources, + const GPUImage& target, + const glm::mat4& projection); + void cleanup(GfxDevice& gfxDevice); + +private: + struct QuadVertex { + glm::vec2 position; + glm::vec2 uv; + glm::vec4 color; + std::uint32_t textureId; + }; + + void ensureCapacity(GfxDevice& gfxDevice, std::size_t requiredQuads); + + VkPipelineLayout pipelineLayout{VK_NULL_HANDLE}; + std::unique_ptr pipeline; + GPUBuffer vertexBuffer{}; + GPUBuffer indexBuffer{}; + std::size_t quadCapacity{0}; +}; + +#endif // DESTRUM_QUADRENDERERPASS_H \ No newline at end of file diff --git a/destrum/include/destrum/Graphics/Renderer.h b/destrum/include/destrum/Graphics/Renderer.h index 727a3d4..db8f448 100644 --- a/destrum/include/destrum/Graphics/Renderer.h +++ b/destrum/include/destrum/Graphics/Renderer.h @@ -1,11 +1,13 @@ #ifndef RENDERER_H #define RENDERER_H +#include #include #include #include #include +#include #include #include #include @@ -69,6 +71,11 @@ public: return resources; } + void drawQuads( + VkCommandBuffer cmd, + GfxDevice& gfxDevice, + const glm::mat4& projection); + private: void createDrawImage(GfxDevice& gfxDevice, const glm::ivec2& drawImageSize, bool firstCreate); @@ -113,6 +120,7 @@ private: std::unique_ptr meshPipeline; std::unique_ptr skyboxPipeline; std::unique_ptr lineRenderingPass; + std::unique_ptr quadRendererPass; std::unique_ptr skinningPipeline; bool initialized{false}; diff --git a/destrum/src/Graphics/Managers/QuadRenderingManager.cpp b/destrum/src/Graphics/Managers/QuadRenderingManager.cpp new file mode 100644 index 0000000..493c4d7 --- /dev/null +++ b/destrum/src/Graphics/Managers/QuadRenderingManager.cpp @@ -0,0 +1,31 @@ +#include + +void QuadRenderingManager::SubmitQuad( + const glm::vec2& position, + const glm::vec2& size, + const glm::vec4& color, + ImageID textureId) +{ + SubmitQuad(position, size, 0.0f, color, textureId); +} + +void QuadRenderingManager::SubmitQuad( + const glm::vec2& position, + const glm::vec2& size, + float rotation, + const glm::vec4& color, + ImageID textureId) +{ + quads.push_back(QuadSubmission{ + .position = position, + .size = size, + .rotation = rotation, + .color = color, + .textureId = textureId, + }); +} + +void QuadRenderingManager::ClearQuads() +{ + quads.clear(); +} \ No newline at end of file diff --git a/destrum/src/Graphics/Pipelines/QuadRendererPass.cpp b/destrum/src/Graphics/Pipelines/QuadRendererPass.cpp new file mode 100644 index 0000000..06fe5eb --- /dev/null +++ b/destrum/src/Graphics/Pipelines/QuadRendererPass.cpp @@ -0,0 +1,334 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "volk.h" + +namespace { + constexpr std::size_t InitialQuadCapacity = 256; + constexpr std::size_t VerticesPerQuad = 4; + constexpr std::size_t IndicesPerQuad = 6; + + glm::vec2 rotatePoint(const glm::vec2& point, float cosA, float sinA) { + return glm::vec2{ + point.x * cosA - point.y * sinA, + point.x * sinA + point.y * cosA, + }; + } +} + +void QuadRendererPass::init(GfxDevice& gfxDevice, RenderResources& resources, VkFormat drawImageFormat) +{ + try { + const std::size_t vertexBytes = InitialQuadCapacity * VerticesPerQuad * sizeof(QuadVertex); + vertexBuffer = gfxDevice.createBuffer( + vertexBytes, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_HOST); + + const std::size_t indexBytes = InitialQuadCapacity * IndicesPerQuad * sizeof(std::uint32_t); + indexBuffer = gfxDevice.createBuffer( + indexBytes, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_HOST); + + quadCapacity = InitialQuadCapacity; + + const auto vertexShader = + AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/quad.vert"); + const auto fragmentShader = + AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/quad.frag"); + + const auto pushConstantRange = VkPushConstantRange{ + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .offset = 0, + .size = sizeof(glm::mat4), + }; + + const auto layouts = std::array{ + resources.getBindlessDescSetLayout() + }; + + const auto pipelineLayoutInfo = VkPipelineLayoutCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = static_cast(layouts.size()), + .pSetLayouts = layouts.data(), + .pushConstantRangeCount = 1, + .pPushConstantRanges = &pushConstantRange, + }; + + VK_CHECK(vkCreatePipelineLayout( + gfxDevice.getDevice(), + &pipelineLayoutInfo, + nullptr, + &pipelineLayout)); + + PipelineConfigInfo pipelineConfig{}; + Pipeline::DefaultPipelineConfigInfo(pipelineConfig); + pipelineConfig.name = "Quad Rendering Pipeline"; + pipelineConfig.pipelineLayout = pipelineLayout; + pipelineConfig.inputAssemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + pipelineConfig.rasterizationInfo.cullMode = VK_CULL_MODE_NONE; + pipelineConfig.depthStencilInfo.depthTestEnable = VK_FALSE; + pipelineConfig.depthStencilInfo.depthWriteEnable = VK_FALSE; + pipelineConfig.depthStencilInfo.depthCompareOp = VK_COMPARE_OP_ALWAYS; + pipelineConfig.colorAttachments = {drawImageFormat}; + pipelineConfig.depthAttachment = VK_FORMAT_UNDEFINED; + + pipelineConfig.vertexBindingDescriptions = { + VkVertexInputBindingDescription{ + .binding = 0, + .stride = sizeof(QuadVertex), + .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, + } + }; + pipelineConfig.vertexAttributeDescriptions = { + VkVertexInputAttributeDescription{ + .location = 0, + .binding = 0, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = offsetof(QuadVertex, position), + }, + VkVertexInputAttributeDescription{ + .location = 1, + .binding = 0, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = offsetof(QuadVertex, uv), + }, + VkVertexInputAttributeDescription{ + .location = 2, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = offsetof(QuadVertex, color), + }, + VkVertexInputAttributeDescription{ + .location = 3, + .binding = 0, + .format = VK_FORMAT_R32_UINT, + .offset = offsetof(QuadVertex, textureId), + }, + }; + + pipelineConfig.colorBlendAttachment.blendEnable = VK_TRUE; + pipelineConfig.colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + pipelineConfig.colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + pipelineConfig.colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + pipelineConfig.colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + pipelineConfig.colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + pipelineConfig.colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + pipeline = std::make_unique( + gfxDevice, + vertexShader.string(), + fragmentShader.string(), + pipelineConfig); + } catch (...) { + cleanup(gfxDevice); + throw; + } +} + +void QuadRendererPass::draw( + VkCommandBuffer cmd, + GfxDevice& gfxDevice, + RenderResources& resources, + const GPUImage& target, + const glm::mat4& projection) +{ + auto& quadManager = QuadRenderingManager::GetInstance(); + const auto& quads = quadManager.GetQuads(); + if (!pipeline || quads.empty()) { + quadManager.ClearQuads(); + return; + } + + const std::size_t numQuads = quads.size(); + if (numQuads > std::numeric_limits::max() / VerticesPerQuad) { + throw std::overflow_error("Too many quads submitted for rendering"); + } + + const std::size_t numVertices = numQuads * VerticesPerQuad; + const std::size_t numIndices = numQuads * IndicesPerQuad; + + if (numVertices > std::numeric_limits::max()) { + throw std::overflow_error("Too many quad vertices submitted for rendering"); + } + + ensureCapacity(gfxDevice, numQuads); + + const ImageID whiteTextureId = resources.getWhiteTextureID(); + + auto* vertexMapped = reinterpret_cast(vertexBuffer.info.pMappedData); + auto* indexMapped = reinterpret_cast(indexBuffer.info.pMappedData); + + constexpr std::array localCorners = {{ + glm::vec2{-0.5f, -0.5f}, + glm::vec2{ 0.5f, -0.5f}, + glm::vec2{ 0.5f, 0.5f}, + glm::vec2{-0.5f, 0.5f}, + }}; + + constexpr std::array uvs = {{ + glm::vec2{0.0f, 1.0f}, + glm::vec2{1.0f, 1.0f}, + glm::vec2{1.0f, 0.0f}, + glm::vec2{0.0f, 0.0f}, + }}; + + auto* vertWrite = reinterpret_cast(vertexMapped); + auto* idxWrite = reinterpret_cast(indexMapped); + std::uint32_t vertexBase = 0; + + for (const auto& quad : quads) { + const float cosA = std::cos(quad.rotation); + const float sinA = std::sin(quad.rotation); + const glm::vec2 halfSize = quad.size * 0.5f; + const ImageID texId = (quad.textureId == NULL_IMAGE_ID) ? whiteTextureId : quad.textureId; + + for (std::size_t v = 0; v < VerticesPerQuad; ++v) { + glm::vec2 localPos = localCorners[v] * halfSize; + glm::vec2 rotatedPos = rotatePoint(localPos, cosA, sinA); + glm::vec2 worldPos = quad.position + rotatedPos; + + vertWrite[vertexBase + v] = QuadVertex{ + .position = worldPos, + .uv = uvs[v], + .color = quad.color, + .textureId = static_cast(texId), + }; + } + + const std::uint32_t idxOffset = vertexBase / 4 * 6; + idxWrite[idxOffset + 0] = vertexBase + 0; + idxWrite[idxOffset + 1] = vertexBase + 1; + idxWrite[idxOffset + 2] = vertexBase + 2; + idxWrite[idxOffset + 3] = vertexBase + 0; + idxWrite[idxOffset + 4] = vertexBase + 2; + idxWrite[idxOffset + 5] = vertexBase + 3; + vertexBase += VerticesPerQuad; + } + + auto& memMgr = gfxDevice.getMemoryManager(); + memMgr.flushAllocation(vertexBuffer, 0, numVertices * sizeof(QuadVertex)); + memMgr.flushAllocation(indexBuffer, 0, numIndices * sizeof(std::uint32_t)); + + auto renderInfo = vkutil::createRenderingInfo({ + .renderExtent = target.getExtent2D(), + .colorImageView = target.imageView, + }); + + vkCmdBeginRendering(cmd, &renderInfo.renderingInfo); + + pipeline->bind(cmd); + resources.bindBindlessDescSet(cmd, pipelineLayout); + + const auto viewport = VkViewport{ + .x = 0.0f, + .y = 0.0f, + .width = static_cast(target.extent.width), + .height = static_cast(target.extent.height), + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + vkCmdSetViewport(cmd, 0, 1, &viewport); + + const auto scissor = VkRect2D{ + .offset = {}, + .extent = target.getExtent2D(), + }; + vkCmdSetScissor(cmd, 0, 1, &scissor); + vkCmdSetPolygonModeEXT(cmd, VK_POLYGON_MODE_FILL); + + vkCmdPushConstants( + cmd, + pipelineLayout, + VK_SHADER_STAGE_VERTEX_BIT, + 0, + sizeof(glm::mat4), + &projection); + + const VkBuffer vertexBufferHandle = vertexBuffer.buffer; + const VkDeviceSize vertexBufferOffset = 0; + vkCmdBindVertexBuffers(cmd, 0, 1, &vertexBufferHandle, &vertexBufferOffset); + + const VkBuffer indexBufferHandle = indexBuffer.buffer; + vkCmdBindIndexBuffer(cmd, indexBufferHandle, 0, VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(cmd, static_cast(numIndices), 1, 0, 0, 0); + + vkCmdEndRendering(cmd); + quadManager.ClearQuads(); +} + +void QuadRendererPass::cleanup(GfxDevice& gfxDevice) +{ + pipeline.reset(); + + if (pipelineLayout != VK_NULL_HANDLE && gfxDevice.getDevice() != VK_NULL_HANDLE) { + vkDestroyPipelineLayout(gfxDevice.getDevice(), pipelineLayout, nullptr); + } + pipelineLayout = VK_NULL_HANDLE; + + if (vertexBuffer.buffer != VK_NULL_HANDLE) { + gfxDevice.destroyBuffer(vertexBuffer); + } + if (indexBuffer.buffer != VK_NULL_HANDLE) { + gfxDevice.destroyBuffer(indexBuffer); + } + quadCapacity = 0; +} + +void QuadRendererPass::ensureCapacity( + GfxDevice& gfxDevice, + std::size_t requiredQuads) +{ + if (requiredQuads <= quadCapacity) { + return; + } + + std::size_t newCapacity = std::max(quadCapacity, InitialQuadCapacity); + while (newCapacity < requiredQuads) { + if (newCapacity > std::numeric_limits::max() / 2) { + newCapacity = requiredQuads; + break; + } + newCapacity *= 2; + } + + gfxDevice.waitIdle(); + + if (vertexBuffer.buffer != VK_NULL_HANDLE) { + gfxDevice.destroyBuffer(vertexBuffer); + } + vertexBuffer = gfxDevice.createBuffer( + newCapacity * VerticesPerQuad * sizeof(QuadVertex), + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_HOST); + + if (indexBuffer.buffer != VK_NULL_HANDLE) { + gfxDevice.destroyBuffer(indexBuffer); + } + indexBuffer = gfxDevice.createBuffer( + newCapacity * IndicesPerQuad * sizeof(std::uint32_t), + VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO_PREFER_HOST); + + quadCapacity = newCapacity; +} \ No newline at end of file diff --git a/destrum/src/Graphics/Renderer.cpp b/destrum/src/Graphics/Renderer.cpp index de69a17..af8640e 100644 --- a/destrum/src/Graphics/Renderer.cpp +++ b/destrum/src/Graphics/Renderer.cpp @@ -1,11 +1,14 @@ #include #include +#include #include #include #include +#include + #include "volk.h" #include "destrum/Util/GameState.h" #include "spdlog/spdlog.h" @@ -31,7 +34,7 @@ void GameRenderer::init(GfxDevice& gfxDevice, RenderResources& _resources, glm:: meshPipeline = std::make_unique(); meshPipeline->init(gfxDevice, _resources, drawImageFormat, depthImageFormat); - skyboxPipeline = std::make_unique(); +skyboxPipeline = std::make_unique(); skyboxPipeline->init(gfxDevice, _resources, drawImageFormat, depthImageFormat); skinningPipeline = std::make_unique(); @@ -40,6 +43,9 @@ void GameRenderer::init(GfxDevice& gfxDevice, RenderResources& _resources, glm:: lineRenderingPass = std::make_unique(); lineRenderingPass->init(gfxDevice, drawImageFormat); + quadRendererPass = std::make_unique(); + quadRendererPass->init(gfxDevice, _resources, drawImageFormat); + GameState::GetInstance().SetRenderer(this); initialized = true; } catch (...) { @@ -53,6 +59,7 @@ void GameRenderer::beginDrawing(GfxDevice& gfxDevice) flushMaterialUpdates(gfxDevice); meshDrawCommands.clear(); skinningPipeline->beginDrawing(gfxDevice.getCurrentFrameIndex()); + QuadRenderingManager::GetInstance().ClearQuads(); } void GameRenderer::endDrawing() @@ -209,7 +216,30 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, LineRenderingManager::GetInstance()); } - // vkutil::cmdEndLabel(cmd); + + { + TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "QuadRendererPass::draw"); + + quadRendererPass->draw( + cmd, + gfxDevice, + *resources, + drawImage, + glm::ortho(0.0f, + static_cast(drawImage.extent.width), + static_cast(drawImage.extent.height), + 0.0f, -1.0f, 1.0f)); + } +// vkutil::cmdEndLabel(cmd); +} + +void GameRenderer::drawQuads( + VkCommandBuffer cmd, + GfxDevice& gfxDevice, + const glm::mat4& projection) +{ + const auto& drawImage = resources->getImage(drawImageId); + quadRendererPass->draw(cmd, gfxDevice, *resources, drawImage, projection); } void GameRenderer::cleanup(GfxDevice& gfxDevice) @@ -220,12 +250,15 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice) vkDeviceWaitIdle(device); } - if (skinningPipeline) +if (skinningPipeline) skinningPipeline->cleanup(gfxDevice); if (lineRenderingPass) lineRenderingPass->cleanup(gfxDevice); + if (quadRendererPass) + quadRendererPass->cleanup(gfxDevice); + if (skyboxPipeline) skyboxPipeline->cleanup(device); @@ -247,6 +280,7 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice) skyboxPipeline.reset(); skinningPipeline.reset(); lineRenderingPass.reset(); + quadRendererPass.reset(); pendingMaterialUploads.clear(); meshDrawCommands.clear(); sortedMeshDrawCommands.clear(); diff --git a/lightkeeper/include/Lightkeeper.h b/lightkeeper/include/Lightkeeper.h index 6174113..f6324cd 100644 --- a/lightkeeper/include/Lightkeeper.h +++ b/lightkeeper/include/Lightkeeper.h @@ -34,6 +34,8 @@ private: MeshID sphereMesh; MaterialID sphereMaterial; + ImageID texID; + std::unique_ptr skyboxCubemap; GameObject* capybara = nullptr; @@ -42,6 +44,8 @@ private: char newSceneName[128]{"EmptyScene"}; std::string sceneStatus; bool renderBoxColliders{false}; + bool renderQuads{true}; + bool renderDebugLines{true}; }; #endif //LIGHTKEEPER_H diff --git a/lightkeeper/src/Lightkeeper.cpp b/lightkeeper/src/Lightkeeper.cpp index 2bf9c29..b4d82d3 100644 --- a/lightkeeper/src/Lightkeeper.cpp +++ b/lightkeeper/src/Lightkeeper.cpp @@ -1,9 +1,11 @@ #include "Lightkeeper.h" #include +#include #include #include #include +#include #include "glm/gtx/transform.hpp" #include "spdlog/spdlog.h" #include @@ -444,6 +446,8 @@ void LightKeeper::customInit() triggerObj->AddComponent(); triggerObj->GetTransform().SetWorldPosition({0.0f, 0.0f, 0.0f}); + const auto texPath = AssetFS::GetInstance().GetFullPath("engine://textures/kobe.png"); + texID = resources.loadImageFromFile(gfxDevice, texPath); } @@ -487,6 +491,7 @@ void LightKeeper::customUpdate(float dt) } ImGui::End(); } + } void LightKeeper::drawDebugMenuBar() @@ -526,6 +531,8 @@ void LightKeeper::drawDebugMenuBar() if (ImGui::BeginMenu("Render")) { ImGui::MenuItem("Box Colliders", nullptr, &renderBoxColliders); + ImGui::MenuItem("2D Quads", nullptr, &renderQuads); + ImGui::MenuItem("Debug Lines", nullptr, &renderDebugLines); ImGui::EndMenu(); } @@ -660,6 +667,10 @@ void LightKeeper::customDraw() renderer.beginDrawing(gfxDevice); submitBoxColliderDebugLines(); + QuadRenderingManager::GetInstance().SubmitQuad( + glm::vec2{100.0f, 100.0f}, glm::vec2{200.0f, 200.0f}, 0.0f, + glm::vec4{1.0f}, texID); + const RenderContext ctx{ .renderer = renderer, .camera = camera,