diff --git a/examples/ExampleApps/LabelDrawableExampleApp.cpp b/examples/ExampleApps/LabelDrawableExampleApp.cpp index 21dffe2..1ca40b7 100644 --- a/examples/ExampleApps/LabelDrawableExampleApp.cpp +++ b/examples/ExampleApps/LabelDrawableExampleApp.cpp @@ -57,19 +57,26 @@ namespace OpenVulkano auto& resourceLoader = ResourceLoader::GetInstance(); auto sdfMetadataInfo = resourceLoader.GetResource("sdf_atlas_packed.png"); - const int N = 3; + TextDrawable textDrawable(sdfMetadataInfo); + const std::vector texts = { "_!?{}.#@", "1", "XYZ", "12345" }; + const int N = texts.size(); m_nodesPool.resize(N); m_drawablesPool.reserve(N); - TextDrawable textDrawable(sdfMetadataInfo); - 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); + if (i == 3) + { + labelSettings.hasRoundedCorners = labelSettings.hasArrow = true; + } + else + { + labelSettings.hasRoundedCorners = (i % 2 == 0 ? 0 : 1); + labelSettings.hasArrow = (i % 2 == 0 ? 1 : 0); + } bool isBillboard = (i % 2 == 0 ? 1 : 0); LabelDrawable& label = m_drawablesPool.emplace_back(textDrawable.GetAtlasData(), labelSettings, isBillboard); label.SetBillboardSettings(billboardSettings); diff --git a/openVulkanoCpp/Scene/Prefabs/LabelDrawable.cpp b/openVulkanoCpp/Scene/Prefabs/LabelDrawable.cpp index 5473c10..4ca1aff 100644 --- a/openVulkanoCpp/Scene/Prefabs/LabelDrawable.cpp +++ b/openVulkanoCpp/Scene/Prefabs/LabelDrawable.cpp @@ -28,16 +28,17 @@ namespace OpenVulkano::Scene SetLabelSettings(settings); SetupShaders(); SetupBuffers(); - m_backgroundGeometry.SetFreeAfterUpload(false); } void LabelDrawable::SetLabelSettings(const LabelDrawableSettings& settings) { m_settings = settings; + m_labelData.color = settings.backgroundColor; m_labelData.hasRoundedCorners = settings.hasRoundedCorners; m_labelData.hasArrow = settings.hasArrow; - m_labelData.cornerRadius = settings.cornerRadius; + m_labelData.cornerRadius = settings.cornerRadius * settings.cornerRadius; m_labelData.arrowLength = settings.arrowLength; + m_labelData.arrowWidth = settings.arrowWidth; } void LabelDrawable::AddText(const std::string& text, const TextConfig& config) @@ -47,8 +48,8 @@ namespace OpenVulkano::Scene return; } - // do not render glyph's background TextDrawable& textDrawable = m_texts.emplace_back(m_atlasData, config); + // do not render glyph's background textDrawable.GetConfig().backgroundColor.a = 0; textDrawable.SetShader(&m_textShader); double lineHeight = m_atlasData->meta.lineHeight; @@ -58,72 +59,20 @@ namespace OpenVulkano::Scene // 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); + const float yOffset = m_settings.hasArrow ? m_settings.arrowLength : 0; + v.position = Vector3f(min.x - m_settings.horizontalOffset, min.y - m_settings.verticalOffset - yOffset, min.z - offset); + v2.position = Vector3f(max.x + m_settings.horizontalOffset, min.y - m_settings.verticalOffset - yOffset, 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; - } + m_labelData.textSize.x = v2.position.x - v.position.x; + m_labelData.textSize.y = v3.position.y - v.position.y; + m_labelData.bboxCenter.x = (v2.position.x + v.position.x) / 2; + m_labelData.bboxCenter.y = (v3.position.y + v.position.y) / 2; } void LabelDrawable::SetBillboardSettings(const BillboardControlBlock& settings) @@ -150,23 +99,22 @@ namespace OpenVulkano::Scene void LabelDrawable::SetupShaders() { - DescriptorSetLayoutBinding binding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING; if (!m_isBillboard) { - m_backgroundShader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/basic"); + m_backgroundShader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/label"); } else { - m_backgroundShader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/billboard"); + m_backgroundShader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/labelBillboard"); // binding for billboard's buffer + DescriptorSetLayoutBinding binding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING; 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.AddDescriptorSetLayoutBinding(UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING, 5); + m_backgroundShader.topology = Topology::TRIANGLE_STRIP; m_backgroundShader.cullMode = CullMode::NONE; SetShader(&m_backgroundShader); @@ -198,17 +146,16 @@ namespace OpenVulkano::Scene 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; + DescriptorSetLayoutBinding binding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING; + binding.stageFlags = ShaderProgramType::Type::VERTEX; 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; + m_labelBuffer.binding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING; } } diff --git a/openVulkanoCpp/Scene/Prefabs/LabelDrawable.hpp b/openVulkanoCpp/Scene/Prefabs/LabelDrawable.hpp index 40aa851..c8269fb 100644 --- a/openVulkanoCpp/Scene/Prefabs/LabelDrawable.hpp +++ b/openVulkanoCpp/Scene/Prefabs/LabelDrawable.hpp @@ -25,22 +25,23 @@ namespace OpenVulkano::Scene Math::Vector4f backgroundColor = { 1, 0, 0, 1 }; float horizontalOffset = 0.05f; float verticalOffset = 0.05f; - float cornerRadius = 0.5f; + float cornerRadius = 0.05f; float arrowLength = 0.5f; + float arrowWidth = 0.2f; int32_t hasRoundedCorners = false; int32_t hasArrow = false; }; - namespace + struct LabelUniformData { - struct LabelUniformData - { - Math::Vector2f textSize = { 0, 0 }; - float cornerRadius = 0.f; - float arrowLength = 0.f; - int32_t hasRoundedCorners = false; - int32_t hasArrow = false; - }; + Math::Vector4f textSize = { 0, 0, 0, 0 }; + Math::Vector4f color = { 0, 0, 0, 0 }; + Math::Vector4f bboxCenter = { 0, 0, 0, 0 }; + float cornerRadius = 0.f; + float arrowLength = 0.f; + float arrowWidth = 0.f; + int32_t hasRoundedCorners = false; + int32_t hasArrow = false; }; class LabelDrawable final : public Drawable @@ -56,7 +57,6 @@ namespace OpenVulkano::Scene 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; } @@ -67,10 +67,8 @@ namespace OpenVulkano::Scene 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; Shader m_textShader; diff --git a/openVulkanoCpp/Shader/label.frag b/openVulkanoCpp/Shader/label.frag index 95780af..33c8fc5 100644 --- a/openVulkanoCpp/Shader/label.frag +++ b/openVulkanoCpp/Shader/label.frag @@ -1,44 +1,73 @@ #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; + vec4 textSize; + vec4 color; + vec4 bboxCenter; float radius; float arrowLength; + float arrowWidth; 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); - } + if (labelInfo.hasRoundedCorners || labelInfo.hasArrow) + { + vec2 bbox = vec2(labelInfo.textSize); + const float arrowLength = int(labelInfo.hasArrow) * labelInfo.arrowLength; + float arrowWidth = labelInfo.arrowWidth; + vec2 uvScaled = texCoord; - 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) + float distanceFromCorner = distX * distY; + + if (distanceFromCorner < labelInfo.radius) { - discard; - } + // plain arrow + if (labelInfo.hasArrow && !labelInfo.hasRoundedCorners) + { + // Some bias to prevent line between arrow and label + + // check that we are dealing with lower parts of the label where arrow should be drawn, + // to prevent discarding fragments from upper corners + if (distY <= 0.01 && uvScaled.y < (bbox.y - arrowWidth) && arrowLength != 0.0f) + { + arrowWidth = arrowWidth * ((arrowLength + distY) / arrowLength); + if (distX < (bbox.x - arrowWidth) * 0.5) + { + discard; + } + } + } + // TODO: rounded corners + rounded arrow. + // now renders rounded corners and sharp arrow + else if (labelInfo.hasArrow && labelInfo.hasRoundedCorners) + { + if (distY <= 0.05 && uvScaled.y < (bbox.y - arrowWidth) && arrowLength != 0.0f) + { + arrowWidth = arrowWidth * ((arrowLength + distY) / arrowLength); + if (distX < (bbox.x - arrowWidth) * 0.5) + { + discard; + } + } + else + { + discard; + } + } + // no arrow, rounded corners + else + { + discard; + } + } } - outColor = color; + outColor = labelInfo.color; } \ No newline at end of file diff --git a/openVulkanoCpp/Shader/label.vert b/openVulkanoCpp/Shader/label.vert new file mode 100644 index 0000000..ca0380d --- /dev/null +++ b/openVulkanoCpp/Shader/label.vert @@ -0,0 +1,45 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set = 0, binding = 0) uniform NodeData +{ + mat4 world; +} node; + +layout(set = 1, binding = 0) uniform CameraData +{ + mat4 viewProjection; +} cam; + +layout(set = 5, binding = 0) uniform LabelData +{ + vec4 textSize; + vec4 color; + vec4 bboxCenter; + float radius; + float arrowLength; + bool hasRoundedCorners; + bool hasArrow; +} labelInfo; + +layout(location = 0) out vec4 color; +layout(location = 1) out vec2 textureCoordinates; + +// Background plane positions are in clipped space +const vec4 PLANE[4] = vec4[]( + vec4(-0.5, -0.5, 0, 1), vec4(0.5, -0.5, 0, 1), vec4(-0.5, 0.5, 0, 1), vec4(0.5, 0.5, 0, 1) + + ); +const vec2 TEX_COORDS[4] = vec2[]( + vec2(0, 0), vec2(1, 0), vec2(0, 1), vec2(1, 1) + ); + +void main() { + vec4 position = PLANE[gl_VertexIndex]; + vec2 bbox = labelInfo.textSize.xy; + position.xy *= bbox; + position.xy += vec2(labelInfo.bboxCenter); + position.z = -0.001; + gl_Position = cam.viewProjection * node.world * position; + textureCoordinates = TEX_COORDS[gl_VertexIndex] * bbox; +} diff --git a/openVulkanoCpp/Shader/labelBillboard.vert b/openVulkanoCpp/Shader/labelBillboard.vert new file mode 100644 index 0000000..00e1d02 --- /dev/null +++ b/openVulkanoCpp/Shader/labelBillboard.vert @@ -0,0 +1,87 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set = 0, binding = 0) uniform NodeData +{ + mat4 world; +} node; + +layout(set = 1, binding = 0) uniform CameraData +{ + mat4 viewProjection; + mat4 view; + mat4 projection; + vec4 camPos; + float nearPlane; + float farPlane; + float width; + float height; + float fov; + float aspect; + float scaleFactor; + float pixelScaleFactor; +} cam; + +layout(set = 4, binding = 0) uniform BillboardData +{ + vec2 size; + bool isFixedSize; +} billboardInfo; + +layout(set = 5, binding = 0) uniform LabelData +{ + vec4 textSize; + vec4 color; + vec4 bboxCenter; + float radius; + float arrowLength; + bool hasRoundedCorners; + bool hasArrow; +} labelInfo; + +layout(location = 0) out vec4 color; +layout(location = 1) out vec2 textureCoordinates; + +// Background plane positions are in clipped space +const vec4 PLANE[4] = vec4[]( + vec4(-0.5, -0.5, 0, 1), vec4(0.5, -0.5, 0, 1), vec4(-0.5, 0.5, 0, 1), vec4(0.5, 0.5, 0, 1) + + ); +const vec2 TEX_COORDS[4] = vec2[]( + vec2(0, 0), vec2(1, 0), vec2(0, 1), vec2(1, 1) + ); + +void main() { + vec4 position = PLANE[gl_VertexIndex]; + vec2 bbox = labelInfo.textSize.xy; + position.xy *= bbox; + position.xy += vec2(labelInfo.bboxCenter); + position.z = -0.001; + textureCoordinates = TEX_COORDS[gl_VertexIndex] * bbox; + + if (!billboardInfo.isFixedSize) + { + mat4 mv = cam.view * node.world; + + mv[0][0] = 1; + mv[0][1] = 0; + mv[0][2] = 0; + + mv[1][0] = 0; + mv[1][1] = 1; + mv[1][2] = 0; + + mv[2][0] = 0; + mv[2][1] = 0; + mv[2][2] = 1; + + gl_Position = cam.projection * mv * vec4(position.xyz, 1); + } + else + { + vec4 billboardPos = vec4(0.5, 0.5, 0.5, 1); + vec4 viewPos = cam.view * node.world * billboardPos; + float dist = -viewPos.z; + gl_Position = cam.projection * (viewPos + vec4(position.xy*dist*0.2,0,0)); + } +} diff --git a/openVulkanoCpp/Vulkan/Scene/LabelDrawableVulkanEncoder.cpp b/openVulkanoCpp/Vulkan/Scene/LabelDrawableVulkanEncoder.cpp index 0d7053e..e7ff24b 100644 --- a/openVulkanoCpp/Vulkan/Scene/LabelDrawableVulkanEncoder.cpp +++ b/openVulkanoCpp/Vulkan/Scene/LabelDrawableVulkanEncoder.cpp @@ -23,15 +23,12 @@ namespace OpenVulkano::Vulkan if (labelDrawable->IsBillboard()) { OpenVulkano::Scene::UniformBuffer* buffer = labelDrawable->GetBillboardBuffer(); - if (buffer) + VulkanUniformBuffer* vkBuffer = static_cast(buffer->renderBuffer); + if (!vkBuffer) { - VulkanUniformBuffer* vkBuffer = static_cast(buffer->renderBuffer); - if (!vkBuffer) - { - vkBuffer = drawContext->renderer->GetResourceManager().PrepareUniformBuffer(buffer); - } - vkBuffer->Record(drawContext); + vkBuffer = drawContext->renderer->GetResourceManager().PrepareUniformBuffer(buffer); } + vkBuffer->Record(drawContext); } OpenVulkano::Scene::UniformBuffer* labelBuffer = labelDrawable->GetLabelBuffer(); @@ -42,14 +39,6 @@ namespace OpenVulkano::Vulkan } 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]] @@ -57,8 +46,8 @@ namespace OpenVulkano::Vulkan continue; } drawContext->EncodeNode(node); - renderGeo->RecordDraw(drawContext->commandBuffer); } + drawContext->commandBuffer.draw(4, 1, 0, 0); } void EncodeTextDrawable(LabelDrawable* labelDrawable, Vulkan::VulkanDrawContext* drawContext)