diff --git a/res/shaders/TextPass.slang b/res/shaders/TextPass.slang index f7ec62c..a3144b0 100644 --- a/res/shaders/TextPass.slang +++ b/res/shaders/TextPass.slang @@ -4,6 +4,7 @@ struct GlyphInstanceData { float x; float y; + float z; float width; float height; uint glyphIndex; @@ -40,15 +41,15 @@ VertexOutput vertexMain(VertexInput input) float h = pText.glyphs[input.instanceId].height; const float4 coordinates[4] = { - float4(xpos, ypos, 0, 1), - float4(xpos, ypos + h, 0, 0), - float4(xpos + w, ypos, 1, 1), - float4(xpos + w, ypos + h, 1, 0), + float4(xpos, ypos, 0, 0), + float4(xpos, ypos + h, 0, 1), + float4(xpos + w, ypos, 1, 0), + float4(xpos + w, ypos + h, 1, 1), }; float4 vertex = coordinates[input.vertexId]; VertexOutput output; output.texCoords = vertex.zw; - output.position = mul(pViewParams.projectionMatrix, float4(vertex.xy, 0, 1)); + output.position = mul(pViewParams.projectionMatrix, float4(vertex.xy, pText.glyphs[input.instanceId].z-1, 1)); output.glyphIndex = pText.glyphs[input.instanceId].glyphIndex; return output; } @@ -60,5 +61,5 @@ float4 fragmentMain( uint glyphIndex : GLYPHINDEX ) : SV_Target { - return float4(1, 0, 0, pText.glyphTextures[glyphIndex].Sample(pText.glyphSampler, texCoords)); + return float4(0,0,0,pText.glyphTextures[glyphIndex].Sample(pText.glyphSampler, texCoords)); } \ No newline at end of file diff --git a/res/shaders/UIPass.slang b/res/shaders/UIPass.slang index e68b8ed..15ab839 100644 --- a/res/shaders/UIPass.slang +++ b/res/shaders/UIPass.slang @@ -2,44 +2,41 @@ import Common; struct RenderElementStyle { - float3 position; - uint backgroundImageIndex; - float3 backgroundColor; + float x; + float y; + float w; + float h; + float3 color; float opacity; - //float4 borderBottomColor; - //float4 borderLeftColor; - //float4 borderRightColor; - //float4 borderTopColor; - //float borderBottomLeftRadius; - //float borderBottomRightRadius; - //float borderTopLeftRadius; - //float borderTopRightRadius; - float2 dimensions; + float z; + uint textureIndex; + uint pad0; + uint pad1; }; struct UIParameter { - SamplerState backgroundSampler; - uint numBackgroundTextures; - Texture2D backgroundTextures[]; -} - + StructuredBuffer elements; + SamplerState sampler; + Texture2D textures[]; +}; ParameterBlock pParams; struct VertexOutput { float4 position : SV_Position; float2 texCoords : TEXCOORD; - RenderElementStyle style : RENDER_STYLE; + RenderElementStyle style : STYLE; }; [shader("vertex")] -VertexOutput vertexMain(uint vertexId : SV_VertexID, RenderElementStyle style) +VertexOutput vertexMain(uint vertexId : SV_VertexID, uint instanceId: SV_InstanceID) { - float xMin = style.position.x; - float xMax = xMin + style.dimensions.x; - float yMin = style.position.y; - float yMax = yMin + style.dimensions.y; + RenderElementStyle style = pParams.elements[instanceId]; + float xMin = style.x; + float xMax = xMin + style.w; + float yMin = style.y; + float yMax = yMin + style.h; float4 coordinates[] = { float4(xMin, yMin, 0, 0), float4(xMin, yMax, 0, 1), @@ -47,7 +44,7 @@ VertexOutput vertexMain(uint vertexId : SV_VertexID, RenderElementStyle style) float4(xMax, yMax, 1, 1) }; VertexOutput output; - output.position = mul(pViewParams.projectionMatrix, float4(coordinates[vertexId].xy, style.position.z, 1)); + output.position = mul(pViewParams.projectionMatrix, float4(coordinates[vertexId].xy, style.z-1, 1)); output.texCoords = coordinates[vertexId].zw; output.style = style; return output; @@ -55,12 +52,12 @@ VertexOutput vertexMain(uint vertexId : SV_VertexID, RenderElementStyle style) [shader("fragment")] float4 fragmentMain( - float4 position : SV_Position, float2 texCoords : TEXCOORD, - RenderElementStyle style : RENDER_STYLE + RenderElementStyle style : STYLE ) : SV_Target { - float4 bgTextureColor = float4(1, 1, 1, 1); - uint imageIndex = style.backgroundImageIndex; - return float4(0, 1, 0, 1); + if(style.textureIndex == -1) { + return float4(style.color, style.opacity); + } + return float4(pParams.textures[style.textureIndex].Sample(pParams.sampler, texCoords.xy).xyz, style.opacity); } \ No newline at end of file diff --git a/src/Editor/Asset/FontLoader.cpp b/src/Editor/Asset/FontLoader.cpp index 47fa480..dd13f04 100644 --- a/src/Editor/Asset/FontLoader.cpp +++ b/src/Editor/Asset/FontLoader.cpp @@ -39,7 +39,7 @@ void FontLoader::import(FontImportArgs args, PFontAsset asset) { assert(!error); FT_Set_Pixel_Sizes(face, 0, 48); for (uint32 c = 0; c < 256; ++c) { - if (FT_Load_Char(face, c, FT_LOAD_RENDER)) { + if (FT_Load_Char(face, c, FT_LOAD_RENDER | FT_LOAD_COLOR)) { std::cout << "error loading " << (char)c << std::endl; continue; } @@ -51,10 +51,22 @@ void FontLoader::import(FontImportArgs args, PFontAsset asset) { if (glyph.textureData.size() == 0) { glyph.size.x = 1; glyph.size.y = 1; - glyph.textureData.add(0); // load a single transparent pixel, so that we dont have to handle empty textures later + glyph.textureData.add(0); + // load a single transparent pixel, so that we dont have to handle empty textures later } else { std::memcpy(glyph.textureData.data(), face->glyph->bitmap.buffer, glyph.textureData.size()); } + glyph.texture = graphics->createTexture2D(TextureCreateInfo{ + .sourceData = + { + .size = glyph.textureData.size(), + .data = glyph.textureData.data(), + }, + .format = Gfx::SE_FORMAT_R8_UNORM, + .width = glyph.size.x, + .height = glyph.size.y, + .name = "FontGlyph", + }); } FT_Done_Face(face); FT_Done_FreeType(ft); diff --git a/src/Editor/Window/InspectorView.cpp b/src/Editor/Window/InspectorView.cpp index 29b84e7..e17c35f 100644 --- a/src/Editor/Window/InspectorView.cpp +++ b/src/Editor/Window/InspectorView.cpp @@ -3,13 +3,16 @@ #include "Asset/AssetRegistry.h" #include "Asset/FontLoader.h" #include "Graphics/Graphics.h" +#include "UI/Element/Div.h" #include "UI/Element/Text.h" using namespace Seele; using namespace Seele::Editor; InspectorView::InspectorView(Gfx::PGraphics graphics, PWindow window, const ViewportCreateInfo& createInfo) - : View(graphics, window, createInfo, "InspectorView"), uiSystem(new UI::System(new UI::Text("Test"))) { + : View(graphics, window, createInfo, "InspectorView"), + uiSystem(new UI::System(new UI::Div( + UI::Attributes{}, {new UI::Div({}, {new UI::Text("OtherTest")}), new UI::Text("Test")}))) { renderGraph.addPass(new UIPass(graphics, uiSystem)); renderGraph.setViewport(viewport); renderGraph.createRenderPass(); diff --git a/src/Engine/Containers/Array.h b/src/Engine/Containers/Array.h index 8699c58..5ac7983 100644 --- a/src/Engine/Containers/Array.h +++ b/src/Engine/Containers/Array.h @@ -6,7 +6,6 @@ #include #include - #ifndef DEFAULT_ALLOC_SIZE #define DEFAULT_ALLOC_SIZE 16 #endif @@ -111,11 +110,12 @@ template > s } } } + template >> constexpr Array(std::initializer_list init, const allocator_type& alloc = allocator_type()) : arraySize(init.size()), allocated(init.size()), allocator(alloc) { _data = allocateArray(init.size()); assert(_data != nullptr); - std::uninitialized_move(init.begin(), init.end(), begin()); + std::uninitialized_copy(init.begin(), init.end(), begin()); } constexpr Array(const Array& other) @@ -184,8 +184,7 @@ template > s } constexpr ~Array() { clear(); } - [[nodiscard]] - constexpr iterator find(const value_type& item) noexcept { + [[nodiscard]] constexpr iterator find(const value_type& item) noexcept { for (size_type i = 0; i < arraySize; ++i) { if (_data[i] == item) { return iterator(&_data[i]); @@ -193,8 +192,7 @@ template > s } return end(); } - [[nodiscard]] - constexpr iterator find(value_type&& item) noexcept { + [[nodiscard]] constexpr iterator find(value_type&& item) noexcept { for (size_type i = 0; i < arraySize; ++i) { if (_data[i] == item) { return iterator(&_data[i]); @@ -202,8 +200,7 @@ template > s } return end(); } - [[nodiscard]] - constexpr const_iterator find(const value_type& item) const noexcept { + [[nodiscard]] constexpr const_iterator find(const value_type& item) const noexcept { for (size_type i = 0; i < arraySize; ++i) { if (_data[i] == item) { return const_iterator(&_data[i]); @@ -211,8 +208,7 @@ template > s } return end(); } - [[nodiscard]] - constexpr const_iterator find(value_type&& item) const noexcept { + [[nodiscard]] constexpr const_iterator find(value_type&& item) const noexcept { for (size_type i = 0; i < arraySize; ++i) { if (_data[i] == item) { return const_iterator(&_data[i]); @@ -321,10 +317,7 @@ template > s constexpr size_type indexOf(T& t) { return indexOf(find(t)); } constexpr size_type indexOf(const T& t) const { return indexOf(find(t)); } constexpr size_type size() const noexcept { return arraySize; } - [[nodiscard]] - constexpr bool empty() const noexcept { - return arraySize == 0; - } + [[nodiscard]] constexpr bool empty() const noexcept { return arraySize == 0; } constexpr void reserve(size_type new_cap) { if (new_cap > allocated) { T* temp = allocateArray(new_cap); @@ -374,8 +367,7 @@ template > s return geometric; // geometric growth is sufficient } - [[nodiscard]] - T* allocateArray(size_type size) { + [[nodiscard]] T* allocateArray(size_type size) { T* result = allocator.allocate(size); assert(result != nullptr); return result; diff --git a/src/Engine/Graphics/RenderPass/RayTracingPass.cpp b/src/Engine/Graphics/RenderPass/RayTracingPass.cpp index d5d9712..3921074 100644 --- a/src/Engine/Graphics/RenderPass/RayTracingPass.cpp +++ b/src/Engine/Graphics/RenderPass/RayTracingPass.cpp @@ -63,13 +63,13 @@ RayTracingPass::RayTracingPass(Gfx::PGraphics graphics, PScene scene) : RenderPa skyBoxSampler = graphics->createSampler({}); } -static uint32 i = 0; +static uint32 pass = 0; static Component::Camera lastCam; void RayTracingPass::beginFrame(const Component::Camera& cam) { RenderPass::beginFrame(cam); if (lastCam.getCameraPosition() != cam.getCameraPosition() || lastCam.getCameraForward() != cam.getCameraForward()) { lastCam = cam; - i = 0; + pass = 0; } } @@ -156,7 +156,7 @@ void RayTracingPass::render() { }; // for (uint32 i = 0; i < sampleParams.samplesPerPixel; ++i) { - sampleParams.pass = i; + sampleParams.pass = pass; command->pushConstants(Gfx::SE_SHADER_STAGE_RAYGEN_BIT_KHR, 0, sizeof(SampleParams), &sampleParams); command->traceRays(texture->getWidth(), texture->getHeight(), 1); radianceAccumulator->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, @@ -164,8 +164,8 @@ void RayTracingPass::render() { texture->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR); } - i++; - std::cout << i << std::endl; + pass++; + std::cout << pass << std::endl; texture->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, Gfx::SE_ACCESS_TRANSFER_READ_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT); Array commands; diff --git a/src/Engine/Graphics/RenderPass/UIPass.cpp b/src/Engine/Graphics/RenderPass/UIPass.cpp index 32f3a9c..bde1100 100644 --- a/src/Engine/Graphics/RenderPass/UIPass.cpp +++ b/src/Engine/Graphics/RenderPass/UIPass.cpp @@ -8,8 +8,9 @@ using namespace Seele; -UIPass::UIPass(Gfx::PGraphics graphics, UI::PSystem system) : RenderPass(graphics) { +UIPass::UIPass(Gfx::PGraphics graphics, UI::PSystem system) : RenderPass(graphics), system(system) { glyphInstanceBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{.name = "GlyphInstanceBuffer"}); + elementBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{.name = "RenderStyleElements"}); textDescriptorLayout = graphics->createDescriptorLayout("pText"); textDescriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ .binding = 0, @@ -28,15 +29,24 @@ UIPass::UIPass(Gfx::PGraphics graphics, UI::PSystem system) : RenderPass(graphic textPipelineLayout->addDescriptorLayout(viewParamsLayout); textPipelineLayout->addDescriptorLayout(textDescriptorLayout); + uiDescriptorLayout = graphics->createDescriptorLayout("pParams"); + uiDescriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .binding = 0, + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + uiDescriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .binding = 1, + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_SAMPLER, + }); + uiDescriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .binding = 2, + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .descriptorCount = 1024, + }); uiPipelineLayout = graphics->createPipelineLayout("UIPipeline"); uiPipelineLayout->addDescriptorLayout(viewParamsLayout); + uiPipelineLayout->addDescriptorLayout(uiDescriptorLayout); // todo: - texts.add(TextRender{ - .text = "TestText123", - .font = AssetRegistry::findFont("", "arial"), - .fontSize = 12, - .position = Vector2(0, 100), - }); } UIPass::~UIPass() {} @@ -45,35 +55,49 @@ void UIPass::beginFrame(const Component::Camera& cam) { RenderPass::beginFrame(cam); glyphs.clear(); usedTextures.clear(); - for (TextRender& render : texts) { - TextResources& res = textResources[render.font].add(); - float x = render.position.x * viewport->getContentScaleX(); - float y = (viewport->getHeight() - render.position.y) * viewport->getContentScaleY(); - for (uint32 c : render.text) { - const FontAsset::Glyph& glyph = render.font->getGlyphData(c); - Vector2 bearing = Vector2(glyph.bearing) * viewport->getContentScaleX(); - Vector2 size = Vector2(glyph.size) * viewport->getContentScaleY(); - float xpos = x + glyph.bearing.x; - float ypos = y + (size.y - bearing.y); - - float w = size.x; - float h = size.y; - - glyphs.add(GlyphInstanceData{ - .x = xpos, - .y = ypos, - .width = w, - .height = h, - .glyphIndex = (uint32)usedTextures.size(), + for (const auto& render : renderElements) { + float x = render.position.x; + float y = render.position.y; + if (render.text.empty()) { + //todo: convert using actual tree depth + elements.add(RenderElementStyle{ + .x = x, + .y = y, + .w = render.dimensions.x, + .h = render.dimensions.y, + .color = render.backgroundColor, + .z = render.level / 100.f, }); - usedTextures.add(glyph.texture); - x += glyph.advance >> 6; + } else { + y = y + render.fontSize; + for (uint32 c : render.text) { + const FontAsset::Glyph& glyph = render.font->getGlyphData(c); + Vector2 bearing = Vector2(glyph.bearing); + Vector2 size = Vector2(glyph.size); + float xpos = x + glyph.bearing.x; + float ypos = y - bearing.y; + + float w = size.x; + float h = size.y; + + glyphs.add(GlyphInstanceData{ + .x = xpos, + .y = ypos, + .z = render.level / 100.0f, + .width = w, + .height = h, + .glyphIndex = (uint32)usedTextures.size(), + }); + usedTextures.add(glyph.texture); + x += glyph.advance / 64.0f; + } } } glyphInstanceBuffer->rotateBuffer(sizeof(GlyphInstanceData) * glyphs.size()); glyphInstanceBuffer->updateContents(0, sizeof(GlyphInstanceData) * glyphs.size(), glyphs.data()); glyphInstanceBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_VERTEX_SHADER_BIT); + textDescriptorLayout->reset(); textDescriptorSet = textDescriptorLayout->allocateDescriptorSet(); textDescriptorSet->updateBuffer(0, 0, glyphInstanceBuffer); @@ -82,6 +106,16 @@ void UIPass::beginFrame(const Component::Camera& cam) { textDescriptorSet->updateTexture(2, i, usedTextures[i]); } textDescriptorSet->writeChanges(); + + elementBuffer->rotateBuffer(sizeof(RenderElementStyle) * elements.size()); + elementBuffer->updateContents(0, sizeof(RenderElementStyle) * elements.size(), elements.data()); + elementBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_VERTEX_SHADER_BIT); + uiDescriptorLayout->reset(); + uiDescriptorSet = uiDescriptorLayout->allocateDescriptorSet(); + uiDescriptorSet->updateBuffer(0, 0, elementBuffer); + uiDescriptorSet->updateSampler(1, 0, glyphSampler); + uiDescriptorSet->writeChanges(); } void UIPass::render() { @@ -89,6 +123,9 @@ void UIPass::render() { Array commands; Gfx::ORenderCommand command = graphics->createRenderCommand("TextPassCommand"); command->setViewport(viewport); + command->bindPipeline(uiPipeline); + command->bindDescriptor({viewParamsSet, uiDescriptorSet}); + command->draw(4, elements.size(), 0, 0); command->bindPipeline(textPipeline); command->bindDescriptor({viewParamsSet, textDescriptorSet}); command->draw(4, glyphs.size(), 0, 0); @@ -142,7 +179,7 @@ void UIPass::createRenderPass() { .addressModeV = Gfx::SE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, }); - Gfx::LegacyPipelineCreateInfo pipelineInfo = { + textPipeline = graphics->createGraphicsPipeline(Gfx::LegacyPipelineCreateInfo{ .topology = Gfx::SE_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, .vertexShader = textVertexShader, .fragmentShader = textFragmentShader, @@ -167,15 +204,45 @@ void UIPass::createRenderPass() { Gfx::SE_COLOR_COMPONENT_A_BIT, }}, }, - }; + }); - textPipeline = graphics->createGraphicsPipeline(std::move(pipelineInfo)); graphics->beginShaderCompilation(ShaderCompilationInfo{ .name = "UIVertex", .modules = {"UIPass"}, .entryPoints = {{"vertexMain", "UIPass"}, {"fragmentMain", "UIPass"}}, .rootSignature = uiPipelineLayout, }); + uiPipelineLayout->create(); uiVertexShader = graphics->createVertexShader({0}); uiFragmentShader = graphics->createFragmentShader({1}); + + uiPipeline = graphics->createGraphicsPipeline(Gfx::LegacyPipelineCreateInfo{ + .topology = Gfx::SE_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + .vertexShader = uiVertexShader, + .fragmentShader = uiFragmentShader, + .renderPass = renderPass, + .pipelineLayout = uiPipelineLayout, + .rasterizationState = + { + .cullMode = Gfx::SE_CULL_MODE_NONE, + }, + .colorBlend = + { + + .attachmentCount = 1, + .blendAttachments = {{ + .blendEnable = true, + .srcColorBlendFactor = Gfx::SE_BLEND_FACTOR_SRC_ALPHA, + .dstColorBlendFactor = Gfx::SE_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + .srcAlphaBlendFactor = Gfx::SE_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + .dstAlphaBlendFactor = Gfx::SE_BLEND_FACTOR_ZERO, + .alphaBlendOp = Gfx::SE_BLEND_OP_ADD, + .colorWriteMask = Gfx::SE_COLOR_COMPONENT_R_BIT | Gfx::SE_COLOR_COMPONENT_G_BIT | Gfx::SE_COLOR_COMPONENT_B_BIT | + Gfx::SE_COLOR_COMPONENT_A_BIT, + }}, + }, + }); + system->style(); + system->layout(UVector2(viewport->getWidth(), viewport->getHeight())); + renderElements = system->render(); } diff --git a/src/Engine/Graphics/RenderPass/UIPass.h b/src/Engine/Graphics/RenderPass/UIPass.h index 1c9ebce..1836640 100644 --- a/src/Engine/Graphics/RenderPass/UIPass.h +++ b/src/Engine/Graphics/RenderPass/UIPass.h @@ -7,13 +7,6 @@ namespace Seele { DECLARE_NAME_REF(Gfx, Texture2D) DECLARE_NAME_REF(Gfx, RenderTargetAttachment) -struct TextRender { - std::string text; - PFontAsset font; - uint32 fontSize; - Vector4 textColor; - Vector2 position; -}; class UIPass : public RenderPass { public: UIPass(Gfx::PGraphics graphics, UI::PSystem system); @@ -35,21 +28,24 @@ class UIPass : public RenderPass { struct GlyphInstanceData { float x; float y; + float z; float width; float height; uint32 glyphIndex; }; - struct TextData { - Vector4 textColor; - float scale; - }; - struct TextResources { - Gfx::PShaderBuffer instanceBuffer; - Gfx::PDescriptorSet textureArraySet; - TextData textData; + struct RenderElementStyle { + float x; + float y; + float w; + float h; + Vector color; + float opacity = 1; + float z = 0; + uint32 textureIndex = -1ul; + uint32 pad0; + uint32 pad1; }; - Map> textResources; Gfx::RenderTargetAttachment colorAttachment; Gfx::RenderTargetAttachment depthAttachment; @@ -71,9 +67,12 @@ class UIPass : public RenderPass { Gfx::OPipelineLayout uiPipelineLayout; Gfx::PGraphicsPipeline uiPipeline; - Array texts; + UI::PSystem system; + Array renderElements; Array glyphs; + Array elements; Gfx::OShaderBuffer glyphInstanceBuffer; + Gfx::OShaderBuffer elementBuffer; Gfx::OSampler glyphSampler; Array usedTextures; }; diff --git a/src/Engine/Graphics/Vulkan/Buffer.h b/src/Engine/Graphics/Vulkan/Buffer.h index e43fdb7..2cc058c 100644 --- a/src/Engine/Graphics/Vulkan/Buffer.h +++ b/src/Engine/Graphics/Vulkan/Buffer.h @@ -39,7 +39,11 @@ class Buffer { virtual ~Buffer(); VkBuffer getHandle() const { return buffers[currentBuffer]->buffer; } VkDeviceAddress getDeviceAddress() const { return buffers[currentBuffer]->deviceAddress; } - PBufferAllocation getAlloc() const { return buffers[currentBuffer]; } + PBufferAllocation getAlloc() const { + if (buffers.empty()) + return nullptr; + return buffers[currentBuffer]; + } uint64 getSize() const { return buffers[currentBuffer]->size; } void updateContents(uint64 regionOffset, uint64 regionSize, void* ptr); void readContents(uint64 regionOffset, uint64 regionSize, void* ptr); diff --git a/src/Engine/Graphics/Vulkan/Descriptor.cpp b/src/Engine/Graphics/Vulkan/Descriptor.cpp index 3664235..da77047 100644 --- a/src/Engine/Graphics/Vulkan/Descriptor.cpp +++ b/src/Engine/Graphics/Vulkan/Descriptor.cpp @@ -171,7 +171,7 @@ DescriptorSet::~DescriptorSet() {} void DescriptorSet::updateBuffer(uint32_t binding, uint32 index, Gfx::PUniformBuffer uniformBuffer) { PUniformBuffer vulkanBuffer = uniformBuffer.cast(); - if (boundResources[binding][index] == vulkanBuffer->getAlloc()) { + if (boundResources[binding][index] == vulkanBuffer->getAlloc() || vulkanBuffer->getAlloc() == nullptr) { return; } @@ -197,7 +197,7 @@ void DescriptorSet::updateBuffer(uint32_t binding, uint32 index, Gfx::PUniformBu void DescriptorSet::updateBuffer(uint32_t binding, uint32 index, Gfx::PShaderBuffer shaderBuffer) { PShaderBuffer vulkanBuffer = shaderBuffer.cast(); - if (boundResources[binding][index] == vulkanBuffer->getAlloc()) { + if (boundResources[binding][index] == vulkanBuffer->getAlloc() || vulkanBuffer->getAlloc() == nullptr) { return; } @@ -222,7 +222,7 @@ void DescriptorSet::updateBuffer(uint32_t binding, uint32 index, Gfx::PShaderBuf void DescriptorSet::updateBuffer(uint32_t binding, uint32 index, Gfx::PVertexBuffer indexBuffer) { PVertexBuffer vulkanBuffer = indexBuffer.cast(); - if (boundResources[binding][index] == vulkanBuffer->getAlloc()) { + if (boundResources[binding][index] == vulkanBuffer->getAlloc() || vulkanBuffer->getAlloc() == nullptr) { return; } @@ -247,7 +247,7 @@ void DescriptorSet::updateBuffer(uint32_t binding, uint32 index, Gfx::PVertexBuf void DescriptorSet::updateBuffer(uint32_t binding, uint32 index, Gfx::PIndexBuffer indexBuffer) { PIndexBuffer vulkanBuffer = indexBuffer.cast(); - if (boundResources[binding][index] == vulkanBuffer->getAlloc()) { + if (boundResources[binding][index] == vulkanBuffer->getAlloc() || vulkanBuffer->getAlloc() == nullptr) { return; } diff --git a/src/Engine/Graphics/Window.cpp b/src/Engine/Graphics/Window.cpp index 5b1b939..960e4db 100644 --- a/src/Engine/Graphics/Window.cpp +++ b/src/Engine/Graphics/Window.cpp @@ -30,6 +30,6 @@ Matrix4 Viewport::getProjectionMatrix() const { //); return glm::perspective(fieldOfView, sizeX / static_cast(sizeY), 0.1f, 1000.0f); } else { - return glm::ortho(0.0f, (float)sizeX, 0.0f, (float)sizeY); + return glm::ortho(0.0f, (float)sizeX, (float)sizeY, 0.0f); } } \ No newline at end of file diff --git a/src/Engine/UI/Element.cpp b/src/Engine/UI/Element.cpp index 5c0d8fc..c07c911 100644 --- a/src/Engine/UI/Element.cpp +++ b/src/Engine/UI/Element.cpp @@ -4,16 +4,13 @@ using namespace Seele; using namespace Seele::UI; -Array Element::render() { - RenderElement result; - result.position = UVector2(0, 0); - result.size = UVector2(style.width, style.height); - result.backgroundColor = style.backgroundColor; - result.fontSize = style.fontSize; - result.fontFamily = style.fontFamily; - return {result}; +Element::Element(Attributes attr, Array _children) : attr(attr) { + for (auto c : _children) { + children.add(c); + } + for (auto& child : children) { + child->setParent(this); + } } -Element::Element(Attributes attr, Array children) : attr(attr), children(std::move(children)) {} - -Element::~Element() {} \ No newline at end of file +Element::~Element() {} diff --git a/src/Engine/UI/Element.h b/src/Engine/UI/Element.h index cb6257e..3f8a850 100644 --- a/src/Engine/UI/Element.h +++ b/src/Engine/UI/Element.h @@ -6,18 +6,138 @@ namespace Seele { namespace UI { -struct RenderElement; +struct UIRender { + std::string text; + PFontAsset font; + uint32 fontSize; + Vector textColor; + Vector backgroundColor; + Vector2 position; + Vector2 dimensions; + uint32 z; + uint32 level; +}; DECLARE_REF(Element) class Element { public: - Element(Attributes attr, Array children); + Element(Attributes attr, Array children); virtual ~Element(); - virtual void calcStyle(Style parentStyle) = 0; - Array render(); + void setParent(PElement p) { parent = p; } + // calculates the final style object taking into account inherited properties + virtual void applyStyle(Style parentStyle) = 0; + void calcStyle(Style parentStyle) { + applyStyle(parentStyle); + for (auto& child : children) { + child->calcStyle(style); + } + } + // calculates the relative positions of the child elements for the applied layout style + virtual void layout(UVector2 parentSize) { + if (style.maxWidthType == DimensionType::Auto) { + maxDimensions.x = parentSize.x; + } + if (style.maxHeightType == DimensionType::Auto) { + maxDimensions.y = parentSize.y; + } + if (style.widthType == DimensionType::Pixel) { + dimensions.x = style.width; + } else if (style.widthType == DimensionType::Percent) { + dimensions.x = parentSize.x * style.width / 100.0f; + } + + if (style.heightType == DimensionType::Pixel) { + dimensions.y = style.height; + } else if (style.heightType == DimensionType::Percent) { + dimensions.y = parentSize.y * style.height / 100.0f; + } + for (auto& child : children) { + child->layout(maxDimensions); + } + switch (style.innerDisplay) { + case InnerDisplayType::Flow: + flowLayout(); + break; + } + } + // normal flow of the elements, inline elements go left to right, blocks get their own new lines + void flowLayout() { + // use a cursor going from left to right, top to bottom, starting at padding + Vector2 cursor = Vector2(style.paddingLeft, style.paddingTop); + uint32 lineHeight = 0; + for (auto& child : children) { + // inline elements flow left to right + if (child->style.outerDisplay == OuterDisplayType::Inline) { + // only static and relative elements actually reserve + if (child->style.position == PositionType::Static || child->style.position == PositionType::Relative) { + // create a new line in case the element doesnt fit on the current one anymore, + // but keep in current line in case we are still at the start, as a new line wouldnt help here + if (cursor.x + child->dimensions.x > dimensions.x - child->style.left && cursor.x != 0) { + cursor.x = 0; + cursor.y += lineHeight; + } + // todo: border + child->position.x = cursor.x + child->style.marginLeft; + child->position.y = cursor.y + child->style.marginTop; + cursor.x = child->position.x + child->dimensions.x; + cursor.y = child->position.y + child->dimensions.y; + // apply offsets after updating the cursor for relative elements + if (child->style.position == PositionType::Relative) { + child->position.x += child->style.left; + child->position.y += child->style.top; + } + lineHeight = std::max(lineHeight, child->style.lineHeight); + } + // block elements always get their own lines, like paragraphs + } else if (child->style.outerDisplay == OuterDisplayType::Block) { + // static and relative require space to be created for the elements + if (child->style.position == PositionType::Static || child->style.position == PositionType::Relative) { + // create a new line in case we are not already at one + if (cursor.x != 0) { + cursor.x = 0; + cursor.y += lineHeight; + } + child->position.x = cursor.x + child->style.marginLeft; + child->position.y = cursor.y + child->style.marginTop; + // new line after block + cursor.x = 0; + cursor.y = child->position.y + child->dimensions.y; + } + } + } + if (style.widthType == DimensionType::Auto) { + dimensions.x = cursor.x + style.paddingRight; + } + if (style.heightType == DimensionType::Auto) { + dimensions.y = cursor.y + style.paddingBottom; + } + } + virtual Array render(Vector2 anchor, uint32 level) { + Array result = { + UIRender{ + .backgroundColor = style.backgroundColor, + .position = position + anchor, + .dimensions = dimensions, + .z = style.z, + .level = level, + }, + }; + for (auto& child : children) { + for (const auto& render : child->render(position + anchor, level+1)) { + result.add(render); + } + } + return result; + } + Style style; + Vector2 maxDimensions = Vector2(0); + // calculated position relative to parent + Vector2 position = Vector2(0); + // calculated dimensions in pixels after layouting + Vector2 dimensions = Vector2(0); protected: - Style style; Attributes attr; + PElement parent = nullptr; Array children; }; DEFINE_REF(Element) diff --git a/src/Engine/UI/Element/Div.h b/src/Engine/UI/Element/Div.h index ea51379..995ab2a 100644 --- a/src/Engine/UI/Element/Div.h +++ b/src/Engine/UI/Element/Div.h @@ -7,8 +7,15 @@ namespace UI { template class Div : public Element { public: - Div(Attributes attr, std::initializer_list children) : Element(attr, children) { style.displayType = DisplayType::Block; } + Div(Attributes attr, Array children) : Element(attr, std::move(children)) { } virtual ~Div() {} + virtual void applyStyle(Style parentStyle) override { + style = parentStyle; + style.outerDisplay = OuterDisplayType::Block; + style.widthType = DimensionType::Percent; + style.width = 100; + (classes::apply(style), ...); + } private: }; } // namespace UI diff --git a/src/Engine/UI/Element/Text.h b/src/Engine/UI/Element/Text.h index f3292c9..abefe4b 100644 --- a/src/Engine/UI/Element/Text.h +++ b/src/Engine/UI/Element/Text.h @@ -4,10 +4,36 @@ namespace Seele { namespace UI { -class Text : public Element { +template class Text : public Element { public: Text(std::string text) : Element({}, {}), text(text) {} - virtual void calcStyle(Style parentStyle) override { style = parentStyle; } + virtual void applyStyle(Style parentStyle) { + style = parentStyle; + style.outerDisplay = OuterDisplayType::Inline; + (classes::apply(style), ...); + } + virtual void layout(UVector2 parentSize) override { + size_t cursor = 0; + for (uint32 i = 0; i < text.size() - 1; ++i) { + dimensions.x += style.fontFamily->getGlyphData(text[i]).advance / 64.0f; + } + dimensions.x += style.fontFamily->getGlyphData(text[text.size() - 1]).size.x; + dimensions.y = style.fontSize; + } + virtual Array render(Vector2 anchor, uint32 level) override { + return { + UIRender{ + .text = text, + .font = style.fontFamily, + .fontSize = style.fontSize, + .position = position + anchor, + .dimensions = dimensions, + .z = style.z, + .level = level, + }, + }; + } + // height = fontsize * 1.12 private: std::string text; diff --git a/src/Engine/UI/Style/Class.h b/src/Engine/UI/Style/Class.h index 40e0fd4..eae7198 100644 --- a/src/Engine/UI/Style/Class.h +++ b/src/Engine/UI/Style/Class.h @@ -1,27 +1,45 @@ #pragma once #include "MinimalEngine.h" #include "Style.h" +#include "Asset/AssetRegistry.h" namespace Seele { -namespace UI { template -concept StyleClass = requires(C c, Style& s) { c.apply(s); }; +concept StyleClass = requires(C c, UI::Style& s) { c.apply(s); }; -template struct WidthClass { - static void apply(Style& s) { +template struct WidthClass { + static void apply(UI::Style& s) { s.width = w; s.widthType = widthType; } }; -using W_Full = WidthClass<100, DimensionType::Percent>; -using W_1 = WidthClass<2, DimensionType::Pixel>; +using W_Full = WidthClass<100, UI::DimensionType::Percent>; +using W_1 = WidthClass<2, UI::DimensionType::Pixel>; -template struct DisplayClass { - static void apply(Style& s) { s.displayType = displayType; } +template struct DisplayClass { + static void apply(UI::Style& s) { + s.outerDisplay = outer; + s.innerDisplay = inner; + } }; -using Hidden = DisplayClass; -using Block = DisplayClass; -using Flex = DisplayClass; +using Hidden = DisplayClass; +using Block = DisplayClass; +using Inline = DisplayClass; -} // namespace UI +template struct StringLiteral { + constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); } + + char value[N]; +}; +template struct FontClass { + static void apply(UI::Style& s) { s.fontFamily = AssetRegistry::findFont("", lit.value); } +}; + +using Font_Arial = FontClass<"arial">; + +template struct BackgroundColorClass { + static void apply(UI::Style& s) { s.backgroundColor = backgroundColor; } +}; + +using BG_Red = BackgroundColorClass; } // namespace Seele \ No newline at end of file diff --git a/src/Engine/UI/Style/Style.h b/src/Engine/UI/Style/Style.h index b30779e..c5b5e26 100644 --- a/src/Engine/UI/Style/Style.h +++ b/src/Engine/UI/Style/Style.h @@ -8,32 +8,41 @@ namespace UI { enum class DimensionType { Pixel, Percent, // EM, PEM, etc... + Auto, }; enum class PositionType { - Relative, Static, + Relative, Absolute, Sticky, }; -enum class DisplayType { +enum class OuterDisplayType { Inline, - Hidden, Block, + Hidden, +}; +enum class InnerDisplayType { + Flow, Flex, Grid, }; struct Style { - DimensionType widthType = DimensionType::Pixel; - uint32 width = 0; - DimensionType heightType = DimensionType::Pixel; - uint32 height = 0; + DimensionType widthType = DimensionType::Auto; + float width = 0; + DimensionType maxWidthType = DimensionType::Auto; + float maxWidth = 0; + DimensionType heightType = DimensionType::Auto; + float height = 0; + DimensionType maxHeightType = DimensionType::Auto; + float maxHeight = 0; uint32 z = 0; Vector backgroundColor = Vector(1, 1, 1); - DisplayType displayType = DisplayType::Inline; + OuterDisplayType outerDisplay = OuterDisplayType::Inline; + InnerDisplayType innerDisplay = InnerDisplayType::Flow; PositionType position = PositionType::Relative; PFontAsset fontFamily; - uint32 lineHeight = 12; - uint32 fontSize = 12; + uint32 lineHeight = 48; + uint32 fontSize = 48; uint32 fontWeight = 0; uint32 paddingTop = 0; uint32 paddingBottom = 0; @@ -44,6 +53,10 @@ struct Style { uint32 marginLeft = 0; uint32 marginRight = 0; uint32 gap = 0; + uint32 top = 0; + uint32 bottom = 0; + uint32 left = 0; + uint32 right = 0; }; } // namespace UI } // namespace Seele \ No newline at end of file diff --git a/src/Engine/UI/System.h b/src/Engine/UI/System.h index 8b57e7b..031b18c 100644 --- a/src/Engine/UI/System.h +++ b/src/Engine/UI/System.h @@ -3,26 +3,13 @@ namespace Seele { namespace UI { -struct RenderElement { - // position relative to target viewport in pixels - UVector2 position; - // size relative to target viewport in pixels - UVector2 size; - // background color of area - Vector backgroundColor; - // text to render - std::string text; - // font size in pixels - uint32 fontSize; - // font family - PFontAsset fontFamily; -}; class System { public: System(OElement rootElement) : rootElement(std::move(rootElement)) {} - ~System(); - Array render(UVector2 viewport) { - } + ~System() {} + void style() { rootElement->calcStyle(UI::Style()); } + void layout(UVector2 viewport) { rootElement->layout(viewport); } + Array render() { return rootElement->render(Vector2(0, 0), 1); } private: OElement rootElement;