Merge remote-tracking branch 'origin/master'
This commit is contained in:
1
3rdParty/glfw/CMakeLists.txt
vendored
1
3rdParty/glfw/CMakeLists.txt
vendored
@@ -13,6 +13,7 @@ FetchContent_Declare(
|
|||||||
)
|
)
|
||||||
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||||
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||||
|
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(GLFW_VULKAN_STATIC ON CACHE BOOL "" FORCE)
|
set(GLFW_VULKAN_STATIC ON CACHE BOOL "" FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
6
3rdParty/msdf/CMakeLists.txt
vendored
6
3rdParty/msdf/CMakeLists.txt
vendored
@@ -36,6 +36,12 @@ if (ENABLE_MSDF)
|
|||||||
|
|
||||||
set(FT_SRC_DIR "${CMAKE_BINARY_DIR}/_deps/freetype-src")
|
set(FT_SRC_DIR "${CMAKE_BINARY_DIR}/_deps/freetype-src")
|
||||||
set(FT_BUILD_DIR "${FT_SRC_DIR}/build")
|
set(FT_BUILD_DIR "${FT_SRC_DIR}/build")
|
||||||
|
|
||||||
|
set(FREETYPE_CONFIG_FILE "${FT_SRC_DIR}/include/freetype/config/ftoption.h")
|
||||||
|
file(READ "${FREETYPE_CONFIG_FILE}" FILE_CONTENTS)
|
||||||
|
string(REPLACE "/* #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING */" "#define FT_CONFIG_OPTION_SUBPIXEL_RENDERING" FILE_CONTENTS "${FILE_CONTENTS}")
|
||||||
|
file(WRITE "${FREETYPE_CONFIG_FILE}" "${FILE_CONTENTS}")
|
||||||
|
|
||||||
file(MAKE_DIRECTORY ${FT_BUILD_DIR})
|
file(MAKE_DIRECTORY ${FT_BUILD_DIR})
|
||||||
if (IOS)
|
if (IOS)
|
||||||
set(PLATFORM_CFG -DCMAKE_TOOLCHAIN_FILE=${CMAKE_CURRENT_SOURCE_DIR}/patched_freetype_iOS_toolchain.cmake)
|
set(PLATFORM_CFG -DCMAKE_TOOLCHAIN_FILE=${CMAKE_CURRENT_SOURCE_DIR}/patched_freetype_iOS_toolchain.cmake)
|
||||||
|
|||||||
@@ -56,17 +56,18 @@ namespace OpenVulkano
|
|||||||
texts.push_back(std::make_pair("This is first line\nSecond gg line\nThird G line", TextConfig()));
|
texts.push_back(std::make_pair("This is first line\nSecond gg line\nThird G line", TextConfig()));
|
||||||
texts[1].second.backgroundColor.a = 255;
|
texts[1].second.backgroundColor.a = 255;
|
||||||
|
|
||||||
const int N = texts.size();
|
constexpr int atlasesCount = 4;
|
||||||
|
const int textsCount = texts.size();
|
||||||
auto& resourceLoader = ResourceLoader::GetInstance();
|
auto& resourceLoader = ResourceLoader::GetInstance();
|
||||||
const std::string fontPath = resourceLoader.GetResourcePath("Roboto-Regular.ttf");
|
const std::string fontPath = resourceLoader.GetResourcePath("Roboto-Regular.ttf");
|
||||||
m_nodesPool.resize(N * 3);
|
m_nodesPool.resize(textsCount * atlasesCount);
|
||||||
m_drawablesPool.resize(N * 3);
|
m_drawablesPool.resize(textsCount * atlasesCount);
|
||||||
|
|
||||||
if constexpr (CREATE_BITMAP_ATLAS)
|
if constexpr (CREATE_BITMAP_ATLAS)
|
||||||
{
|
{
|
||||||
// ReSharper disable once CppDFAUnreachableCode
|
// ReSharper disable once CppDFAUnreachableCode
|
||||||
std::set<uint32_t> s = BitmapFontAtlasGenerator::LoadAllGlyphs(fontPath);
|
std::set<uint32_t> s = BitmapFontAtlasGenerator::LoadAllGlyphs(fontPath);
|
||||||
BitmapFontAtlasGenerator generator;
|
BitmapFontAtlasGenerator generator(FontPixelSizeConfig(), SubpixelLayout::RGB);
|
||||||
generator.GenerateAtlas(fontPath, s);
|
generator.GenerateAtlas(fontPath, s);
|
||||||
generator.GetAtlas()->Save("bitmap_atlas_packed.png");
|
generator.GetAtlas()->Save("bitmap_atlas_packed.png");
|
||||||
}
|
}
|
||||||
@@ -81,63 +82,73 @@ namespace OpenVulkano
|
|||||||
auto sdfMetadataInfo = resourceLoader.GetResource("sdf_atlas_packed.png");
|
auto sdfMetadataInfo = resourceLoader.GetResource("sdf_atlas_packed.png");
|
||||||
auto msdfMetadataInfo = resourceLoader.GetResource("msdf_atlas_packed.png");
|
auto msdfMetadataInfo = resourceLoader.GetResource("msdf_atlas_packed.png");
|
||||||
auto bitmapMetadataInfo = resourceLoader.GetResource("bitmap_atlas_packed.png");
|
auto bitmapMetadataInfo = resourceLoader.GetResource("bitmap_atlas_packed.png");
|
||||||
|
auto bitmapSubpixelRenderingMetadataInfo = resourceLoader.GetResource("bitmap_subpixel_atlas_packed.png");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (int i = 0; i < texts.size() * 3; i++)
|
for (int i = 0, xOffset = -5; i < atlasesCount; i++, xOffset += 20)
|
||||||
{
|
{
|
||||||
int textIdx = i % texts.size();
|
for (int j = 0; j < texts.size(); j++)
|
||||||
TextDrawable* t = nullptr;
|
{
|
||||||
|
TextDrawable* t = nullptr;
|
||||||
#if defined(MSDFGEN_AVAILABLE) && CREATE_NEW_ATLAS
|
#if defined(MSDFGEN_AVAILABLE) && CREATE_NEW_ATLAS
|
||||||
if (i < texts.size())
|
if (i < texts.size())
|
||||||
{
|
{
|
||||||
t = new TextDrawable(m_atlasGenerator.GetAtlasData(), texts[textIdx].second);
|
t = new TextDrawable(m_atlasGenerator.GetAtlasData(), texts[j].second);
|
||||||
}
|
t->SetShader(&TextDrawable::GetSdfDefaultShader());
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
t = new TextDrawable(m_msdfAtlasGenerator.GetAtlasData(), texts[textIdx].second);
|
{
|
||||||
}
|
t = new TextDrawable(m_msdfAtlasGenerator.GetAtlasData(), texts[j].second);
|
||||||
|
t->SetShader(&TextDrawable::GetMsdfDefaultShader());
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
int xOffset = 0;
|
if (i == 0)
|
||||||
if (i < N)
|
{
|
||||||
{
|
t = new TextDrawable(sdfMetadataInfo, texts[j].second);
|
||||||
t = new TextDrawable(sdfMetadataInfo, texts[textIdx].second);
|
}
|
||||||
xOffset = -5;
|
else if (i == 1)
|
||||||
|
{
|
||||||
|
t = new TextDrawable(msdfMetadataInfo, texts[j].second);
|
||||||
|
}
|
||||||
|
else if (i == 2)
|
||||||
|
{
|
||||||
|
// bitmap
|
||||||
|
t = new TextDrawable(bitmapMetadataInfo, texts[j].second);
|
||||||
|
}
|
||||||
|
else if (i == 3)
|
||||||
|
{
|
||||||
|
// bitmap subpixel rendering
|
||||||
|
t = new TextDrawable(bitmapSubpixelRenderingMetadataInfo, texts[j].second);
|
||||||
|
}
|
||||||
|
// OR use separate texture + metadata file
|
||||||
|
//auto metadataInfo = resourceLoader.GetResource("atlas_metadata");
|
||||||
|
//auto data = resourceLoader.GetResource("roboto-regular-atlas.png");
|
||||||
|
//Image::ImageLoaderPng loader;
|
||||||
|
//static auto image = loader.loadData(reinterpret_cast<uint8_t*>(data.Data()), data.Size());
|
||||||
|
//static Texture tex;
|
||||||
|
//tex.resolution = image->resolution;
|
||||||
|
//tex.textureBuffer = image->data.Data();
|
||||||
|
//tex.format = image->dataFormat;
|
||||||
|
//tex.size = image->data.Size(); // 1 channel
|
||||||
|
//TextDrawable* t = new TextDrawable(metadataInfo, &tex, texts[i].second);
|
||||||
|
#endif // MSDFGEN_AVAILABLE
|
||||||
|
const int nodeIdx = i * texts.size() + j;
|
||||||
|
t->GenerateText(texts[j].first);
|
||||||
|
m_drawablesPool[nodeIdx].reset(t);
|
||||||
|
m_nodesPool[nodeIdx].Init();
|
||||||
|
m_nodesPool[nodeIdx].SetMatrix(
|
||||||
|
Math::Utils::translate(glm::mat4x4(1.f), Vector3f(xOffset, 2 - j * 2, 0)));
|
||||||
|
m_nodesPool[nodeIdx].AddDrawable(m_drawablesPool[nodeIdx].get());
|
||||||
|
m_scene.GetRoot()->AddChild(&m_nodesPool[nodeIdx]);
|
||||||
}
|
}
|
||||||
else if (i >= N && i < N * 2)
|
|
||||||
{
|
|
||||||
t = new TextDrawable(msdfMetadataInfo, texts[textIdx].second);
|
|
||||||
xOffset = 15;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
t = new TextDrawable(bitmapMetadataInfo, texts[textIdx].second);
|
|
||||||
xOffset = 35;
|
|
||||||
}
|
|
||||||
// OR use separate texture + metadata file
|
|
||||||
//auto metadataInfo = resourceLoader.GetResource("atlas_metadata");
|
|
||||||
//auto data = resourceLoader.GetResource("roboto-regular-atlas.png");
|
|
||||||
//Image::ImageLoaderPng loader;
|
|
||||||
//static auto image = loader.loadData(reinterpret_cast<uint8_t*>(data.Data()), data.Size());
|
|
||||||
//static Texture tex;
|
|
||||||
//tex.resolution = image->resolution;
|
|
||||||
//tex.textureBuffer = image->data.Data();
|
|
||||||
//tex.format = image->dataFormat;
|
|
||||||
//tex.size = image->data.Size(); // 1 channel
|
|
||||||
//TextDrawable* t = new TextDrawable(metadataInfo, &tex, texts[i].second);
|
|
||||||
#endif // MSDFGEN_AVAILABLE
|
|
||||||
t->GenerateText(texts[textIdx].first);
|
|
||||||
m_drawablesPool[i].reset(t);
|
|
||||||
m_nodesPool[i].Init();
|
|
||||||
m_nodesPool[i].SetMatrix(Math::Utils::translate(glm::mat4x4(1.f), Vector3f(xOffset, 2 - textIdx * 2, 0)));
|
|
||||||
m_nodesPool[i].AddDrawable(m_drawablesPool[i].get());
|
|
||||||
m_scene.GetRoot()->AddChild(&m_nodesPool[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GetGraphicsAppManager()->GetRenderer()->SetScene(&m_scene);
|
GetGraphicsAppManager()->GetRenderer()->SetScene(&m_scene);
|
||||||
m_camController.Init(&m_cam);
|
m_camController.Init(&m_cam);
|
||||||
m_camController.SetDefaultKeybindings();
|
m_camController.SetDefaultKeybindings();
|
||||||
m_camController.SetPosition({ 10, 0, 15 });
|
m_camController.SetPosition({ 10, 0, 15 });
|
||||||
m_camController.SetBoostFactor(5);
|
m_camController.SetBoostFactor(5);
|
||||||
|
|
||||||
std::shared_ptr<UI::PerformanceInfo> m_perfInfo =
|
std::shared_ptr<UI::PerformanceInfo> m_perfInfo =
|
||||||
std::make_shared<UI::PerformanceInfo>();
|
std::make_shared<UI::PerformanceInfo>();
|
||||||
m_ui.AddElement(m_perfInfo);
|
m_ui.AddElement(m_perfInfo);
|
||||||
|
|||||||
BIN
examples/ExampleSources/bitmap_subpixel_atlas_packed.png
Normal file
BIN
examples/ExampleSources/bitmap_subpixel_atlas_packed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 258 KiB |
@@ -10,6 +10,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include "Base/Event.hpp"
|
#include "Base/Event.hpp"
|
||||||
|
#include "Scene/SubpixelLayout.hpp"
|
||||||
#undef max
|
#undef max
|
||||||
|
|
||||||
namespace OpenVulkano
|
namespace OpenVulkano
|
||||||
@@ -34,6 +35,9 @@ namespace OpenVulkano
|
|||||||
[[nodiscard]] bool GetVSync() const { return m_vSync; }
|
[[nodiscard]] bool GetVSync() const { return m_vSync; }
|
||||||
void SetVSync(bool vSync) { m_vSync = vSync; }
|
void SetVSync(bool vSync) { m_vSync = vSync; }
|
||||||
|
|
||||||
|
[[nodiscard]] SubpixelLayout GetSubpixelLayout() const { return m_subpixelLayout; }
|
||||||
|
void SetSubpixelLayout(SubpixelLayout subpixelLayout) { m_subpixelLayout = subpixelLayout; }
|
||||||
|
|
||||||
[[nodiscard]] int32_t GetFpsCap() const { return m_fpsCap; }
|
[[nodiscard]] int32_t GetFpsCap() const { return m_fpsCap; }
|
||||||
void SetFpsCap(int32_t fpsCap)
|
void SetFpsCap(int32_t fpsCap)
|
||||||
{
|
{
|
||||||
@@ -58,6 +62,7 @@ namespace OpenVulkano
|
|||||||
bool m_preferFramebufferFormatSRGB = true;
|
bool m_preferFramebufferFormatSRGB = true;
|
||||||
bool m_lazyRendering = false;
|
bool m_lazyRendering = false;
|
||||||
bool m_vSync = false;
|
bool m_vSync = false;
|
||||||
|
SubpixelLayout m_subpixelLayout = SubpixelLayout::AUTO;
|
||||||
int32_t m_fpsCap = -1; // -1 = no fps cap. 0 = fps cap if vsync, no cap otherwise. > 0 = set fps cap
|
int32_t m_fpsCap = -1; // -1 = no fps cap. 0 = fps cap if vsync, no cap otherwise. > 0 = set fps cap
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
uint32_t m_preferredImageCount = 3;
|
uint32_t m_preferredImageCount = 3;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "Math/Math.hpp"
|
#include "Math/Math.hpp"
|
||||||
#include "Base/PlatformEnums.hpp"
|
#include "Base/PlatformEnums.hpp"
|
||||||
|
#include "Scene/SubpixelLayout.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@@ -89,6 +90,7 @@ namespace OpenVulkano
|
|||||||
|
|
||||||
virtual float GetContentScale() const { return 1; }
|
virtual float GetContentScale() const { return 1; }
|
||||||
virtual float GetInterfaceOrientation() const { return 0; }
|
virtual float GetInterfaceOrientation() const { return 0; }
|
||||||
|
virtual SubpixelLayout GetSubpixelLayout() const { return SubpixelLayout::UNKNOWN; }
|
||||||
protected:
|
protected:
|
||||||
static uint32_t CreateWindowId()
|
static uint32_t CreateWindowId()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -60,8 +60,10 @@ SetShaderDependency(openVulkanoCpp
|
|||||||
${SHADER_OUTPUT_DEST})
|
${SHADER_OUTPUT_DEST})
|
||||||
|
|
||||||
if (NOT ANDROID AND NOT IOS)
|
if (NOT ANDROID AND NOT IOS)
|
||||||
target_link_libraries(openVulkanoCpp PUBLIC glfw pugixml)
|
if (LINUX)
|
||||||
target_link_libraries(openVulkanoCpp PUBLIC ftxui::screen ftxui::dom ftxui::component)
|
target_link_libraries(openVulkanoCpp PUBLIC fontconfig)
|
||||||
|
endif()
|
||||||
|
target_link_libraries(openVulkanoCpp PUBLIC glfw pugixml ftxui::screen ftxui::dom ftxui::component)
|
||||||
if (ENABLE_CURL)
|
if (ENABLE_CURL)
|
||||||
LinkCurl(openVulkanoCpp)
|
LinkCurl(openVulkanoCpp)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -6,13 +6,15 @@
|
|||||||
|
|
||||||
#include "WindowGLFW.hpp"
|
#include "WindowGLFW.hpp"
|
||||||
#include "Base/Logger.hpp"
|
#include "Base/Logger.hpp"
|
||||||
|
#include "Base/EngineConfiguration.hpp"
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
#if __linux__
|
||||||
|
#include <fontconfig/fontconfig.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace OpenVulkano::GLFW
|
namespace OpenVulkano::GLFW
|
||||||
{
|
{
|
||||||
WindowGLFW::WindowGLFW(OpenVulkano::GLFW::InputProviderGLFW& inputProvider)
|
WindowGLFW::WindowGLFW(OpenVulkano::GLFW::InputProviderGLFW& inputProvider) : inputProvider(inputProvider) {}
|
||||||
: inputProvider(inputProvider)
|
|
||||||
{}
|
|
||||||
|
|
||||||
WindowGLFW::~WindowGLFW() noexcept
|
WindowGLFW::~WindowGLFW() noexcept
|
||||||
{
|
{
|
||||||
@@ -31,8 +33,8 @@ namespace OpenVulkano::GLFW
|
|||||||
{
|
{
|
||||||
int posX, posY, sizeX, sizeY;
|
int posX, posY, sizeX, sizeY;
|
||||||
glfwGetMonitorWorkarea(monitors[i], &posX, &posY, &sizeX, &sizeY);
|
glfwGetMonitorWorkarea(monitors[i], &posX, &posY, &sizeX, &sizeY);
|
||||||
if (windowConfig.position.x >= posX && windowConfig.position.x < posX + sizeX &&
|
if (windowConfig.position.x >= posX && windowConfig.position.x < posX + sizeX
|
||||||
windowConfig.position.y >= posY && windowConfig.position.y < posY + sizeY)
|
&& windowConfig.position.y >= posY && windowConfig.position.y < posY + sizeY)
|
||||||
{
|
{
|
||||||
return monitors[i];
|
return monitors[i];
|
||||||
}
|
}
|
||||||
@@ -56,8 +58,12 @@ namespace OpenVulkano::GLFW
|
|||||||
glfwWindowHint(GLFW_DECORATED, (~windowConfig.windowMode) & 1);
|
glfwWindowHint(GLFW_DECORATED, (~windowConfig.windowMode) & 1);
|
||||||
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, windowConfig.transparentFrameBuffer);
|
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, windowConfig.transparentFrameBuffer);
|
||||||
//TODO handle full screen resolutions
|
//TODO handle full screen resolutions
|
||||||
window = glfwCreateWindow(windowConfig.size.x, windowConfig.size.y, windowConfig.title.c_str(), GetTargetMonitor(), nullptr);
|
window = glfwCreateWindow(windowConfig.size.x, windowConfig.size.y, windowConfig.title.c_str(),
|
||||||
if (!window) return;
|
GetTargetMonitor(), nullptr);
|
||||||
|
if (!window)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
float scaleX, scaleY;
|
float scaleX, scaleY;
|
||||||
glfwGetWindowContentScale(window, &scaleX, &scaleY);
|
glfwGetWindowContentScale(window, &scaleX, &scaleY);
|
||||||
contentScale = std::max(scaleX, scaleY);
|
contentScale = std::max(scaleX, scaleY);
|
||||||
@@ -89,13 +95,9 @@ namespace OpenVulkano::GLFW
|
|||||||
{
|
{
|
||||||
glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, hideMouse);
|
glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, hideMouse);
|
||||||
glfwSetInputMode(window, GLFW_CURSOR, hideMouse ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
|
glfwSetInputMode(window, GLFW_CURSOR, hideMouse ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLFWmonitor* WindowGLFW::GetPrimaryMonitor()
|
GLFWmonitor* WindowGLFW::GetPrimaryMonitor() { return glfwGetPrimaryMonitor(); }
|
||||||
{
|
|
||||||
return glfwGetPrimaryMonitor();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<GLFWmonitor*> WindowGLFW::GetMonitors()
|
std::vector<GLFWmonitor*> WindowGLFW::GetMonitors()
|
||||||
{
|
{
|
||||||
@@ -112,13 +114,19 @@ namespace OpenVulkano::GLFW
|
|||||||
|
|
||||||
void WindowGLFW::Init(RenderAPI::RenderApi renderApi)
|
void WindowGLFW::Init(RenderAPI::RenderApi renderApi)
|
||||||
{
|
{
|
||||||
if (renderApi == RenderAPI::Vulkan) glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
if (renderApi == RenderAPI::Vulkan)
|
||||||
|
{
|
||||||
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
|
}
|
||||||
Create();
|
Create();
|
||||||
if (!window)
|
if (!window)
|
||||||
{
|
{
|
||||||
throw WindowInitFailedException("Failed to initialize window");
|
throw WindowInitFailedException("Failed to initialize window");
|
||||||
}
|
}
|
||||||
if (renderApi != RenderAPI::Vulkan) MakeCurrentThread();
|
if (renderApi != RenderAPI::Vulkan)
|
||||||
|
{
|
||||||
|
MakeCurrentThread();
|
||||||
|
}
|
||||||
Logger::WINDOW->info("GLFW Window created (id: {0}) with scale {1}", GetWindowId(), contentScale);
|
Logger::WINDOW->info("GLFW Window created (id: {0}) with scale {1}", GetWindowId(), contentScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,20 +137,11 @@ namespace OpenVulkano::GLFW
|
|||||||
Logger::WINDOW->info("GLFW Window destroyed (id: {0})", GetWindowId());
|
Logger::WINDOW->info("GLFW Window destroyed (id: {0})", GetWindowId());
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowGLFW::Present() const
|
void WindowGLFW::Present() const { glfwSwapBuffers(window); }
|
||||||
{
|
|
||||||
glfwSwapBuffers(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WindowGLFW::Show()
|
void WindowGLFW::Show() { glfwShowWindow(window); }
|
||||||
{
|
|
||||||
glfwShowWindow(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WindowGLFW::Hide()
|
void WindowGLFW::Hide() { glfwHideWindow(window); }
|
||||||
{
|
|
||||||
glfwHideWindow(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WindowGLFW::SetTitle(const std::string& title)
|
void WindowGLFW::SetTitle(const std::string& title)
|
||||||
{
|
{
|
||||||
@@ -175,7 +174,9 @@ namespace OpenVulkano::GLFW
|
|||||||
Math::Vector2ui WindowGLFW::GetSize()
|
Math::Vector2ui WindowGLFW::GetSize()
|
||||||
{
|
{
|
||||||
if (currentSize.x == 0 || currentSize.y == 0)
|
if (currentSize.x == 0 || currentSize.y == 0)
|
||||||
|
{
|
||||||
glfwGetWindowSize(window, reinterpret_cast<int*>(¤tSize.x), reinterpret_cast<int*>(¤tSize.y));
|
glfwGetWindowSize(window, reinterpret_cast<int*>(¤tSize.x), reinterpret_cast<int*>(¤tSize.y));
|
||||||
|
}
|
||||||
return currentSize;
|
return currentSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,6 +196,67 @@ namespace OpenVulkano::GLFW
|
|||||||
glfwSetWindowSizeLimits(window, minWidth, minHeight, maxWidth, maxHeight);
|
glfwSetWindowSizeLimits(window, minWidth, minHeight, maxWidth, maxHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SubpixelLayout WindowGLFW::GetSubpixelLayout() const
|
||||||
|
{
|
||||||
|
SubpixelLayout engineLayout = EngineConfiguration::GetEngineConfiguration()->GetSubpixelLayout();
|
||||||
|
if (engineLayout != SubpixelLayout::UNKNOWN)
|
||||||
|
{
|
||||||
|
return engineLayout;
|
||||||
|
}
|
||||||
|
#if _WIN32
|
||||||
|
BOOL val;
|
||||||
|
// check if font smoothing is enabled
|
||||||
|
SystemParametersInfoA(SPI_GETFONTSMOOTHING, 0, &val, 0);
|
||||||
|
if (!val)
|
||||||
|
{
|
||||||
|
return SubpixelLayout::UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT mode;
|
||||||
|
SystemParametersInfoA(SPI_GETFONTSMOOTHINGORIENTATION, 0, &mode, 0);
|
||||||
|
if (mode == FE_FONTSMOOTHINGORIENTATIONBGR)
|
||||||
|
{
|
||||||
|
return SubpixelLayout::BGR;
|
||||||
|
}
|
||||||
|
else if (mode == FE_FONTSMOOTHINGORIENTATIONRGB)
|
||||||
|
{
|
||||||
|
return SubpixelLayout::RGB;
|
||||||
|
}
|
||||||
|
return SubpixelLayout::UNKNOWN;
|
||||||
|
#elif __linux__
|
||||||
|
FcInit();
|
||||||
|
FcPattern* pattern = FcPatternCreate();
|
||||||
|
FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
|
||||||
|
FcDefaultSubstitute(pattern);
|
||||||
|
|
||||||
|
FcResult result;
|
||||||
|
FcPattern* match = FcFontMatch(nullptr, pattern, &result);
|
||||||
|
if (!match)
|
||||||
|
{
|
||||||
|
Logger::WINDOW->error("Failed to match font pattern.");
|
||||||
|
FcPatternDestroy(pattern);
|
||||||
|
return SubpixelLayout::UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int subpixelOrder = -1;
|
||||||
|
if (FcPatternGetInteger(match, FC_RGBA, 0, &subpixelOrder) == FcResultMatch) {
|
||||||
|
switch (subpixelOrder) {
|
||||||
|
case FC_RGBA_RGB: return SubpixelLayout::RGB;
|
||||||
|
case FC_RGBA_BGR: return SubpixelLayout::BGR;
|
||||||
|
case FC_RGBA_VRGB: return SubpixelLayout::RGBV;
|
||||||
|
case FC_RGBA_VBGR: return SubpixelLayout::BGRV;
|
||||||
|
case FC_RGBA_NONE:
|
||||||
|
default: return SubpixelLayout::UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FcPatternDestroy(match);
|
||||||
|
FcPatternDestroy(pattern);
|
||||||
|
return SubpixelLayout::UNKNOWN;
|
||||||
|
#else
|
||||||
|
return SubpixelLayout::UNKNOWN;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void WindowGLFW::MakeCurrentThread()
|
void WindowGLFW::MakeCurrentThread()
|
||||||
{
|
{
|
||||||
glfwMakeContextCurrent(window);
|
glfwMakeContextCurrent(window);
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ namespace OpenVulkano::GLFW
|
|||||||
|
|
||||||
void SetSizeLimits(int minWidth, int minHeight, int maxWidth, int maxHeight) override;
|
void SetSizeLimits(int minWidth, int minHeight, int maxWidth, int maxHeight) override;
|
||||||
|
|
||||||
|
SubpixelLayout GetSubpixelLayout() const override;
|
||||||
|
|
||||||
[[nodiscard]] float GetContentScale() const override { return contentScale; }
|
[[nodiscard]] float GetContentScale() const override { return contentScale; }
|
||||||
|
|
||||||
void MakeCurrentThread() override;
|
void MakeCurrentThread() override;
|
||||||
|
|||||||
@@ -424,7 +424,7 @@ namespace OpenVulkano
|
|||||||
static const std::string osName = "Windows";
|
static const std::string osName = "Windows";
|
||||||
return osName;
|
return osName;
|
||||||
}
|
}
|
||||||
|
|
||||||
OsVersion SystemInfo::GetOsVersion()
|
OsVersion SystemInfo::GetOsVersion()
|
||||||
{
|
{
|
||||||
static OsVersion osVersion = {};
|
static OsVersion osVersion = {};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "BitmapFontAtlasGenerator.hpp"
|
#include "BitmapFontAtlasGenerator.hpp"
|
||||||
#include "Base/Logger.hpp"
|
#include "Base/Logger.hpp"
|
||||||
#include "Text/FontAtlas.hpp"
|
#include "Text/FontAtlas.hpp"
|
||||||
|
#include <freetype/ftlcdfil.h>
|
||||||
|
|
||||||
namespace OpenVulkano::Scene
|
namespace OpenVulkano::Scene
|
||||||
{
|
{
|
||||||
@@ -34,10 +35,19 @@ namespace OpenVulkano::Scene
|
|||||||
|
|
||||||
const auto& [lib, face] = FontAtlasGeneratorBase::InitFreetype(source);
|
const auto& [lib, face] = FontAtlasGeneratorBase::InitFreetype(source);
|
||||||
FT_Set_Pixel_Sizes(face.get(), 0, m_pixelSizeConfig.CalculatePixelSize());
|
FT_Set_Pixel_Sizes(face.get(), 0, m_pixelSizeConfig.CalculatePixelSize());
|
||||||
|
if (m_subpixelLayout != SubpixelLayout::UNKNOWN)
|
||||||
|
{
|
||||||
|
FT_Error error = FT_Library_SetLcdFilter(lib.get(), FT_LCD_FILTER_DEFAULT);
|
||||||
|
if (error != 0)
|
||||||
|
{
|
||||||
|
m_subpixelLayout = SubpixelLayout::UNKNOWN;
|
||||||
|
m_channelsCount = 1;
|
||||||
|
Logger::SCENE->error("Failed to set lcd filter for subpixel rendering. {}", GetFreetypeErrorDescription(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto [allGlyphs, area] = InitGlyphsForPacking(chset, face);
|
auto [allGlyphs, atlasWidth] = InitGlyphsForPacking(chset, face);
|
||||||
const double atlasWidth = ceil(sqrt(area));
|
std::vector<Shelf> shelves = Shelf::CreateShelves(atlasWidth, allGlyphs, face, m_channelsCount);
|
||||||
std::vector<Shelf> shelves = Shelf::CreateShelves(atlasWidth, allGlyphs, face);
|
|
||||||
uint32_t atlasHeight = 0;
|
uint32_t atlasHeight = 0;
|
||||||
std::for_each(shelves.begin(), shelves.end(), [&](const Shelf& shelf) { atlasHeight += shelf.GetHeight(); });
|
std::for_each(shelves.begin(), shelves.end(), [&](const Shelf& shelf) { atlasHeight += shelf.GetHeight(); });
|
||||||
const Math::Vector2ui atlasResolution = { atlasWidth, atlasHeight };
|
const Math::Vector2ui atlasResolution = { atlasWidth, atlasHeight };
|
||||||
@@ -47,7 +57,10 @@ namespace OpenVulkano::Scene
|
|||||||
// but since some algorithms have already been implemented for EM_NORMALIZED mode, currently there is no support for default font metrics (ints)
|
// but since some algorithms have already been implemented for EM_NORMALIZED mode, currently there is no support for default font metrics (ints)
|
||||||
// The coordinates will be normalized to the em size, i.e. 1 = 1 em
|
// The coordinates will be normalized to the em size, i.e. 1 = 1 em
|
||||||
const double scaleFactor = (1. / face->units_per_EM);
|
const double scaleFactor = (1. / face->units_per_EM);
|
||||||
m_atlasData = std::make_shared<FontAtlas>(atlasResolution, face->height * scaleFactor, FontAtlasType::BITMAP);
|
m_atlasData = std::make_shared<FontAtlas>(atlasResolution, face->height * scaleFactor,
|
||||||
|
static_cast<bool>(m_subpixelLayout) ? FontAtlasType::BITMAP_SUBPIXEL :
|
||||||
|
FontAtlasType::BITMAP,
|
||||||
|
m_subpixelLayout.GetTextureDataFormat());
|
||||||
FillGlyphsInfo(allGlyphs, face, scaleFactor);
|
FillGlyphsInfo(allGlyphs, face, scaleFactor);
|
||||||
if (pngOutput) m_atlasData->Save(*pngOutput);
|
if (pngOutput) m_atlasData->Save(*pngOutput);
|
||||||
}
|
}
|
||||||
@@ -61,20 +74,67 @@ namespace OpenVulkano::Scene
|
|||||||
allGlyphs.reserve(chset.size());
|
allGlyphs.reserve(chset.size());
|
||||||
for (uint32_t codepoint : chset)
|
for (uint32_t codepoint : chset)
|
||||||
{
|
{
|
||||||
error = FT_Load_Char(face.get(), codepoint, FT_LOAD_RENDER);
|
error = FT_Load_Char(face.get(), codepoint, GetGlyphRenderMode());
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
Logger::SCENE->error("FT_Load_Char for codepoint {} has failed. {}", codepoint,
|
Logger::SCENE->error("FT_Load_Char for codepoint {} has failed. {}", codepoint,
|
||||||
GetFreetypeErrorDescription(error));
|
GetFreetypeErrorDescription(error));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// TODO: Try to reduce resulting texture size in subpixel rendering mode,
|
||||||
|
// since freetype for some glyphs not only triples width/height by 3, but also adds extra padding and extra(meaningful?) pixels.
|
||||||
|
// NOTE: looks like it adds 2 pixels to the left and right in FT_LOAD_TARGET_LCD mode, so we should take this into account in FillSubpixelData.
|
||||||
|
// https://freetype.org/freetype2/docs/reference/ft2-lcd_rendering.html
|
||||||
|
// So, the possible approach to try is:
|
||||||
|
// 1) render glyph here with FT_LOAD_RENDER mode;
|
||||||
|
// 2) render glyph in FillGlyphsInfo with FT_LOAD_RENDER | FT_LOAD_TARGET_LCD mode;
|
||||||
|
// 3) take into account all mentioned things above for proper mapping.
|
||||||
FT_GlyphSlot slot = face->glyph;
|
FT_GlyphSlot slot = face->glyph;
|
||||||
GlyphForPacking& glyph = allGlyphs.emplace_back(codepoint, Math::Vector2ui(slot->bitmap.width, slot->bitmap.rows));
|
GlyphForPacking& glyph = allGlyphs.emplace_back(codepoint, ScaleGlyphSize(slot->bitmap.width, slot->bitmap.rows));
|
||||||
area += slot->bitmap.rows * slot->bitmap.width;
|
area += slot->bitmap.rows * slot->bitmap.width;
|
||||||
}
|
}
|
||||||
std::sort(allGlyphs.begin(), allGlyphs.end(),
|
std::sort(allGlyphs.begin(), allGlyphs.end(),
|
||||||
[](const GlyphForPacking& a, const GlyphForPacking& b) { return a.size.y > b.size.y; });
|
[](const GlyphForPacking& a, const GlyphForPacking& b) { return a.size.y > b.size.y; });
|
||||||
return { allGlyphs, area };
|
// make atlas in square form, so that atlasWidth +- equals atlasHeight
|
||||||
|
return { allGlyphs, ceil(sqrt(area / (m_channelsCount == 1 ? 1 : 3))) };
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vector2ui BitmapFontAtlasGenerator::ScaleGlyphSize(unsigned int w, unsigned int h) const
|
||||||
|
{
|
||||||
|
if (m_subpixelLayout == SubpixelLayout::UNKNOWN || m_channelsCount == 1)
|
||||||
|
{
|
||||||
|
return { w, h };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_subpixelLayout.IsHorizontalSubpixelLayout())
|
||||||
|
{
|
||||||
|
assert(w % 3 == 0);
|
||||||
|
w /= 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(h % 3 == 0);
|
||||||
|
h /= 3;
|
||||||
|
}
|
||||||
|
return { w, h };
|
||||||
|
}
|
||||||
|
|
||||||
|
FT_Int32 BitmapFontAtlasGenerator::GetGlyphRenderMode() const
|
||||||
|
{
|
||||||
|
if (m_channelsCount == 1)
|
||||||
|
{
|
||||||
|
return FT_LOAD_RENDER;
|
||||||
|
}
|
||||||
|
FT_Int32 glyphRenderMode = FT_LOAD_RENDER;
|
||||||
|
if (m_subpixelLayout < SubpixelLayout::RGBV)
|
||||||
|
{
|
||||||
|
glyphRenderMode |= FT_LOAD_TARGET_LCD;
|
||||||
|
}
|
||||||
|
else if (m_subpixelLayout < SubpixelLayout::UNKNOWN)
|
||||||
|
{
|
||||||
|
glyphRenderMode |= FT_LOAD_TARGET_LCD_V;
|
||||||
|
}
|
||||||
|
return glyphRenderMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitmapFontAtlasGenerator::FillGlyphsInfo(const std::vector<GlyphForPacking>& allGlyphs, const FtFaceRecPtr& face, double scaleFactor)
|
void BitmapFontAtlasGenerator::FillGlyphsInfo(const std::vector<GlyphForPacking>& allGlyphs, const FtFaceRecPtr& face, double scaleFactor)
|
||||||
@@ -82,7 +142,7 @@ namespace OpenVulkano::Scene
|
|||||||
size_t loadedGlyphs = 0;
|
size_t loadedGlyphs = 0;
|
||||||
for (const GlyphForPacking& glyph : allGlyphs)
|
for (const GlyphForPacking& glyph : allGlyphs)
|
||||||
{
|
{
|
||||||
FT_Error error = FT_Load_Char(face.get(), glyph.code, FT_LOAD_RENDER);
|
FT_Error error = FT_Load_Char(face.get(), glyph.code, GetGlyphRenderMode());
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
Logger::SCENE->error("FT_Load_Char for codepoint {} has failed. {}", glyph.code,
|
Logger::SCENE->error("FT_Load_Char for codepoint {} has failed. {}", glyph.code,
|
||||||
@@ -91,12 +151,20 @@ namespace OpenVulkano::Scene
|
|||||||
}
|
}
|
||||||
|
|
||||||
FT_GlyphSlot slot = face->glyph;
|
FT_GlyphSlot slot = face->glyph;
|
||||||
char* baseAddress = static_cast<char*>(m_atlasData->GetTexture()->textureBuffer) + glyph.firstGlyphByteInAtlas;
|
if (m_channelsCount == 1)
|
||||||
for (int row = 0; row < slot->bitmap.rows; row++)
|
|
||||||
{
|
{
|
||||||
std::memcpy(baseAddress + row * m_atlasData->GetTexture()->resolution.x,
|
char* baseAddress = static_cast<char*>(m_atlasData->GetTexture()->textureBuffer)
|
||||||
&slot->bitmap.buffer[(slot->bitmap.rows - 1 - row) * slot->bitmap.pitch],
|
+ glyph.firstGlyphByteInAtlas;
|
||||||
slot->bitmap.width);
|
for (int row = 0; row < slot->bitmap.rows; row++)
|
||||||
|
{
|
||||||
|
std::memcpy(baseAddress + row * m_atlasData->GetTexture()->resolution.x,
|
||||||
|
&slot->bitmap.buffer[(slot->bitmap.rows - 1 - row) * slot->bitmap.pitch],
|
||||||
|
slot->bitmap.width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FillSubpixelData(slot->bitmap, glyph);
|
||||||
}
|
}
|
||||||
|
|
||||||
GlyphInfo& glyphInfo = m_atlasData->GetGlyphs()[glyph.code];
|
GlyphInfo& glyphInfo = m_atlasData->GetGlyphs()[glyph.code];
|
||||||
@@ -104,10 +172,56 @@ namespace OpenVulkano::Scene
|
|||||||
slot->metrics.height * scaleFactor };
|
slot->metrics.height * scaleFactor };
|
||||||
const Math::Vector2d glyphBearing = { slot->metrics.horiBearingX * scaleFactor,
|
const Math::Vector2d glyphBearing = { slot->metrics.horiBearingX * scaleFactor,
|
||||||
slot->metrics.horiBearingY * scaleFactor };
|
slot->metrics.horiBearingY * scaleFactor };
|
||||||
Math::AABB glyphAtlasAABB(Math::Vector3f(glyph.atlasPos.x, glyph.atlasPos.y, 0), Math::Vector3f(glyph.atlasPos.x + slot->bitmap.width, glyph.atlasPos.y + slot->bitmap.rows, 0));
|
|
||||||
|
const Math::Vector2ui scaledAtlasSize = ScaleGlyphSize(slot->bitmap.width, slot->bitmap.rows);
|
||||||
|
Math::AABB glyphAtlasAABB(Math::Vector3f(glyph.atlasPos.x, glyph.atlasPos.y, 0), Math::Vector3f(glyph.atlasPos.x + scaledAtlasSize.x, glyph.atlasPos.y + scaledAtlasSize.y, 0));
|
||||||
SetGlyphData(glyphInfo, glyphBearing, glyphMetrics, glyphAtlasAABB, slot->advance.x * scaleFactor);
|
SetGlyphData(glyphInfo, glyphBearing, glyphMetrics, glyphAtlasAABB, slot->advance.x * scaleFactor);
|
||||||
loadedGlyphs++;
|
loadedGlyphs++;
|
||||||
}
|
}
|
||||||
Logger::SCENE->debug("Created atlas with {} glyphs, {} glyphs could not be loaded", loadedGlyphs, allGlyphs.size() - loadedGlyphs);
|
Logger::SCENE->debug("Created atlas with {} glyphs, {} glyphs could not be loaded", loadedGlyphs, allGlyphs.size() - loadedGlyphs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BitmapFontAtlasGenerator::FillSubpixelData(const FT_Bitmap& bitmap, const GlyphForPacking& glyph)
|
||||||
|
{
|
||||||
|
Texture* tex = m_atlasData->GetTexture();
|
||||||
|
char* texBuffer = static_cast<char*>(tex->textureBuffer);
|
||||||
|
if (m_subpixelLayout.IsHorizontalSubpixelLayout())
|
||||||
|
{
|
||||||
|
// RGB RGB RGB
|
||||||
|
assert(bitmap.width % 3 == 0);
|
||||||
|
for (int row = 0; row < bitmap.rows; row++)
|
||||||
|
{
|
||||||
|
for (int col = 0, atlasPos = 0; col < bitmap.width; col += 3, atlasPos += 4)
|
||||||
|
{
|
||||||
|
const size_t bitmapPos = row * bitmap.pitch + col;
|
||||||
|
const size_t texturePos = (glyph.firstGlyphByteInAtlas - row * tex->resolution.x * m_channelsCount) + atlasPos;
|
||||||
|
const uint8_t rgb[3] = { bitmap.buffer[bitmapPos], bitmap.buffer[bitmapPos + 1],
|
||||||
|
bitmap.buffer[bitmapPos + 2] };
|
||||||
|
std::memcpy(texBuffer + texturePos, rgb, 3);
|
||||||
|
texBuffer[texturePos + 3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// RRR
|
||||||
|
// GGG
|
||||||
|
// BBB
|
||||||
|
assert(bitmap.rows % 3 == 0);
|
||||||
|
for (int row = 0; row < bitmap.rows; row += 3)
|
||||||
|
{
|
||||||
|
for (int col = 0; col < bitmap.width; col++)
|
||||||
|
{
|
||||||
|
const size_t bitmapPos = col + (bitmap.pitch * row);
|
||||||
|
const size_t texturePos = (glyph.firstGlyphByteInAtlas + col * m_channelsCount)
|
||||||
|
- ((row / 3) * (tex->resolution.x * m_channelsCount));
|
||||||
|
const uint8_t rgb[3] = { bitmap.buffer[bitmapPos + 2 * bitmap.pitch],
|
||||||
|
bitmap.buffer[bitmapPos + bitmap.pitch], bitmap.buffer[bitmapPos] };
|
||||||
|
std::memcpy(texBuffer + texturePos, rgb, 3);
|
||||||
|
texBuffer[texturePos + 3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "FontAtlasGeneratorBase.hpp"
|
#include "FontAtlasGeneratorBase.hpp"
|
||||||
|
#include "Scene/SubpixelLayout.hpp"
|
||||||
#include "Shelf.hpp"
|
#include "Shelf.hpp"
|
||||||
|
|
||||||
namespace OpenVulkano::Scene
|
namespace OpenVulkano::Scene
|
||||||
@@ -34,7 +35,13 @@ namespace OpenVulkano::Scene
|
|||||||
class BitmapFontAtlasGenerator : public FontAtlasGeneratorBase
|
class BitmapFontAtlasGenerator : public FontAtlasGeneratorBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BitmapFontAtlasGenerator(FontPixelSizeConfig config = FontPixelSizeConfig()) : FontAtlasGeneratorBase(1), m_pixelSizeConfig(config) {}
|
BitmapFontAtlasGenerator(FontPixelSizeConfig config = FontPixelSizeConfig(),
|
||||||
|
std::optional<SubpixelLayout> subpixelLayout = std::nullopt)
|
||||||
|
: FontAtlasGeneratorBase(subpixelLayout.has_value() && *subpixelLayout < SubpixelLayout::UNKNOWN ? 4 : 1)
|
||||||
|
, m_pixelSizeConfig(config)
|
||||||
|
, m_subpixelLayout(subpixelLayout.value_or(SubpixelLayout::UNKNOWN))
|
||||||
|
{
|
||||||
|
}
|
||||||
void GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset,
|
void GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset,
|
||||||
const std::optional<std::string>& pngOutput = std::nullopt) override;
|
const std::optional<std::string>& pngOutput = std::nullopt) override;
|
||||||
void GenerateAtlas(const Array<char>& fontData, const std::set<uint32_t>& charset,
|
void GenerateAtlas(const Array<char>& fontData, const std::set<uint32_t>& charset,
|
||||||
@@ -42,8 +49,13 @@ namespace OpenVulkano::Scene
|
|||||||
private:
|
private:
|
||||||
void Generate(const std::variant<std::string, Array<char>>& source, const std::set<uint32_t>& chset, const std::optional<std::string>& pngOutput);
|
void Generate(const std::variant<std::string, Array<char>>& source, const std::set<uint32_t>& chset, const std::optional<std::string>& pngOutput);
|
||||||
void FillGlyphsInfo(const std::vector<GlyphForPacking>& allGlyphs, const FtFaceRecPtr& face, double scaleFactor);
|
void FillGlyphsInfo(const std::vector<GlyphForPacking>& allGlyphs, const FtFaceRecPtr& face, double scaleFactor);
|
||||||
|
void FillSubpixelData(const FT_Bitmap& bitmap, const GlyphForPacking& glyph);
|
||||||
|
FT_Int32 GetGlyphRenderMode() const;
|
||||||
|
// tmp function
|
||||||
|
Math::Vector2ui ScaleGlyphSize(unsigned int w, unsigned int h) const;
|
||||||
std::pair<std::vector<GlyphForPacking>, double> InitGlyphsForPacking(const std::set<uint32_t>& chset, const FtFaceRecPtr& face);
|
std::pair<std::vector<GlyphForPacking>, double> InitGlyphsForPacking(const std::set<uint32_t>& chset, const FtFaceRecPtr& face);
|
||||||
private:
|
private:
|
||||||
FontPixelSizeConfig m_pixelSizeConfig;
|
FontPixelSizeConfig m_pixelSizeConfig;
|
||||||
|
SubpixelLayout m_subpixelLayout;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ namespace OpenVulkano::Scene
|
|||||||
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
m_atlasData = std::make_shared<FontAtlas>(Math::Vector2ui{ width, height }, fontGeometry.getMetrics().lineHeight,
|
m_atlasData = std::make_shared<FontAtlas>(Math::Vector2ui{ width, height }, fontGeometry.getMetrics().lineHeight,
|
||||||
channelsCount == 1 ? FontAtlasType::SDF : FontAtlasType::MSDF);
|
channelsCount == 1 ? FontAtlasType::SDF : FontAtlasType::MSDF, m_channelsCount == 1 ? DataFormat::R8_UNORM : DataFormat::R8G8B8A8_UNORM);
|
||||||
|
|
||||||
if constexpr (Channels == 3)
|
if constexpr (Channels == 3)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,9 +24,10 @@ namespace OpenVulkano::Scene
|
|||||||
struct Shelf
|
struct Shelf
|
||||||
{
|
{
|
||||||
inline static std::vector<Shelf> CreateShelves(uint32_t atlasWidth, std::vector<GlyphForPacking>& glyphs,
|
inline static std::vector<Shelf> CreateShelves(uint32_t atlasWidth, std::vector<GlyphForPacking>& glyphs,
|
||||||
const FtFaceRecPtr& face);
|
const FtFaceRecPtr& face, int channelsCount);
|
||||||
|
|
||||||
Shelf(uint32_t width, uint32_t height) : m_width(width), m_height(height), m_remainingWidth(width) {}
|
Shelf(uint32_t width, uint32_t height, int pixelSize, uint32_t prevShelvesHeight)
|
||||||
|
: m_width(width), m_height(height), m_remainingWidth(width), m_pixelSize(pixelSize), m_prevShelvesHeight(prevShelvesHeight) {}
|
||||||
bool HasSpaceForGlyph(uint32_t glyphWidth, uint32_t glyphHeight) const
|
bool HasSpaceForGlyph(uint32_t glyphWidth, uint32_t glyphHeight) const
|
||||||
{
|
{
|
||||||
return m_remainingWidth >= glyphWidth && m_height >= glyphHeight;
|
return m_remainingWidth >= glyphWidth && m_height >= glyphHeight;
|
||||||
@@ -35,7 +36,10 @@ namespace OpenVulkano::Scene
|
|||||||
uint32_t GetHeight() const { return m_height; }
|
uint32_t GetHeight() const { return m_height; }
|
||||||
uint32_t GetNextGlyphPos() const { return m_nextGlyphPos; };
|
uint32_t GetNextGlyphPos() const { return m_nextGlyphPos; };
|
||||||
uint32_t GetOccupiedSize() const { return ((m_width - m_remainingWidth) * m_height); }
|
uint32_t GetOccupiedSize() const { return ((m_width - m_remainingWidth) * m_height); }
|
||||||
std::optional<uint32_t> AddGlyph(uint32_t glyphWidth, uint32_t glyphHeight)
|
uint32_t GetPrevShelvesHeight() const { return m_prevShelvesHeight; }
|
||||||
|
int GetPixelSize() const { return m_pixelSize; }
|
||||||
|
|
||||||
|
std::optional<std::pair<uint32_t, uint32_t>> AddGlyph(uint32_t glyphWidth, uint32_t glyphHeight)
|
||||||
{
|
{
|
||||||
if (!HasSpaceForGlyph(glyphWidth, glyphHeight))
|
if (!HasSpaceForGlyph(glyphWidth, glyphHeight))
|
||||||
{
|
{
|
||||||
@@ -44,7 +48,13 @@ namespace OpenVulkano::Scene
|
|||||||
uint32_t insertionPos = m_nextGlyphPos;
|
uint32_t insertionPos = m_nextGlyphPos;
|
||||||
m_nextGlyphPos += glyphWidth;
|
m_nextGlyphPos += glyphWidth;
|
||||||
m_remainingWidth -= glyphWidth;
|
m_remainingWidth -= glyphWidth;
|
||||||
return insertionPos;
|
|
||||||
|
uint32_t hOffset = m_height - (m_height - glyphHeight);
|
||||||
|
uint32_t glyphFirstByte = (insertionPos * m_pixelSize)
|
||||||
|
+ ((hOffset * m_width * m_pixelSize) - (m_width * m_pixelSize))
|
||||||
|
+ (m_prevShelvesHeight * m_width * m_pixelSize);
|
||||||
|
|
||||||
|
return std::make_pair(insertionPos, glyphFirstByte);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -52,10 +62,12 @@ namespace OpenVulkano::Scene
|
|||||||
uint32_t m_height;
|
uint32_t m_height;
|
||||||
uint32_t m_remainingWidth;
|
uint32_t m_remainingWidth;
|
||||||
uint32_t m_nextGlyphPos = 0;
|
uint32_t m_nextGlyphPos = 0;
|
||||||
|
uint32_t m_prevShelvesHeight;
|
||||||
|
int m_pixelSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Shelf> Shelf::CreateShelves(uint32_t atlasWidth, std::vector<GlyphForPacking>& glyphs,
|
std::vector<Shelf> Shelf::CreateShelves(uint32_t atlasWidth, std::vector<GlyphForPacking>& glyphs,
|
||||||
const FtFaceRecPtr& face)
|
const FtFaceRecPtr& face, int channelsCount)
|
||||||
{
|
{
|
||||||
std::vector<Shelf> shelves;
|
std::vector<Shelf> shelves;
|
||||||
for (GlyphForPacking& glyph : glyphs)
|
for (GlyphForPacking& glyph : glyphs)
|
||||||
@@ -71,10 +83,10 @@ namespace OpenVulkano::Scene
|
|||||||
uint32_t totalPrevShelvesHeight = 0;
|
uint32_t totalPrevShelvesHeight = 0;
|
||||||
for (Shelf& shelf : shelves)
|
for (Shelf& shelf : shelves)
|
||||||
{
|
{
|
||||||
if (std::optional<uint32_t> insertionPosX = shelf.AddGlyph(glyph.size.x, glyph.size.y))
|
if (std::optional<std::pair<uint32_t, uint32_t>> opt = shelf.AddGlyph(glyph.size.x, glyph.size.y))
|
||||||
{
|
{
|
||||||
glyph.firstGlyphByteInAtlas = *insertionPosX + (totalPrevShelvesHeight * atlasWidth);
|
glyph.firstGlyphByteInAtlas = opt->second;
|
||||||
glyph.atlasPos.x = *insertionPosX;
|
glyph.atlasPos.x = opt->first;
|
||||||
glyph.atlasPos.y = totalPrevShelvesHeight;
|
glyph.atlasPos.y = totalPrevShelvesHeight;
|
||||||
needNewShelf = false;
|
needNewShelf = false;
|
||||||
break;
|
break;
|
||||||
@@ -84,9 +96,10 @@ namespace OpenVulkano::Scene
|
|||||||
|
|
||||||
if (needNewShelf)
|
if (needNewShelf)
|
||||||
{
|
{
|
||||||
shelves.emplace_back(atlasWidth, glyph.size.y);
|
shelves.emplace_back(atlasWidth, glyph.size.y, channelsCount, totalPrevShelvesHeight);
|
||||||
shelves.back().AddGlyph(glyph.size.x, glyph.size.y);
|
Shelf& shelf = shelves.back();
|
||||||
glyph.firstGlyphByteInAtlas = totalPrevShelvesHeight * atlasWidth;
|
uint32_t firstByte = (*shelf.AddGlyph(glyph.size.x, glyph.size.y)).second;
|
||||||
|
glyph.firstGlyphByteInAtlas = firstByte;
|
||||||
glyph.atlasPos.x = 0;
|
glyph.atlasPos.x = 0;
|
||||||
glyph.atlasPos.y = totalPrevShelvesHeight;
|
glyph.atlasPos.y = totalPrevShelvesHeight;
|
||||||
}
|
}
|
||||||
|
|||||||
75
openVulkanoCpp/Scene/SubpixelLayout.hpp
Normal file
75
openVulkanoCpp/Scene/SubpixelLayout.hpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Scene/DataFormat.hpp"
|
||||||
|
#include <magic_enum.hpp>
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace OpenVulkano
|
||||||
|
{
|
||||||
|
class SubpixelLayout
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Layout : uint32_t
|
||||||
|
{
|
||||||
|
RGB,
|
||||||
|
BGR,
|
||||||
|
RGBV,
|
||||||
|
BGRV,
|
||||||
|
// unknown and auto must be last two
|
||||||
|
UNKNOWN,
|
||||||
|
NONE = UNKNOWN,
|
||||||
|
AUTO = UNKNOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr SubpixelLayout() = default;
|
||||||
|
|
||||||
|
constexpr SubpixelLayout(Layout layout) : m_layout(layout) {}
|
||||||
|
|
||||||
|
[[nodiscard]] DataFormat GetTextureDataFormat() const
|
||||||
|
{
|
||||||
|
if (m_layout >= SubpixelLayout::UNKNOWN)
|
||||||
|
{
|
||||||
|
return DataFormat::R8_UINT;
|
||||||
|
}
|
||||||
|
if (m_layout == SubpixelLayout::BGR || m_layout == SubpixelLayout::BGRV)
|
||||||
|
{
|
||||||
|
return DataFormat::B8G8R8A8_UINT;
|
||||||
|
}
|
||||||
|
return DataFormat::R8G8B8A8_UINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::string_view GetName() const { return magic_enum::enum_name(m_layout); }
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr bool operator==(Layout rhs) const
|
||||||
|
{
|
||||||
|
return m_layout == rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr bool operator!=(Layout rhs) const
|
||||||
|
{
|
||||||
|
return m_layout != rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr operator uint32_t() const
|
||||||
|
{
|
||||||
|
return m_layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsHorizontalSubpixelLayout() const
|
||||||
|
{
|
||||||
|
return m_layout == SubpixelLayout::RGB || m_layout == SubpixelLayout::BGR;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const { return m_layout < Layout::UNKNOWN; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Layout m_layout = Layout::UNKNOWN;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -120,7 +120,10 @@ namespace OpenVulkano::Scene
|
|||||||
const std::span metadataSpan(metadata, eofHeader.metadataSize);
|
const std::span metadataSpan(metadata, eofHeader.metadataSize);
|
||||||
if (eofHeader.flags.version == 0) LoadLegacy(metadataSpan);
|
if (eofHeader.flags.version == 0) LoadLegacy(metadataSpan);
|
||||||
else LoadNew(metadataSpan);
|
else LoadNew(metadataSpan);
|
||||||
if (GetAtlasType() >= FontAtlasType::BITMAP) m_texture.m_samplerConfig = &SamplerConfig::NEAREST;
|
if (GetAtlasType() >= FontAtlasType::BITMAP && GetAtlasType() != FontAtlasType::UNKNOWN)
|
||||||
|
{
|
||||||
|
m_texture.m_samplerConfig = &SamplerConfig::NEAREST;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontAtlas::LoadLegacy(const std::span<char> data)
|
void FontAtlas::LoadLegacy(const std::span<char> data)
|
||||||
@@ -171,14 +174,18 @@ namespace OpenVulkano::Scene
|
|||||||
m_texture.textureBuffer = m_imgData.Data();
|
m_texture.textureBuffer = m_imgData.Data();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontAtlas::Init(const Math::Vector2ui textureResolution, const double lineHeight, const FontAtlasType atlasType)
|
void FontAtlas::Init(const Math::Vector2ui textureResolution, const double lineHeight,
|
||||||
|
const FontAtlasType atlasType, DataFormat dataFormat)
|
||||||
{
|
{
|
||||||
m_metadata = { lineHeight, atlasType };
|
m_metadata = { lineHeight, atlasType };
|
||||||
m_texture.format = atlasType.GetChannelCount() == 1 ? DataFormat::R8_UNORM : DataFormat::R8G8B8A8_UNORM;
|
m_texture.format = dataFormat;
|
||||||
m_texture.resolution = { textureResolution, 1 };
|
m_texture.resolution = { textureResolution, 1 };
|
||||||
m_imgData = Array<uint8_t>(m_texture.format.CalculatedSize(m_texture.resolution.x, m_texture.resolution.y));
|
m_imgData = Array<uint8_t>(m_texture.format.CalculatedSize(m_texture.resolution.x, m_texture.resolution.y));
|
||||||
m_texture.textureBuffer = m_imgData.Data();
|
m_texture.textureBuffer = m_imgData.Data();
|
||||||
m_texture.size = m_imgData.Size();
|
m_texture.size = m_imgData.Size();
|
||||||
if (atlasType >= FontAtlasType::BITMAP) m_texture.m_samplerConfig = &SamplerConfig::NEAREST;
|
if (atlasType >= FontAtlasType::BITMAP && atlasType != FontAtlasType::UNKNOWN)
|
||||||
|
{
|
||||||
|
m_texture.m_samplerConfig = &SamplerConfig::NEAREST;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,11 +46,15 @@ namespace OpenVulkano::Scene
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
FontAtlas() = default;
|
FontAtlas() = default;
|
||||||
FontAtlas(const Math::Vector2ui textureResolution, const double lineHeight, const FontAtlasType atlasType) { Init(textureResolution, lineHeight, atlasType); }
|
FontAtlas(const Math::Vector2ui textureResolution, const double lineHeight, const FontAtlasType atlasType,
|
||||||
|
DataFormat dataFormat)
|
||||||
|
{
|
||||||
|
Init(textureResolution, lineHeight, atlasType, dataFormat);
|
||||||
|
}
|
||||||
FontAtlas(const std::filesystem::path& path);
|
FontAtlas(const std::filesystem::path& path);
|
||||||
FontAtlas(const std::span<char> data) { Load(data); }
|
FontAtlas(const std::span<char> data) { Load(data); }
|
||||||
|
|
||||||
void Init(Math::Vector2ui textureResolution, double lineHeight, FontAtlasType atlasType);
|
void Init(Math::Vector2ui textureResolution, double lineHeight, FontAtlasType atlasType, DataFormat dataFormat);
|
||||||
|
|
||||||
void Save(const std::filesystem::path& path) const;
|
void Save(const std::filesystem::path& path) const;
|
||||||
void Load(std::span<char> data);
|
void Load(std::span<char> data);
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ namespace OpenVulkano::Scene
|
|||||||
SDF = 0,
|
SDF = 0,
|
||||||
MSDF,
|
MSDF,
|
||||||
BITMAP,
|
BITMAP,
|
||||||
|
BITMAP_SUBPIXEL,
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::string_view DEFAULT_FG_SHADERS[] = { "Shader/sdfText", "Shader/msdfText", "Shader/text" };
|
static constexpr std::string_view DEFAULT_FG_SHADERS[] = { "Shader/sdfText", "Shader/msdfText", "Shader/text",
|
||||||
|
"Shader/subpixelText" };
|
||||||
|
|
||||||
static constexpr uint32_t CHANNEL_COUNT[] = { 1, 4, 1, 4, 0 };
|
static constexpr uint32_t CHANNEL_COUNT[] = { 1, 4, 1, 4, 0 };
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ namespace OpenVulkano::Scene
|
|||||||
constexpr uint32_t MISSING_GLYPH_SYMBOL = '?';
|
constexpr uint32_t MISSING_GLYPH_SYMBOL = '?';
|
||||||
|
|
||||||
Shader DEFAULT_SHADER_BITMAP = TextDrawable::MakeDefaultShader(FontAtlasType::BITMAP);
|
Shader DEFAULT_SHADER_BITMAP = TextDrawable::MakeDefaultShader(FontAtlasType::BITMAP);
|
||||||
|
Shader DEFAULT_SHADER_BITMAP_SUBPIXEL = TextDrawable::MakeDefaultShader(FontAtlasType::BITMAP_SUBPIXEL);
|
||||||
Shader DEFAULT_SHADER_SDF = TextDrawable::MakeDefaultShader(FontAtlasType::SDF);
|
Shader DEFAULT_SHADER_SDF = TextDrawable::MakeDefaultShader(FontAtlasType::SDF);
|
||||||
Shader DEFAULT_SHADER_MSDF = TextDrawable::MakeDefaultShader(FontAtlasType::MSDF);
|
Shader DEFAULT_SHADER_MSDF = TextDrawable::MakeDefaultShader(FontAtlasType::MSDF);
|
||||||
}
|
}
|
||||||
@@ -156,6 +157,7 @@ namespace OpenVulkano::Scene
|
|||||||
case FontAtlasType::MSDF: return &DEFAULT_SHADER_MSDF;
|
case FontAtlasType::MSDF: return &DEFAULT_SHADER_MSDF;
|
||||||
default: Logger::RENDER->warn("No default shader for atlas type: {}", type.GetName());
|
default: Logger::RENDER->warn("No default shader for atlas type: {}", type.GetName());
|
||||||
case FontAtlasType::BITMAP: return &DEFAULT_SHADER_BITMAP;
|
case FontAtlasType::BITMAP: return &DEFAULT_SHADER_BITMAP;
|
||||||
|
case FontAtlasType::BITMAP_SUBPIXEL: return &DEFAULT_SHADER_BITMAP_SUBPIXEL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
20
openVulkanoCpp/Shader/subpixelText.frag
Normal file
20
openVulkanoCpp/Shader/subpixelText.frag
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec4 color;
|
||||||
|
layout(location = 1) in vec4 bgColor;
|
||||||
|
layout(location = 2) in vec2 texCoord;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
layout(set = 2, binding = 0) uniform sampler2D texSampler;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 sampled = texture(texSampler, texCoord);
|
||||||
|
float alpha = max(sampled.r, max(sampled.g, sampled.b));
|
||||||
|
outColor = vec4(color) * vec4(sampled.rgb, alpha);
|
||||||
|
if (bgColor.a != 0)
|
||||||
|
{
|
||||||
|
outColor = mix(bgColor, outColor, alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user