diff --git a/examples/ExampleAppList.hpp b/examples/ExampleAppList.hpp index d49362e..bcdf0e5 100644 --- a/examples/ExampleAppList.hpp +++ b/examples/ExampleAppList.hpp @@ -11,6 +11,7 @@ #include "ExampleApps/TexturedCubeExampleApp.hpp" #include "ExampleApps/BillboardExampleApp.hpp" #include "ExampleApps/TextExampleApp.hpp" +#include "ExampleApps/LabelDrawableExampleApp.hpp" #include namespace OpenVulkano @@ -20,6 +21,7 @@ namespace OpenVulkano { "Moving Cube Example App", &MovingCubeApp::Create }, { "Textured Cube Example App", &TexturedCubeExampleApp::Create }, { "Billboard Example App", &BillboardExampleApp::Create }, - { "Text Example App", &TextExampleApp::Create } + { "Text Example App", &TextExampleApp::Create }, + { "Label Example App", &LabelDrawableExampleApp::Create } }; } diff --git a/examples/ExampleApps/LabelDrawableExampleApp.cpp b/examples/ExampleApps/LabelDrawableExampleApp.cpp new file mode 100644 index 0000000..d718b77 --- /dev/null +++ b/examples/ExampleApps/LabelDrawableExampleApp.cpp @@ -0,0 +1,133 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "LabelDrawableExampleApp.hpp" +#include "Scene/Scene.hpp" +#include "Scene/Shader/Shader.hpp" +#include "Scene/Geometry.hpp" +#include "Scene/TextDrawable.hpp" +#include "Scene/GeometryFactory.hpp" +#include "Scene/Material.hpp" +#include "Scene/Vertex.hpp" +#include "Scene/SimpleDrawable.hpp" +#include "Scene/UI/PerformanceInfo.hpp" +#include "Scene/UniformBuffer.hpp" +#include "Scene/Prefabs/LabelDrawable.hpp" +#include "Input/InputManager.hpp" +#include "Host/GraphicsAppManager.hpp" +#include "Host/GLFW/WindowGLFW.hpp" +#include "Host/ResourceLoader.hpp" +#include "Math/Math.hpp" +#include "Base/EngineConfiguration.hpp" +#include "Controller/FreeCamCameraController.hpp" +#include "Image/ImageLoaderPng.hpp" +#include "Scene/FontAtlasGenerator.hpp" +#include "Scene/IFontAtlasGenerator.hpp" +#include + +#ifdef _WIN32 + #undef TRANSPARENT +#endif + +namespace OpenVulkano +{ + using namespace Scene; + using namespace Input; + using namespace Math; + namespace fs = std::filesystem; + + class LabelDrawableExampleAppImpl final : public LabelDrawableExampleApp + { + public: + + void Init() override + { + auto engineConfig = OpenVulkano::EngineConfiguration::GetEngineConfiguration(); + engineConfig->SetNumThreads(1); + engineConfig->SetPreferFramebufferFormatSRGB(false); + + std::srand(1); // Fix seed for random numbers + m_scene.Init(); + m_cam.Init(70, 16, 9, 0.1, 100); + m_scene.SetCamera(&m_cam); + + auto& resourceLoader = ResourceLoader::GetInstance(); + auto sdfMetadataInfo = resourceLoader.GetResource("sdf_atlas_packed.png"); + + const int N = 3; + m_nodesPool.resize(N); + m_drawablesPool.reserve(N); + + static TextDrawable textDrawable(sdfMetadataInfo); + auto& sh = TextDrawable::GetSdfDefaultShader(); + sh.depthWrite = false; + sh.depthCompareOp = CompareOp::LESS_OR_EQUAL; + textDrawable.SetShader(&sh); + const std::vector texts = { "Hello", "1", "XYZ" }; + BillboardControlBlock billboardSettings; + LabelDrawableSettings labelSettings; + + for (int i = 0; i < N; i++) + { + //labelSettings.hasRoundedCorners = (i % 2 == 0 ? 1 : 0); + labelSettings.hasArrow = (i % 2 == 0 ? 1 : 0); + bool isBillboard = (i % 2 == 0 ? 1 : 0); + LabelDrawable& label = m_drawablesPool.emplace_back(labelSettings, isBillboard); + label.SetBillboardSettings(billboardSettings); + label.AddText(&textDrawable, texts[i]); + if (i == 2) + { + for (int j = 0; j < 3; j++) + { + label.AddText(&textDrawable, "Additional text" + std::to_string(j)); + } + } + m_scene.GetRoot()->AddChild(&m_nodesPool[i]); + m_nodesPool[i].SetMatrix(Math::Utils::translate(glm::mat4x4(1.f), Vector3f(-5 + std::rand() % 5, -5 + std::rand() % 5, -std::rand() % 10))); + m_nodesPool[i].AddDrawable(&m_drawablesPool[i]); + } + + GetGraphicsAppManager()->GetRenderer()->SetScene(&m_scene); + m_camController.Init(&m_cam); + m_camController.SetDefaultKeybindings(); + m_camController.SetPosition({ 0, 0, 10 }); + m_camController.SetBoostFactor(5); + + std::shared_ptr m_perfInfo = + std::make_shared(); + m_ui.AddElement(m_perfInfo); + GetGraphicsAppManager()->GetRenderer()->SetActiveUi(&m_ui); + } + + void Close() override {} + + void Tick() override + { + m_camController.Tick(); + } + + private: + OpenVulkano::Scene::Scene m_scene; + PerspectiveCamera m_cam; + OpenVulkano::FreeCamCameraController m_camController; + std::vector m_drawablesPool; + std::vector m_nodesPool; + Vector3f_SIMD m_position = { 0, 0, -10 }; + OpenVulkano::Scene::UI::SimpleUi m_ui; + std::shared_ptr m_perfInfo; + }; + + IGraphicsApp* LabelDrawableExampleApp::Create() { return new LabelDrawableExampleAppImpl(); } + + std::unique_ptr LabelDrawableExampleApp::CreateUnique() + { + return std::make_unique(); + } + +} + +#pragma clang diagnostic pop +#pragma clang diagnostic pop \ No newline at end of file diff --git a/examples/ExampleApps/LabelDrawableExampleApp.hpp b/examples/ExampleApps/LabelDrawableExampleApp.hpp new file mode 100644 index 0000000..12198b1 --- /dev/null +++ b/examples/ExampleApps/LabelDrawableExampleApp.hpp @@ -0,0 +1,25 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "Base/IGraphicsApp.hpp" +#include + +namespace OpenVulkano +{ + class LabelDrawableExampleApp : public IGraphicsApp + { + public: + static IGraphicsApp* Create(); + + static std::unique_ptr CreateUnique(); + + [[nodiscard]] std::string GetAppName() const final { return "Label drawable ExampleApp"; } + + [[nodiscard]] OpenVulkano::Version GetAppVersion() const final { return { "v1.0" }; } + }; +} \ No newline at end of file diff --git a/openVulkanoCpp/Scene/Prefabs/LabelDrawable.cpp b/openVulkanoCpp/Scene/Prefabs/LabelDrawable.cpp new file mode 100644 index 0000000..abe1f86 --- /dev/null +++ b/openVulkanoCpp/Scene/Prefabs/LabelDrawable.cpp @@ -0,0 +1,257 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "LabelDrawable.hpp" +#include "Scene/TextDrawable.hpp" +#include "Scene/DrawEncoder.hpp" +#include "Scene/IFontAtlasGenerator.hpp" +#include "Base/Logger.hpp" +#include + +namespace OpenVulkano::Scene +{ + using namespace Math; + + LabelDrawable::LabelDrawable(const LabelDrawableSettings& settings, bool isBillboard) + : Drawable(DrawEncoder::GetDrawEncoder(), DrawPhase::MAIN) + { + m_isBillboard = isBillboard; + SetLabelSettings(settings); + SetupShaders(); + SetupBuffers(); + m_backgroundGeometry.SetFreeAfterUpload(false); + } + + void LabelDrawable::SetLabelSettings(const LabelDrawableSettings& settings) + { + m_settings = settings; + m_labelData.hasRoundedCorners = settings.hasRoundedCorners; + m_labelData.hasArrow = settings.hasArrow; + m_labelData.cornerRadius = settings.cornerRadius; + m_labelData.arrowLength = settings.arrowLength; + } + + void LabelDrawable::AddText(TextDrawable* td, const std::string& text) + { + if (!td || text.empty()) + { + return; + } + if (!td->GetShader()) + { + Logger::RENDER->error("Text drawable shader is null, cannot add text to label drawable\n"); + return; + } + + TextDrawable& textDrawable = m_texts.emplace_back(td->GetConfig()); + // do not render glyph's background + textDrawable.GetConfig().backgroundColor.a = 0; + if (m_isBillboard) + { + textDrawable.SetShader(GetBillboardTextShader(td->GetShader())); + } + else + { + textDrawable.SetShader(td->GetShader()); + } + double lineHeight; + if (textDrawable.GetFontAtlasGenerator()) + { + textDrawable.SetFontAtlasGenerator(td->GetFontAtlasGenerator()); + lineHeight = td->GetFontAtlasGenerator()->GetAtlasMetadata().lineHeight; + } + else + { + textDrawable.SetAtlasData(td->GetAtlasData()); + lineHeight = td->GetAtlasData()->meta.lineHeight; + } + textDrawable.GenerateText(text, m_position); + RecalculateBbox(textDrawable.GetBoundingBox()); + // update position for next text entry + m_position.y = m_bbox.GetMin().y - lineHeight; + + if (!m_settings.hasArrow) + { + if (m_backgroundGeometry.vertexCount != 4) + { + m_backgroundGeometry.Init(4, 6); + uint32_t indices[6] = { 0, 1, 2, 0, 2, 3 }; + m_backgroundGeometry.SetIndices(indices, 6); + } + } + else + { + if (m_backgroundGeometry.vertexCount != 7) + { + m_backgroundGeometry.Init(7, 9); + uint32_t indices[9] = { 0, 1, 2, 0, 2, 3, 4, 5, 6 }; + m_backgroundGeometry.SetIndices(indices, 9); + } + } + + const auto& min = m_bbox.GetMin(); + const auto& max = m_bbox.GetMax(); + Vertex v, v2, v3, v4; + v.color = v2.color = v3.color = v4.color = m_settings.backgroundColor; + const float offset = 0.001; + v.position = Vector3f(min.x - m_settings.horizontalOffset, min.y - m_settings.verticalOffset, min.z - offset); + v2.position = Vector3f(max.x + m_settings.horizontalOffset, min.y - m_settings.verticalOffset, min.z - offset); + v3.position = Vector3f(max.x + m_settings.horizontalOffset, max.y + m_settings.verticalOffset, min.z - offset); + v4.position = Vector3f(min.x - m_settings.horizontalOffset, max.y + m_settings.verticalOffset, min.z - offset); + if (!m_settings.hasArrow) + { + v.textureCoordinates = Vector3f(0, 0, 0); + v2.textureCoordinates = Vector3f(1, 0, 0); + v3.textureCoordinates = Vector3f(1, 1, 0); + v4.textureCoordinates = Vector3f(0, 1, 0); + } + else + { + const float w = v2.position.x - v.position.x; + const float h = v3.position.y - v2.position.y + m_settings.arrowLength; + v.textureCoordinates = Vector3f(0, v.position.y / h, 0); + v2.textureCoordinates = Vector3f(1, v2.position.y / h, 0); + v3.textureCoordinates = Vector3f(1, 1, 0); + v4.textureCoordinates = Vector3f(0, 1, 0); + // arrow vertices + Vertex a, b, c; + a.position = Vector3f(v.position.x + w / 3, v.position.y, v.position.z); + b.position = Vector3f(v.position.x + w / 2, v.position.y - m_settings.arrowLength, v.position.z); + c.position = Vector3f(v2.position.x - w / 3, v.position.y, v.position.z); + a.color = b.color = c.color = m_settings.backgroundColor; + a.textureCoordinates = Vector3f(a.position.x / w, a.position.y / h, 0); + b.textureCoordinates = Vector3f(b.position.x / w, 0, 0); + c.textureCoordinates = Vector3f(c.position.x / w, c.position.y / h, 0); + m_backgroundGeometry.vertices[4] = a; + m_backgroundGeometry.vertices[5] = b; + m_backgroundGeometry.vertices[6] = c; + } + m_backgroundGeometry.vertices[0] = v; + m_backgroundGeometry.vertices[1] = v2; + m_backgroundGeometry.vertices[2] = v3; + m_backgroundGeometry.vertices[3] = v4; + + if (m_settings.hasRoundedCorners) + { + m_labelData.textSize.x = v2.position.x - v.position.x; + m_labelData.textSize.y = v3.position.y - v.position.y; + } + } + + void LabelDrawable::SetBillboardSettings(const BillboardControlBlock& settings) + { + m_billboardSettings = settings; + } + + void LabelDrawable::RecalculateBbox(const Math::AABB& other) + { + if (m_bbox.IsEmpty()) + { + m_bbox = other; + } + else + { + auto& currentMin = m_bbox.GetMin(); + auto& currentMax = m_bbox.GetMax(); + currentMin.x = std::min(currentMin.x, other.min.x); + currentMin.y = std::min(currentMin.y, other.min.y); + currentMax.x = std::max(currentMax.x, other.max.x); + currentMax.y = std::max(currentMax.y, other.max.y); + } + } + + // if we have want our label to be billboard, then we'll create new shader and set it's vertex shader to billboard.vert + // to avoid modifying input shader + Shader* LabelDrawable::GetBillboardTextShader(Shader* src) + { + auto GetFragmentShaderName = [](const Shader& shaderToCheck) -> std::optional + { + const auto& shaderPrograms = shaderToCheck.shaderPrograms; + auto pos = std::find_if(shaderPrograms.begin(), shaderPrograms.end(), + [](const ShaderProgram& program) + { return program.type == ShaderProgramType::FRAGMENT; }); + return pos != shaderPrograms.end() ? std::optional(pos->name) : std::nullopt; + }; + + std::optional inputFragmentName = GetFragmentShaderName(*src); + if (!inputFragmentName) + { + throw std::runtime_error("Shader doesn't have fragment program"); + } + + bool exists = false; + int shaderIdx = 0; + for (; shaderIdx < m_billboardTextShaders.size(); shaderIdx++) + { + std::optional name = GetFragmentShaderName(m_billboardTextShaders[shaderIdx]); + if (name.value() == inputFragmentName.value()) + { + exists = true; + break; + } + } + + if (!exists) + { + Shader& shader = m_billboardTextShaders.emplace_back(); + shaderIdx = m_billboardTextShaders.size() - 1; + shader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/billboard"); + shader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, inputFragmentName.value()); + shader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription()); + shader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING); + DescriptorSetLayoutBinding binding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING; + binding.stageFlags = ShaderProgramType::Type::VERTEX; + DescriptorSetLayoutBinding binding2 = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING; + binding2.stageFlags = ShaderProgramType::FRAGMENT; + shader.AddDescriptorSetLayoutBinding(binding2, 3); + shader.AddDescriptorSetLayoutBinding(binding, 4); + shader.depthWrite = false; + shader.depthCompareOp = CompareOp::LESS_OR_EQUAL; + shader.alphaBlend = true; + shader.cullMode = CullMode::NONE; + } + return &m_billboardTextShaders[shaderIdx]; + } + + void LabelDrawable::SetupShaders() + { + DescriptorSetLayoutBinding binding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING; + if (!m_isBillboard) + { + m_backgroundShader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/basic"); + } + else + { + m_backgroundShader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/billboard"); + // binding for billboard's buffer + binding.stageFlags = ShaderProgramType::Type::VERTEX; + m_backgroundShader.AddDescriptorSetLayoutBinding(binding, 4); + } + m_backgroundShader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, "Shader/label"); + m_backgroundShader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription()); + m_backgroundShader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING, 2); + binding.stageFlags = ShaderProgramType::Type::FRAGMENT; + m_backgroundShader.AddDescriptorSetLayoutBinding(binding, 5); + m_backgroundShader.cullMode = CullMode::NONE; + SetShader(&m_backgroundShader); + } + + void LabelDrawable::SetupBuffers() + { + DescriptorSetLayoutBinding binding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING; + binding.stageFlags = ShaderProgramType::Type::VERTEX; + m_billboardBuffer.size = sizeof(BillboardControlBlock); + m_billboardBuffer.data = &m_billboardSettings; + m_billboardBuffer.setId = 4; + m_billboardBuffer.binding = binding; + + binding.stageFlags = ShaderProgramType::Type::FRAGMENT; + m_labelBuffer.size = sizeof(LabelUniformData); + m_labelBuffer.data = &m_labelData; + m_labelBuffer.setId = 5; + m_labelBuffer.binding = binding; + } +} diff --git a/openVulkanoCpp/Scene/Prefabs/LabelDrawable.hpp b/openVulkanoCpp/Scene/Prefabs/LabelDrawable.hpp new file mode 100644 index 0000000..7d2253c --- /dev/null +++ b/openVulkanoCpp/Scene/Prefabs/LabelDrawable.hpp @@ -0,0 +1,83 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "Base/Wrapper.hpp" +#include "Scene/Drawable.hpp" +#include "Scene/Shader/Shader.hpp" +#include "Scene/Texture.hpp" +#include "Scene/UniformBuffer.hpp" +#include "Scene/Vertex.hpp" +#include "Scene/BillboardControlBlock.hpp" +#include "Math/AABB.hpp" +#include "Scene/TextDrawable.hpp" +#include + +namespace OpenVulkano::Scene +{ + + struct LabelDrawableSettings + { + Math::Vector4f backgroundColor = { 1, 0, 0, 1 }; + float horizontalOffset = 0.05f; + float verticalOffset = 0.05f; + float cornerRadius = 0.5f; + float arrowLength = 0.5f; + int32_t hasRoundedCorners = false; + int32_t hasArrow = false; + }; + + namespace + { + struct LabelUniformData + { + Math::Vector2f textSize = { 0, 0 }; + float cornerRadius = 0.f; + float arrowLength = 0.f; + int32_t hasRoundedCorners = false; + int32_t hasArrow = false; + }; + }; + + class LabelDrawable final : public Drawable + { + public: + LabelDrawable(const LabelDrawableSettings& settings = LabelDrawableSettings(), bool isBillboard = false); + void AddText(TextDrawable* td, const std::string& text); + void SetLabelSettings(const LabelDrawableSettings& settings); + void SetBillboardSettings(const BillboardControlBlock& settings); + void SetPosition(const Math::Vector3f& pos) { m_position = pos; } + std::list& GetTexts() { return m_texts; } + LabelDrawableSettings& GetSettings() { return m_settings; } + UniformBuffer* GetBillboardBuffer() { return &m_billboardBuffer; } + UniformBuffer* GetLabelBuffer() { return &m_labelBuffer; } + Geometry& GetBackgroundGeometry() { return m_backgroundGeometry; } + BillboardControlBlock& GetBillboardSettings() { return m_billboardSettings; } + Math::Vector3f& GetPosition() { return m_position; } + bool IsBillboard() const { return m_isBillboard; } + const Math::AABB& GetBoundingBox() const { return m_bbox; } + private: + void RecalculateBbox(const Math::AABB& other); + Shader* GetBillboardTextShader(Shader* src); + void SetupShaders(); + void SetupBuffers(); + private: + Shader m_backgroundShader; + Geometry m_backgroundGeometry; + UniformBuffer m_billboardBuffer; + UniformBuffer m_labelBuffer; + std::vector m_billboardTextShaders; + // list over vector to prevent memory reallocation and crash + std::list m_texts; + LabelDrawableSettings m_settings; + LabelUniformData m_labelData; + BillboardControlBlock m_billboardSettings; + Math::Vector3f m_position = { 0, 0, 0 }; + Math::AABB m_bbox; + bool m_isBillboard; + }; +} diff --git a/openVulkanoCpp/Shader/label.frag b/openVulkanoCpp/Shader/label.frag new file mode 100644 index 0000000..95780af --- /dev/null +++ b/openVulkanoCpp/Shader/label.frag @@ -0,0 +1,44 @@ +#version 450 + +layout(location = 0) in vec4 color; +layout(location = 1) in vec2 texCoord; +layout(location = 0) out vec4 outColor; + +layout(set = 5, binding = 0) uniform LabelData +{ + vec2 textSize; + float radius; + float arrowLength; + bool hasRoundedCorners; + bool hasArrow; +} labelInfo; + +void main() +{ + if (labelInfo.hasRoundedCorners) + { + vec2 bbox; + if (!labelInfo.hasArrow) + { + bbox = labelInfo.textSize; + } + else + { + bbox = vec2(labelInfo.textSize.x, labelInfo.textSize.y + labelInfo.arrowLength); + } + + const float arrowLength = labelInfo.hasArrow ? labelInfo.arrowLength : 0; + vec2 uvScaled = texCoord * bbox; + //vec2 uvNoArrow = texCoord * labelInfo.textSize; + float distX = min(uvScaled.x, bbox.x - uvScaled.x); + float distY = min(uvScaled.y - arrowLength, bbox.y - uvScaled.y); + + float distanceFromEdge = distX * distY; + float scaledRadius = labelInfo.radius/10000.f; + if (distanceFromEdge < scaledRadius) + { + discard; + } + } + outColor = color; +} \ No newline at end of file diff --git a/openVulkanoCpp/Vulkan/Scene/LabelDrawableVulkanEncoder.cpp b/openVulkanoCpp/Vulkan/Scene/LabelDrawableVulkanEncoder.cpp new file mode 100644 index 0000000..0d7053e --- /dev/null +++ b/openVulkanoCpp/Vulkan/Scene/LabelDrawableVulkanEncoder.cpp @@ -0,0 +1,137 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "Scene/Prefabs/LabelDrawable.hpp" +#include "Scene/SimpleDrawable.hpp" +#include "Scene/TextDrawable.hpp" +#include "Scene/DrawEncoder.hpp" +#include "VulkanGeometry.hpp" +#include "Vulkan/VulkanDrawContext.hpp" +#include "Vulkan/Scene/VulkanTexture.hpp" +#include "Vulkan/Scene/VulkanUniformBuffer.hpp" + +using namespace OpenVulkano::Scene; + +namespace OpenVulkano::Vulkan +{ + + void EncodeBackground(LabelDrawable* labelDrawable, Vulkan::VulkanDrawContext* drawContext) + { + if (labelDrawable->IsBillboard()) + { + OpenVulkano::Scene::UniformBuffer* buffer = labelDrawable->GetBillboardBuffer(); + if (buffer) + { + VulkanUniformBuffer* vkBuffer = static_cast(buffer->renderBuffer); + if (!vkBuffer) + { + vkBuffer = drawContext->renderer->GetResourceManager().PrepareUniformBuffer(buffer); + } + vkBuffer->Record(drawContext); + } + } + + OpenVulkano::Scene::UniformBuffer* labelBuffer = labelDrawable->GetLabelBuffer(); + VulkanUniformBuffer* vkBuffer = static_cast(labelBuffer->renderBuffer); + if (!vkBuffer) + { + vkBuffer = drawContext->renderer->GetResourceManager().PrepareUniformBuffer(labelBuffer); + } + vkBuffer->Record(drawContext); + + Geometry* mesh = &labelDrawable->GetBackgroundGeometry(); + VulkanGeometry* renderGeo = static_cast(mesh->renderGeo); + if (!renderGeo) + { + renderGeo = drawContext->renderer->GetResourceManager().PrepareGeometry(mesh); + } + renderGeo->RecordBind(drawContext->commandBuffer); + + for (Node* node: labelDrawable->GetNodes()) + { + if (!node->IsEnabled()) [[unlikely]] + { + continue; + } + drawContext->EncodeNode(node); + renderGeo->RecordDraw(drawContext->commandBuffer); + } + } + + void EncodeTextDrawable(LabelDrawable* labelDrawable, Vulkan::VulkanDrawContext* drawContext) + { + for (TextDrawable& entry : labelDrawable->GetTexts()) + { + OpenVulkano::Scene::Shader* shader = entry.GetShader(); + drawContext->EncodeShader(shader); + Geometry* mesh = entry.GetMesh(); + VulkanGeometry* renderGeo = static_cast(mesh->renderGeo); + if (!renderGeo) + { + renderGeo = drawContext->renderer->GetResourceManager().PrepareGeometry(mesh); + } + renderGeo->RecordBind(drawContext->commandBuffer); + + std::array uniforms = { nullptr, nullptr }; + // fragment shader buffer + uniforms[0] = entry.GetBuffer(); + if (labelDrawable->IsBillboard()) + { + // vertex shader buffer + uniforms[1] = labelDrawable->GetBillboardBuffer(); + } + + for (OpenVulkano::Scene::UniformBuffer* buffer : uniforms) + { + if (buffer) + { + VulkanUniformBuffer* vkBuffer = static_cast(buffer->renderBuffer); + if (!vkBuffer) + { + vkBuffer = drawContext->renderer->GetResourceManager().PrepareUniformBuffer(buffer); + } + vkBuffer->Record(drawContext); + } + } + + if (Material* material = entry.GetMaterial()) + { + if (Texture* texture = material->texture) + { + VulkanTexture* renderTexture = static_cast(texture->renderTexture); + if (!renderTexture) + { + drawContext->renderer->GetResourceManager().PrepareMaterial(entry.GetMaterial()); + renderTexture = static_cast(texture->renderTexture); + } + renderTexture->Record(drawContext); + } + } + + for (Node* node: labelDrawable->GetNodes()) + { + if (!node->IsEnabled()) [[unlikely]] + { + continue; + } + drawContext->EncodeNode(node); + renderGeo->RecordDraw(drawContext->commandBuffer); + } + } + } + + void EncodeLabelDrawable(Drawable* instance, Vulkan::VulkanDrawContext* drawContext) + { + LabelDrawable* labelDrawable = static_cast(instance); + EncodeBackground(labelDrawable, drawContext); + EncodeTextDrawable(labelDrawable, drawContext); + } +} + +namespace +{ + void* labelDrawableVulkanEncoderReg = DrawEncoder::RegisterVulkanEncodeFunction(&OpenVulkano::Vulkan::EncodeLabelDrawable); +} \ No newline at end of file