various UI and rt changes

This commit is contained in:
Dynamitos
2025-01-29 16:15:48 +01:00
parent 4e6eb02e74
commit e5ac354527
29 changed files with 314 additions and 223 deletions
+53 -16
View File
@@ -12,8 +12,11 @@ FontAsset::FontAsset(std::string_view folderPath, std::string_view name) : Asset
}
FontAsset::~FontAsset() {
FT_Done_Face(face);
FT_Done_Face(ft_face);
FT_Done_FreeType(ft);
hb_font_destroy(hb_font);
hb_face_destroy(hb_face);
hb_blob_destroy(blob);
}
void FontAsset::save(ArchiveBuffer& buffer) const { Serialization::save(buffer, ttfFile); }
@@ -25,23 +28,57 @@ void FontAsset::load(ArchiveBuffer& buffer) {
}
void FontAsset::loadFace() {
FT_Error error = FT_New_Memory_Face(ft, ttfFile.data(), ttfFile.size(), 0, &face);
FT_Error error = FT_New_Memory_Face(ft, (unsigned char*)ttfFile.data(), ttfFile.size(), 0, &ft_face);
assert(!error);
blob = hb_blob_create(ttfFile.data(), ttfFile.size(), HB_MEMORY_MODE_DUPLICATE, nullptr, nullptr);
hb_face = hb_face_create(blob, 0);
hb_font = hb_font_create(hb_face);
}
UVector2 FontAsset::shapeText(std::string_view view, uint32 fontSize, Array<RenderGlyph>& render) {
loadGlyphs(fontSize);
hb_buffer_t* buf = hb_buffer_create();
hb_buffer_add_utf8(buf, view.data(), view.size(), 0, -1);
hb_buffer_set_language(buf, hb_language_from_string("en", -1));
hb_buffer_set_script(buf, HB_SCRIPT_LATIN);
hb_buffer_set_direction(buf, HB_DIRECTION_LTR);
hb_shape(hb_font, buf, nullptr, 0);
uint32 num_glyphs;
hb_glyph_info_t* glyphs = hb_buffer_get_glyph_infos(buf, &num_glyphs);
hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buf, &num_glyphs);
Vector2 min = Vector2(std::numeric_limits<float>::max());
Vector2 max = Vector2(std::numeric_limits<float>::lowest());
int32 xppem, yppem;
hb_font_get_scale(hb_font, &xppem, &yppem);
Vector2 cursor = Vector2(0);
for (uint32 i = 0; i < num_glyphs; ++i) {
hb_codepoint_t glyphid = glyphs[i].codepoint;
float xOffset = positions[i].x_offset * fontSize / xppem;
float yOffset = positions[i].y_offset * fontSize / yppem;
float xAdvance = positions[i].x_advance * fontSize / xppem;
float yAdvance = positions[i].y_advance * fontSize / yppem;
Vector2 position = cursor + Vector2(xOffset, yOffset);
Vector2 dimensions = fontSizes[fontSize].glyphs[glyphid].size;
render.add(RenderGlyph{
.texture = fontSizes[fontSize].glyphs[glyphid].texture,
.position = position,
.dimensions = dimensions,
});
cursor += Vector2(xAdvance, yAdvance);
min = Vector2(std::min(min.x, position.x), std::min(min.y, position.y));
max = Vector2(std::max(max.x, position.x + dimensions.x), std::max(min.y, position.y + dimensions.y));
}
hb_buffer_destroy(buf);
return UVector2(max - min);
}
void FontAsset::loadFontSize(uint32 fontSize) {
FT_Set_Pixel_Sizes(face, 0, fontSize);
fontSizes[fontSize].ascent = face->ascender;
fontSizes[fontSize].descent = face->descender;
fontSizes[fontSize].linegap = 0;
for (uint32 c = 0; c < 256; ++c) {
if (FT_Load_Char(face, c, FT_LOAD_RENDER | FT_LOAD_COLOR)) {
continue;
}
FontAsset::Glyph& glyph = fontSizes[fontSize].glyphs[c];
glyph.size = IVector2(face->glyph->bitmap.width, face->glyph->bitmap.rows);
glyph.bearing = IVector2(face->glyph->bitmap_left, face->glyph->bitmap_top);
glyph.advance = face->glyph->advance.x;
void FontAsset::loadGlyphs(uint32 fontSize) {
FT_Set_Pixel_Sizes(ft_face, 0, fontSize);
for (uint32 i = 0; i < ft_face->num_glyphs; ++i) {
assert(FT_Load_Glyph(ft_face, i, FT_LOAD_RENDER | FT_LOAD_COLOR) == 0);
FontAsset::Glyph& glyph = fontSizes[fontSize].glyphs[i];
glyph.size = IVector2(ft_face->glyph->bitmap.width, ft_face->glyph->bitmap.rows);
glyph.bearing = IVector2(ft_face->glyph->bitmap_left, ft_face->glyph->bitmap_top);
glyph.advance = ft_face->glyph->advance.x;
glyph.textureData.resize(glyph.size.x * glyph.size.y);
if (glyph.textureData.size() == 0) {
glyph.size.x = 1;
@@ -49,7 +86,7 @@ void FontAsset::loadFontSize(uint32 fontSize) {
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());
std::memcpy(glyph.textureData.data(), ft_face->glyph->bitmap.buffer, glyph.textureData.size());
}
glyph.texture = graphics->createTexture2D(TextureCreateInfo{
.sourceData =
+14 -12
View File
@@ -4,6 +4,7 @@
#include "Graphics/Texture.h"
#include "Math/Math.h"
#include <freetype/freetype.h>
#include <harfbuzz/hb.h>
namespace Seele {
class FontAsset : public Asset {
@@ -25,25 +26,26 @@ class FontAsset : public Asset {
void load(ArchiveBuffer& buffer);
};
struct FontData {
int32 ascent;
int32 descent;
int32 linegap;
Map<uint32, Glyph> glyphs;
};
const Glyph& getGlyphData(char c, uint32 fontSize) {
if (!fontSizes.contains(fontSize))
loadFontSize(fontSize);
return fontSizes[fontSize].glyphs[c];
}
const FontData& getFontData(uint32 fontSize) { return fontSizes[fontSize]; }
void loadFace();
struct RenderGlyph {
Gfx::PTexture2D texture;
UVector2 position;
UVector2 dimensions;
};
UVector2 shapeText(std::string_view view, uint32 fontSize, Array<RenderGlyph>& render);
private:
void loadFontSize(uint32 fontSize);
void loadGlyphs(uint32 fontSize);
Gfx::PGraphics graphics;
hb_blob_t* blob;
hb_face_t* hb_face;
hb_font_t* hb_font;
FT_Library ft;
FT_Face face;
Array<uint8> ttfFile;
FT_Face ft_face;
Array<char> ttfFile;
Map<uint32, FontData> fontSizes;
friend class FontLoader;
};
+1 -1
View File
@@ -44,7 +44,7 @@ void Camera::moveY(float amount) {
}
void Camera::buildViewMatrix() {
Vector eyePos = getTransform().getPosition() + Vector(0, 0, -5);
Vector eyePos = getTransform().getPosition();
Vector lookAt = eyePos + getTransform().getForward();
viewMatrix = glm::lookAt(eyePos, lookAt, Vector(0, 1, 0));
cameraPos = eyePos;
+5
View File
@@ -1,7 +1,12 @@
#pragma once
#include <concepts>
#include <ranges>
namespace Seele {
template <typename Range, typename T>
concept container_compatible_range = (std::ranges::input_range<Range>) && std::convertible_to<std::ranges::range_reference_t<Range>, T>;
template <class F, class... Args>
concept invocable = std::invocable<F, Args...>;
+15 -1
View File
@@ -1,5 +1,6 @@
#pragma once
#include "EngineTypes.h"
#include "Concepts.h"
#include <algorithm>
#include <assert.h>
#include <initializer_list>
@@ -117,7 +118,20 @@ template <typename T, typename Allocator = std::pmr::polymorphic_allocator<T>> s
assert(_data != nullptr);
std::uninitialized_copy(init.begin(), init.end(), begin());
}
template <container_compatible_range<T> Range>
constexpr Array(std::from_range_t, Range&& rg, const allocator_type& alloc = allocator_type())
: arraySize(0), allocated(0), allocator(alloc) {
if constexpr (std::ranges::sized_range<Range> || std::ranges::forward_range<Range>) {
arraySize = std::ranges::size(rg);
allocated = arraySize;
_data = allocateArray(allocated);
std::ranges::uninitialized_copy(rg.begin(), rg.end(), begin(), end());
} else {
for (auto it = std::ranges::begin(rg); it != std::ranges::end(rg); it++) {
add(*it);
}
}
}
constexpr Array(const Array& other)
: arraySize(other.arraySize), allocated(other.allocated),
allocator(std::allocator_traits<allocator_type>::select_on_container_copy_construction(other.allocator)) {
+17 -33
View File
@@ -58,40 +58,21 @@ void UIPass::beginFrame(const Component::Camera& cam) {
for (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,
});
} else {
y = y + render.baseline;
for (uint32 c : render.text) {
const FontAsset::Glyph& glyph = render.font->getGlyphData(c, render.fontSize);
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;
}
//todo: convert using actual tree depth
uint32 textureIndex = -1ul;
if (render.backgroundTexture != nullptr) {
textureIndex = usedTextures.size();
usedTextures.add(render.backgroundTexture);
}
elements.add(RenderElementStyle{
.x = x,
.y = y,
.w = render.dimensions.x,
.h = render.dimensions.y,
.color = render.backgroundColor,
.z = render.level / 100.f,
.textureIndex = textureIndex,
});
}
glyphInstanceBuffer->rotateBuffer(sizeof(GlyphInstanceData) * glyphs.size());
glyphInstanceBuffer->updateContents(0, sizeof(GlyphInstanceData) * glyphs.size(), glyphs.data());
@@ -115,6 +96,9 @@ void UIPass::beginFrame(const Component::Camera& cam) {
uiDescriptorSet = uiDescriptorLayout->allocateDescriptorSet();
uiDescriptorSet->updateBuffer(0, 0, elementBuffer);
uiDescriptorSet->updateSampler(1, 0, glyphSampler);
for (uint32 i = 0; i < usedTextures.size(); ++i) {
uiDescriptorSet->updateTexture(2, i, usedTextures[i]);
}
uiDescriptorSet->writeChanges();
}
+1 -2
View File
@@ -122,7 +122,6 @@ TopLevelAS::TopLevelAS(PGraphics graphics, const Gfx::TopLevelASCreateInfo& crea
.flags = 0,
.size = buildSizesInfo.accelerationStructureSize,
.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
},
VmaAllocationCreateInfo{
.usage = VMA_MEMORY_USAGE_AUTO,
@@ -156,7 +155,7 @@ TopLevelAS::TopLevelAS(PGraphics graphics, const Gfx::TopLevelASCreateInfo& crea
VkAccelerationStructureBuildGeometryInfoKHR buildGeometry = {
.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR,
.pNext = nullptr,
.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR,
.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR,
.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR,
.dstAccelerationStructure = handle,
.geometryCount = 1,
+7 -11
View File
@@ -7,14 +7,10 @@
namespace Seele {
namespace UI {
struct UIRender {
std::string text;
PFontAsset font;
uint32 fontSize;
Vector textColor;
Gfx::PTexture2D backgroundTexture;
Vector backgroundColor;
Vector2 position;
Vector2 dimensions;
uint32 baseline;
uint32 z;
uint32 level;
};
@@ -45,15 +41,15 @@ class Element {
} else if (style.widthType == DimensionType::Percent) {
dimensions.x = parentSize.x * style.width / 100.0f;
}
for (auto& child : children) {
child->layout(maxDimensions - Vector2(style.marginLeft + style.marginRight, style.marginTop + style.marginBottom));
}
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 - Vector2(style.marginLeft + style.marginRight, style.marginTop + style.marginBottom));
}
switch (style.innerDisplay) {
case InnerDisplayType::Flow:
flowLayout();
@@ -73,7 +69,7 @@ class Element {
// 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 > maxDimensions.x - child->style.left && cursor.x != 0) {
cursor.x = 0;
cursor.x = style.paddingLeft;
cursor.y += lineHeight;
lineHeight = 0;
}
@@ -94,7 +90,7 @@ class Element {
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.x = style.paddingLeft;
cursor.y += lineHeight;
}
child->position.x = cursor.x + child->style.marginLeft;
+12 -18
View File
@@ -1,6 +1,7 @@
#pragma once
#include "UI/Element.h"
#include "UI/Style/Class.h"
#include <ranges>
namespace Seele {
namespace UI {
@@ -13,31 +14,24 @@ template <StyleClass... classes> class Text : public Element {
(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], style.fontSize).advance / 64.0f;
}
dimensions.x += style.fontFamily->getGlyphData(text[text.size() - 1], style.fontSize).size.x;
dimensions.y = style.fontSize;
UVector2 cursor = UVector2(0);
dimensions = style.fontFamily->shapeText(text, style.fontSize, glyphRenders);
}
virtual Array<UIRender> render(Vector2 anchor, uint32 level) override {
return {
UIRender{
.text = text,
.font = style.fontFamily,
.fontSize = style.fontSize,
.position = position + anchor,
.dimensions = dimensions,
.baseline = style.fontSize * 3 / 4, // TODO: improve
.z = style.z,
.level = level,
},
};
return Array<UIRender>(std::from_range, glyphRenders | std::views::transform([=](const FontAsset::RenderGlyph& glyph) {
return UIRender{
.backgroundTexture = glyph.texture,
.position = Vector2(glyph.position) + anchor,
.dimensions = Vector2(glyph.dimensions),
.level = level,
};
}));
}
// height = fontsize * 1.12
private:
std::string text;
Array<FontAsset::RenderGlyph> glyphRenders;
};
} // namespace UI
} // namespace Seele