basic flow layout
This commit is contained in:
@@ -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));
|
||||
}
|
||||
+26
-29
@@ -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<float4> backgroundTextures[];
|
||||
}
|
||||
|
||||
StructuredBuffer<RenderElementStyle> elements;
|
||||
SamplerState sampler;
|
||||
Texture2D textures[];
|
||||
};
|
||||
ParameterBlock<UIParameter> 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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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<W_Full, BG_Red>({}, {new UI::Text<Font_Arial>("OtherTest")}), new UI::Text<Font_Arial>("Test")}))) {
|
||||
renderGraph.addPass(new UIPass(graphics, uiSystem));
|
||||
renderGraph.setViewport(viewport);
|
||||
renderGraph.createRenderPass();
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <iterator>
|
||||
#include <memory_resource>
|
||||
|
||||
|
||||
#ifndef DEFAULT_ALLOC_SIZE
|
||||
#define DEFAULT_ALLOC_SIZE 16
|
||||
#endif
|
||||
@@ -111,11 +110,12 @@ template <typename T, typename Allocator = std::pmr::polymorphic_allocator<T>> s
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename = std::enable_if_t<std::is_copy_constructible_v<T>>>
|
||||
constexpr Array(std::initializer_list<T> 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 <typename T, typename Allocator = std::pmr::polymorphic_allocator<T>> 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 <typename T, typename Allocator = std::pmr::polymorphic_allocator<T>> 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 <typename T, typename Allocator = std::pmr::polymorphic_allocator<T>> 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 <typename T, typename Allocator = std::pmr::polymorphic_allocator<T>> 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 <typename T, typename Allocator = std::pmr::polymorphic_allocator<T>> 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 <typename T, typename Allocator = std::pmr::polymorphic_allocator<T>> 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;
|
||||
|
||||
@@ -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<Gfx::ORenderCommand> commands;
|
||||
|
||||
@@ -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<Gfx::ORenderCommand> 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();
|
||||
}
|
||||
|
||||
@@ -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<PFontAsset, Array<TextResources>> textResources;
|
||||
|
||||
Gfx::RenderTargetAttachment colorAttachment;
|
||||
Gfx::RenderTargetAttachment depthAttachment;
|
||||
@@ -71,9 +67,12 @@ class UIPass : public RenderPass {
|
||||
Gfx::OPipelineLayout uiPipelineLayout;
|
||||
Gfx::PGraphicsPipeline uiPipeline;
|
||||
|
||||
Array<TextRender> texts;
|
||||
UI::PSystem system;
|
||||
Array<UI::UIRender> renderElements;
|
||||
Array<GlyphInstanceData> glyphs;
|
||||
Array<RenderElementStyle> elements;
|
||||
Gfx::OShaderBuffer glyphInstanceBuffer;
|
||||
Gfx::OShaderBuffer elementBuffer;
|
||||
Gfx::OSampler glyphSampler;
|
||||
Array<Gfx::PTexture2D> usedTextures;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -171,7 +171,7 @@ DescriptorSet::~DescriptorSet() {}
|
||||
|
||||
void DescriptorSet::updateBuffer(uint32_t binding, uint32 index, Gfx::PUniformBuffer uniformBuffer) {
|
||||
PUniformBuffer vulkanBuffer = uniformBuffer.cast<UniformBuffer>();
|
||||
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<ShaderBuffer>();
|
||||
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<VertexBuffer>();
|
||||
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<IndexBuffer>();
|
||||
if (boundResources[binding][index] == vulkanBuffer->getAlloc()) {
|
||||
if (boundResources[binding][index] == vulkanBuffer->getAlloc() || vulkanBuffer->getAlloc() == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,6 @@ Matrix4 Viewport::getProjectionMatrix() const {
|
||||
//);
|
||||
return glm::perspective(fieldOfView, sizeX / static_cast<float>(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);
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,13 @@
|
||||
using namespace Seele;
|
||||
using namespace Seele::UI;
|
||||
|
||||
Array<RenderElement> 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<Element*> _children) : attr(attr) {
|
||||
for (auto c : _children) {
|
||||
children.add(c);
|
||||
}
|
||||
for (auto& child : children) {
|
||||
child->setParent(this);
|
||||
}
|
||||
}
|
||||
|
||||
Element::Element(Attributes attr, Array<OElement> children) : attr(attr), children(std::move(children)) {}
|
||||
|
||||
Element::~Element() {}
|
||||
+125
-5
@@ -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<OElement> children);
|
||||
Element(Attributes attr, Array<Element*> children);
|
||||
virtual ~Element();
|
||||
virtual void calcStyle(Style parentStyle) = 0;
|
||||
Array<RenderElement> 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<UIRender> render(Vector2 anchor, uint32 level) {
|
||||
Array<UIRender> 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<OElement> children;
|
||||
};
|
||||
DEFINE_REF(Element)
|
||||
|
||||
@@ -7,8 +7,15 @@ namespace UI {
|
||||
template<StyleClass... classes>
|
||||
class Div : public Element {
|
||||
public:
|
||||
Div(Attributes attr, std::initializer_list<OElement> children) : Element(attr, children) { style.displayType = DisplayType::Block; }
|
||||
Div(Attributes attr, Array<Element*> 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
|
||||
|
||||
@@ -4,10 +4,36 @@
|
||||
|
||||
namespace Seele {
|
||||
namespace UI {
|
||||
class Text : public Element {
|
||||
template <StyleClass... classes> 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<UIRender> 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;
|
||||
|
||||
+30
-12
@@ -1,27 +1,45 @@
|
||||
#pragma once
|
||||
#include "MinimalEngine.h"
|
||||
#include "Style.h"
|
||||
#include "Asset/AssetRegistry.h"
|
||||
|
||||
namespace Seele {
|
||||
namespace UI {
|
||||
template <typename C>
|
||||
concept StyleClass = requires(C c, Style& s) { c.apply(s); };
|
||||
concept StyleClass = requires(C c, UI::Style& s) { c.apply(s); };
|
||||
|
||||
template <uint32 w, DimensionType widthType> struct WidthClass {
|
||||
static void apply(Style& s) {
|
||||
template <uint32 w, UI::DimensionType widthType> 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 <DisplayType displayType> struct DisplayClass {
|
||||
static void apply(Style& s) { s.displayType = displayType; }
|
||||
template <UI::OuterDisplayType outer, UI::InnerDisplayType inner> struct DisplayClass {
|
||||
static void apply(UI::Style& s) {
|
||||
s.outerDisplay = outer;
|
||||
s.innerDisplay = inner;
|
||||
}
|
||||
};
|
||||
using Hidden = DisplayClass<DisplayType::Hidden>;
|
||||
using Block = DisplayClass<DisplayType::Block>;
|
||||
using Flex = DisplayClass<DisplayType::Flex>;
|
||||
using Hidden = DisplayClass<UI::OuterDisplayType::Hidden, UI::InnerDisplayType::Flow>;
|
||||
using Block = DisplayClass<UI::OuterDisplayType::Block, UI::InnerDisplayType::Flow>;
|
||||
using Inline = DisplayClass<UI::OuterDisplayType::Inline, UI::InnerDisplayType::Flow>;
|
||||
|
||||
} // namespace UI
|
||||
template <size_t N> struct StringLiteral {
|
||||
constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); }
|
||||
|
||||
char value[N];
|
||||
};
|
||||
template <StringLiteral lit> struct FontClass {
|
||||
static void apply(UI::Style& s) { s.fontFamily = AssetRegistry::findFont("", lit.value); }
|
||||
};
|
||||
|
||||
using Font_Arial = FontClass<"arial">;
|
||||
|
||||
template <Vector backgroundColor> struct BackgroundColorClass {
|
||||
static void apply(UI::Style& s) { s.backgroundColor = backgroundColor; }
|
||||
};
|
||||
|
||||
using BG_Red = BackgroundColorClass<Vector(1, 0, 0)>;
|
||||
} // namespace Seele
|
||||
+23
-10
@@ -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
|
||||
+4
-17
@@ -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<RenderElement> render(UVector2 viewport) {
|
||||
}
|
||||
~System() {}
|
||||
void style() { rootElement->calcStyle(UI::Style()); }
|
||||
void layout(UVector2 viewport) { rootElement->layout(viewport); }
|
||||
Array<UIRender> render() { return rootElement->render(Vector2(0, 0), 1); }
|
||||
|
||||
private:
|
||||
OElement rootElement;
|
||||
|
||||
Reference in New Issue
Block a user