feat(renderer): added dynamic font loading based on lang requirements (#2738)
Related YimMenu/Translations#117 Closes #2723 Changes made: - Renderer class was changed to a static instance - Some throw code was removed because of change of instantiation of the renderer - Fonts are dynamically loaded based on the language requirements - renderer files have been moved to make the root directory less messy - Added a font mgr to improve the ease of adding more language support
This commit is contained in:
parent
cde5563204
commit
183b0eee44
@ -2,58 +2,58 @@
|
||||
|
||||
namespace big
|
||||
{
|
||||
bool file_manager::init(const std::filesystem::path& base_dir)
|
||||
{
|
||||
m_base_dir = base_dir;
|
||||
file_manager::ensure_folder_exists(m_base_dir);
|
||||
bool file_manager::init(const std::filesystem::path& base_dir)
|
||||
{
|
||||
m_base_dir = base_dir;
|
||||
file_manager::ensure_folder_exists(m_base_dir);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::filesystem::path& file_manager::get_base_dir()
|
||||
{
|
||||
return m_base_dir;
|
||||
}
|
||||
|
||||
const std::filesystem::path& file_manager::get_base_dir()
|
||||
{
|
||||
return m_base_dir;
|
||||
}
|
||||
|
||||
file file_manager::get_project_file(std::filesystem::path file_path)
|
||||
{
|
||||
if (file_path.is_absolute())
|
||||
throw std::invalid_argument("Project files are relative to the BaseDir, don't use absolute paths!");
|
||||
if (file_path.string().contains(".."))
|
||||
{
|
||||
if (file_path.is_absolute())
|
||||
throw std::invalid_argument("Project files are relative to the BaseDir, don't use absolute paths!");
|
||||
if (file_path.string().contains(".."))
|
||||
throw std::invalid_argument("Relative path traversal is not allowed, refrain from using \"..\" in file paths.");
|
||||
|
||||
return file_manager::ensure_file_can_be_created(m_base_dir / file_path);
|
||||
}
|
||||
return file_manager::ensure_file_can_be_created(m_base_dir / file_path);
|
||||
}
|
||||
|
||||
folder file_manager::get_project_folder(std::filesystem::path folder_path)
|
||||
{
|
||||
if (folder_path.is_absolute())
|
||||
throw std::invalid_argument("Project folders are relative to the BaseDir, don't use absolute paths!");
|
||||
if (folder_path.string().contains(".."))
|
||||
{
|
||||
if (folder_path.is_absolute())
|
||||
throw std::invalid_argument("Project folders are relative to the BaseDir, don't use absolute paths!");
|
||||
if (folder_path.string().contains(".."))
|
||||
throw std::invalid_argument("Relative path traversal is not allowed, refrain from using \"..\" in folder paths.");
|
||||
|
||||
return file_manager::ensure_folder_exists(m_base_dir / folder_path);
|
||||
}
|
||||
return file_manager::ensure_folder_exists(m_base_dir / folder_path);
|
||||
}
|
||||
|
||||
std::filesystem::path file_manager::ensure_file_can_be_created(const std::filesystem::path file_path)
|
||||
{
|
||||
file_manager::ensure_folder_exists(file_path.parent_path());
|
||||
{
|
||||
file_manager::ensure_folder_exists(file_path.parent_path());
|
||||
|
||||
return file_path;
|
||||
}
|
||||
|
||||
return file_path;
|
||||
}
|
||||
|
||||
std::filesystem::path file_manager::ensure_folder_exists(const std::filesystem::path folder_path)
|
||||
{
|
||||
bool create_path = !exists(folder_path);
|
||||
{
|
||||
bool create_path = !exists(folder_path);
|
||||
|
||||
if (!create_path && !is_directory(folder_path))
|
||||
{
|
||||
remove(folder_path);
|
||||
create_path = true;
|
||||
}
|
||||
if (create_path)
|
||||
create_directories(folder_path);
|
||||
if (!create_path && !is_directory(folder_path))
|
||||
{
|
||||
remove(folder_path);
|
||||
create_path = true;
|
||||
}
|
||||
if (create_path)
|
||||
create_directories(folder_path);
|
||||
|
||||
return folder_path;
|
||||
}
|
||||
return folder_path;
|
||||
}
|
||||
}
|
||||
|
24
src/gui.cpp
24
src/gui.cpp
@ -2,7 +2,7 @@
|
||||
|
||||
#include "common.hpp"
|
||||
#include "natives.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "renderer/renderer.hpp"
|
||||
#include "script.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
@ -14,35 +14,35 @@ namespace big
|
||||
m_is_open(false),
|
||||
m_override_mouse(false)
|
||||
{
|
||||
g_renderer->add_dx_callback(view::gta_data, -1);
|
||||
g_renderer->add_dx_callback(view::notifications, -2);
|
||||
g_renderer->add_dx_callback(view::overlay, -3);
|
||||
g_renderer->add_dx_callback(view::cmd_executor, -4);
|
||||
g_renderer->add_dx_callback(
|
||||
g_renderer.add_dx_callback(view::gta_data, -1);
|
||||
g_renderer.add_dx_callback(view::notifications, -2);
|
||||
g_renderer.add_dx_callback(view::overlay, -3);
|
||||
g_renderer.add_dx_callback(view::cmd_executor, -4);
|
||||
g_renderer.add_dx_callback(
|
||||
[this] {
|
||||
dx_on_tick();
|
||||
},
|
||||
-5);
|
||||
|
||||
g_renderer->add_wndproc_callback([this](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
g_renderer.add_wndproc_callback([this](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
wndproc(hwnd, msg, wparam, lparam);
|
||||
});
|
||||
g_renderer->add_wndproc_callback([](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
g_renderer.add_wndproc_callback([](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
if (g.cmd_executor.enabled && msg == WM_KEYUP && wparam == VK_ESCAPE)
|
||||
{
|
||||
g.cmd_executor.enabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
g_renderer->add_dx_callback(view::vehicle_control, 3);
|
||||
g_renderer->add_dx_callback(esp::draw, 2); // TODO: move to ESP service
|
||||
g_renderer->add_dx_callback(view::context_menu, 1);
|
||||
g_renderer.add_dx_callback(view::vehicle_control, 3);
|
||||
g_renderer.add_dx_callback(esp::draw, 2); // TODO: move to ESP service
|
||||
g_renderer.add_dx_callback(view::context_menu, 1);
|
||||
|
||||
|
||||
dx_init();
|
||||
|
||||
g_gui = this;
|
||||
g_renderer->rescale(g.window.gui_scale);
|
||||
g_renderer.rescale(g.window.gui_scale);
|
||||
}
|
||||
|
||||
gui::~gui()
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "hooking/hooking.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "renderer/renderer.hpp"
|
||||
#include "script.hpp"
|
||||
|
||||
namespace big
|
||||
@ -8,9 +8,9 @@ namespace big
|
||||
{
|
||||
if (g_running && ((flags & (UINT)DXGI_PRESENT_TEST) != (UINT)DXGI_PRESENT_TEST))
|
||||
{
|
||||
g_renderer->on_present();
|
||||
g_renderer.on_present();
|
||||
}
|
||||
|
||||
return g_hooking->m_swapchain_hook.get_original<decltype(&swapchain_present)>(swapchain_present_index)(this_, sync_interval, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "hooking/hooking.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "renderer/renderer.hpp"
|
||||
#include "script.hpp"
|
||||
|
||||
namespace big
|
||||
@ -8,13 +8,13 @@ namespace big
|
||||
{
|
||||
if (g_running)
|
||||
{
|
||||
g_renderer->pre_reset();
|
||||
g_renderer.pre_reset();
|
||||
|
||||
const auto result = g_hooking->m_swapchain_hook.get_original<decltype(&swapchain_resizebuffers)>(swapchain_resizebuffers_index)(this_, buffer_count, width, height, new_format, swapchain_flags);
|
||||
|
||||
if (SUCCEEDED(result))
|
||||
{
|
||||
g_renderer->post_reset();
|
||||
g_renderer.post_reset();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "hooking/hooking.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "renderer/renderer.hpp"
|
||||
#include "script.hpp"
|
||||
|
||||
namespace big
|
||||
@ -8,7 +8,7 @@ namespace big
|
||||
{
|
||||
if (g_running)
|
||||
{
|
||||
g_renderer->wndproc(hwnd, msg, wparam, lparam);
|
||||
g_renderer.wndproc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
return CallWindowProcW(g_hooking->m_og_wndproc, hwnd, msg, wparam, lparam);
|
||||
|
10
src/main.cpp
10
src/main.cpp
@ -10,7 +10,7 @@
|
||||
#include "native_hooks/native_hooks.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "rage/gameSkeleton.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "renderer/renderer.hpp"
|
||||
#include "script_mgr.hpp"
|
||||
#include "services/api/api_service.hpp"
|
||||
#include "services/context_menu/context_menu_service.hpp"
|
||||
@ -159,10 +159,10 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
|
||||
LOG(INFO) << "Yim's Menu Initializing";
|
||||
LOGF(INFO, "Git Info\n\tBranch:\t{}\n\tHash:\t{}\n\tDate:\t{}", version::GIT_BRANCH, version::GIT_SHA1, version::GIT_DATE);
|
||||
|
||||
// more tech debt, YAY!
|
||||
// more tech debt, YAY!
|
||||
if (is_proton())
|
||||
{
|
||||
LOG(INFO) << "Running on proton!";
|
||||
LOG(INFO) << "Running on proton!";
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -195,7 +195,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
|
||||
auto byte_patch_manager_instance = std::make_unique<byte_patch_manager>();
|
||||
LOG(INFO) << "Byte Patch Manager initialized.";
|
||||
|
||||
auto renderer_instance = std::make_unique<renderer>();
|
||||
g_renderer.init();
|
||||
LOG(INFO) << "Renderer initialized.";
|
||||
auto gui_instance = std::make_unique<gui>();
|
||||
|
||||
@ -327,7 +327,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
|
||||
fiber_pool_instance.reset();
|
||||
LOG(INFO) << "Fiber pool uninitialized.";
|
||||
|
||||
renderer_instance.reset();
|
||||
g_renderer.destroy();
|
||||
LOG(INFO) << "Renderer uninitialized.";
|
||||
|
||||
byte_patch_manager_instance.reset();
|
||||
|
219
src/renderer.cpp
219
src/renderer.cpp
@ -1,219 +0,0 @@
|
||||
#include "renderer.hpp"
|
||||
|
||||
#include "common.hpp"
|
||||
#include "file_manager.hpp"
|
||||
#include "fonts/fonts.hpp"
|
||||
#include "gui.hpp"
|
||||
#include "pointers.hpp"
|
||||
|
||||
#include <backends/imgui_impl_dx11.h>
|
||||
#include <backends/imgui_impl_win32.h>
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
|
||||
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
namespace big
|
||||
{
|
||||
renderer::renderer() :
|
||||
m_dxgi_swapchain(*g_pointers->m_gta.m_swapchain)
|
||||
{
|
||||
if (m_dxgi_swapchain->GetDevice(__uuidof(ID3D11Device), reinterpret_cast<void**>(&m_d3d_device)) < 0)
|
||||
{
|
||||
throw std::runtime_error("Failed to get D3D device.");
|
||||
}
|
||||
m_d3d_device->GetImmediateContext(&m_d3d_device_context);
|
||||
|
||||
auto file_path = g_file_manager.get_project_file("./imgui.ini").get_path();
|
||||
|
||||
ImGuiContext* ctx = ImGui::CreateContext();
|
||||
|
||||
static std::string path = file_path.make_preferred().string();
|
||||
ctx->IO.IniFilename = path.c_str();
|
||||
|
||||
ImGui_ImplDX11_Init(m_d3d_device, m_d3d_device_context);
|
||||
ImGui_ImplWin32_Init(g_pointers->m_hwnd);
|
||||
|
||||
const auto fonts_folder = std::filesystem::path(std::getenv("SYSTEMROOT")) / "Fonts";
|
||||
|
||||
file font_file(fonts_folder / "msyh.ttc");
|
||||
if (!font_file.exists())
|
||||
font_file = file(fonts_folder / "msyh.ttf");
|
||||
|
||||
if (*g_pointers->m_gta.m_language == 8)
|
||||
{
|
||||
font_file = file(fonts_folder / "malgun.ttf");
|
||||
if (!font_file.exists())
|
||||
{
|
||||
LOG(WARNING) << "Korean language detected, but failed to find Korean font, you may not be able to read the menu!";
|
||||
}
|
||||
}
|
||||
|
||||
if (!font_file.exists())
|
||||
{
|
||||
LOG(WARNING) << "Failed to find msyh font, falling back to Arial!";
|
||||
font_file = file(fonts_folder / "arial.ttf");
|
||||
}
|
||||
|
||||
auto font_file_stream = std::ifstream(font_file.get_path(), std::ios::binary | std::ios::ate);
|
||||
const auto font_data_size = static_cast<int>(font_file_stream.tellg());
|
||||
const auto font_data = std::make_unique<uint8_t[]>(font_data_size);
|
||||
|
||||
font_file_stream.seekg(0);
|
||||
font_file_stream.read(reinterpret_cast<char*>(font_data.get()), font_data_size);
|
||||
font_file_stream.close();
|
||||
|
||||
auto& io = ImGui::GetIO();
|
||||
|
||||
{
|
||||
ImFontConfig fnt_cfg{};
|
||||
fnt_cfg.FontDataOwnedByAtlas = false;
|
||||
strcpy(fnt_cfg.Name, "Fnt20px");
|
||||
|
||||
io.Fonts->AddFontFromMemoryTTF(const_cast<uint8_t*>(font_storopia),
|
||||
sizeof(font_storopia),
|
||||
20.f,
|
||||
&fnt_cfg,
|
||||
io.Fonts->GetGlyphRangesDefault());
|
||||
fnt_cfg.MergeMode = true;
|
||||
if (*g_pointers->m_gta.m_language == 8)
|
||||
io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 20.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesKorean());
|
||||
else
|
||||
io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 20.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon());
|
||||
io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 20.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesCyrillic());
|
||||
io.Fonts->Build();
|
||||
}
|
||||
|
||||
{
|
||||
ImFontConfig fnt_cfg{};
|
||||
fnt_cfg.FontDataOwnedByAtlas = false;
|
||||
strcpy(fnt_cfg.Name, "Fnt28px");
|
||||
|
||||
g.window.font_title = io.Fonts->AddFontFromMemoryTTF(const_cast<uint8_t*>(font_storopia), sizeof(font_storopia), 28.f, &fnt_cfg);
|
||||
fnt_cfg.MergeMode = true;
|
||||
if (*g_pointers->m_gta.m_language == 8)
|
||||
io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 28.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesKorean());
|
||||
else
|
||||
io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 28.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon());
|
||||
io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 28.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesCyrillic());
|
||||
io.Fonts->Build();
|
||||
}
|
||||
|
||||
{
|
||||
ImFontConfig fnt_cfg{};
|
||||
fnt_cfg.FontDataOwnedByAtlas = false;
|
||||
strcpy(fnt_cfg.Name, "Fnt24px");
|
||||
|
||||
g.window.font_sub_title = io.Fonts->AddFontFromMemoryTTF(const_cast<uint8_t*>(font_storopia), sizeof(font_storopia), 24.f, &fnt_cfg);
|
||||
fnt_cfg.MergeMode = true;
|
||||
if (*g_pointers->m_gta.m_language == 8)
|
||||
io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 24.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesKorean());
|
||||
else
|
||||
io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 24.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon());
|
||||
io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 24.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesCyrillic());
|
||||
io.Fonts->Build();
|
||||
}
|
||||
|
||||
{
|
||||
ImFontConfig fnt_cfg{};
|
||||
fnt_cfg.FontDataOwnedByAtlas = false;
|
||||
strcpy(fnt_cfg.Name, "Fnt18px");
|
||||
|
||||
g.window.font_small = io.Fonts->AddFontFromMemoryTTF(const_cast<uint8_t*>(font_storopia), sizeof(font_storopia), 18.f, &fnt_cfg);
|
||||
fnt_cfg.MergeMode = true;
|
||||
if (*g_pointers->m_gta.m_language == 8)
|
||||
io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 18.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesKorean());
|
||||
else
|
||||
io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 18.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon());
|
||||
io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 18.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesCyrillic());
|
||||
io.Fonts->Build();
|
||||
}
|
||||
|
||||
{
|
||||
ImFontConfig font_icons_cfg{};
|
||||
font_icons_cfg.FontDataOwnedByAtlas = false;
|
||||
std::strcpy(font_icons_cfg.Name, "Icons");
|
||||
g.window.font_icon = io.Fonts->AddFontFromMemoryTTF(const_cast<uint8_t*>(font_icons), sizeof(font_icons), 24.f, &font_icons_cfg);
|
||||
}
|
||||
|
||||
g_renderer = this;
|
||||
}
|
||||
|
||||
renderer::~renderer()
|
||||
{
|
||||
g_renderer = nullptr;
|
||||
|
||||
ImGui_ImplWin32_Shutdown();
|
||||
ImGui_ImplDX11_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
bool renderer::add_dx_callback(dx_callback callback, uint32_t priority)
|
||||
{
|
||||
if (!m_dx_callbacks.insert({priority, callback}).second)
|
||||
{
|
||||
LOG(WARNING) << "Duplicate priority given on DX Callback!";
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void renderer::add_wndproc_callback(wndproc_callback callback)
|
||||
{
|
||||
m_wndproc_callbacks.emplace_back(callback);
|
||||
}
|
||||
|
||||
void renderer::on_present()
|
||||
{
|
||||
new_frame();
|
||||
for (const auto& cb : m_dx_callbacks | std::views::values)
|
||||
cb();
|
||||
end_frame();
|
||||
}
|
||||
|
||||
void renderer::rescale(float rel_size)
|
||||
{
|
||||
pre_reset();
|
||||
g_gui->restore_default_style();
|
||||
|
||||
if (rel_size != 1.0f)
|
||||
ImGui::GetStyle().ScaleAllSizes(rel_size);
|
||||
|
||||
ImGui::GetStyle().MouseCursorScale = 1.0f;
|
||||
ImGui::GetIO().FontGlobalScale = rel_size;
|
||||
post_reset();
|
||||
}
|
||||
|
||||
void renderer::pre_reset()
|
||||
{
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
}
|
||||
|
||||
void renderer::post_reset()
|
||||
{
|
||||
ImGui_ImplDX11_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
void renderer::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
for (const auto& cb : m_wndproc_callbacks)
|
||||
cb(hwnd, msg, wparam, lparam);
|
||||
|
||||
|
||||
ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
void renderer::new_frame()
|
||||
{
|
||||
ImGui_ImplDX11_NewFrame();
|
||||
ImGui_ImplWin32_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
|
||||
void renderer::end_frame()
|
||||
{
|
||||
ImGui::Render();
|
||||
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
}
|
15
src/renderer/alphabet_types.hpp
Normal file
15
src/renderer/alphabet_types.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
namespace big
|
||||
{
|
||||
enum class eAlphabetType
|
||||
{
|
||||
LATIN, // default - always built
|
||||
CHINESE,
|
||||
CYRILLIC,
|
||||
KOREAN,
|
||||
JAPANESE,
|
||||
LAST_ALPHABET_TYPE
|
||||
};
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(eAlphabetType, {{eAlphabetType::LATIN, "latin"}, {eAlphabetType::CHINESE, "chinese"}, {eAlphabetType::CYRILLIC, "cyrillic"}, {eAlphabetType::KOREAN, "korean"}, {eAlphabetType::JAPANESE, "japanese"}})
|
||||
}
|
141
src/renderer/font_mgr.cpp
Normal file
141
src/renderer/font_mgr.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
#include "font_mgr.hpp"
|
||||
|
||||
#include "fonts/fonts.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "thread_pool.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
font_mgr::font_mgr(std::vector<std::pair<float, ImFont**>> extra_font_sizes) :
|
||||
m_extra_font_sizes(extra_font_sizes),
|
||||
m_require_extra(eAlphabetType::LATIN),
|
||||
m_fonts({
|
||||
{eAlphabetType::CHINESE, {"msyh.ttc", "msyh.ttf", "arial.ttf"}},
|
||||
{eAlphabetType::CYRILLIC, {"msyh.ttc", "msyh.ttf", "arial.ttf"}},
|
||||
{eAlphabetType::JAPANESE, {"msyh.ttc", "msyh.ttf", "arial.ttf"}},
|
||||
{eAlphabetType::KOREAN, {"malgun.ttf", "arial.ttf"}},
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
bool font_mgr::can_use()
|
||||
{
|
||||
return m_update_lock.try_lock();
|
||||
}
|
||||
|
||||
void font_mgr::release_use()
|
||||
{
|
||||
m_update_lock.unlock();
|
||||
}
|
||||
|
||||
void font_mgr::rebuild()
|
||||
{
|
||||
m_update_lock.lock();
|
||||
|
||||
g_renderer.pre_reset();
|
||||
|
||||
const auto extra_font_file = get_available_font_file_for_alphabet_type();
|
||||
if (m_require_extra != eAlphabetType::LATIN && !extra_font_file.exists())
|
||||
{
|
||||
LOG(WARNING) << "Could not find an appropriate font for the current language!";
|
||||
}
|
||||
const auto extra_glyph_range = get_imgui_alphabet_type();
|
||||
|
||||
auto& io = ImGui::GetIO();
|
||||
io.Fonts->Clear();
|
||||
|
||||
// default font size
|
||||
{
|
||||
ImFontConfig fnt_cfg{};
|
||||
fnt_cfg.FontDataOwnedByAtlas = false;
|
||||
strcpy(fnt_cfg.Name, "Fnt20px");
|
||||
|
||||
io.Fonts->AddFontFromMemoryTTF(const_cast<uint8_t*>(font_storopia),
|
||||
sizeof(font_storopia),
|
||||
20.f,
|
||||
&fnt_cfg,
|
||||
io.Fonts->GetGlyphRangesDefault());
|
||||
if (m_require_extra != eAlphabetType::LATIN && extra_font_file.exists())
|
||||
{
|
||||
fnt_cfg.MergeMode = true;
|
||||
io.Fonts->AddFontFromFileTTF(extra_font_file.get_path().string().c_str(), 20.f, &fnt_cfg, extra_glyph_range);
|
||||
}
|
||||
io.Fonts->Build();
|
||||
}
|
||||
|
||||
// any other font sizes we need to support
|
||||
for (auto [size, font_ptr] : m_extra_font_sizes)
|
||||
{
|
||||
ImFontConfig fnt_cfg{};
|
||||
fnt_cfg.FontDataOwnedByAtlas = false;
|
||||
strcpy(fnt_cfg.Name, std::format("Fnt{}px", (int)size).c_str());
|
||||
|
||||
*font_ptr = io.Fonts->AddFontFromMemoryTTF(const_cast<uint8_t*>(font_storopia),
|
||||
sizeof(font_storopia),
|
||||
size,
|
||||
&fnt_cfg,
|
||||
io.Fonts->GetGlyphRangesDefault());
|
||||
if (m_require_extra != eAlphabetType::LATIN && extra_font_file.exists())
|
||||
{
|
||||
fnt_cfg.MergeMode = true;
|
||||
io.Fonts->AddFontFromFileTTF(extra_font_file.get_path().string().c_str(), size, &fnt_cfg, extra_glyph_range);
|
||||
}
|
||||
io.Fonts->Build();
|
||||
}
|
||||
|
||||
// icons blueh
|
||||
{
|
||||
ImFontConfig font_icons_cfg{};
|
||||
font_icons_cfg.FontDataOwnedByAtlas = false;
|
||||
std::strcpy(font_icons_cfg.Name, "Icons");
|
||||
g.window.font_icon = ImGui::GetIO().Fonts->AddFontFromMemoryTTF(const_cast<uint8_t*>(font_icons), sizeof(font_icons), 24.f, &font_icons_cfg);
|
||||
}
|
||||
|
||||
g_renderer.post_reset();
|
||||
|
||||
m_update_lock.unlock();
|
||||
}
|
||||
|
||||
void font_mgr::update_required_alphabet_type(eAlphabetType type)
|
||||
{
|
||||
m_require_extra = type;
|
||||
|
||||
g_thread_pool->push([this]
|
||||
{
|
||||
rebuild();
|
||||
});
|
||||
}
|
||||
|
||||
file font_mgr::get_available_font_file_for_alphabet_type()
|
||||
{
|
||||
static const auto fonts_folder = std::filesystem::path(std::getenv("SYSTEMROOT")) / "Fonts";
|
||||
|
||||
const auto& fonts = m_fonts.find(m_require_extra);
|
||||
if (fonts == m_fonts.end())
|
||||
return {};
|
||||
for (const auto& font : fonts->second)
|
||||
{
|
||||
auto font_file = file(fonts_folder / font);
|
||||
if (font_file.exists())
|
||||
{
|
||||
return font_file;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
const ImWchar* font_mgr::get_imgui_alphabet_type()
|
||||
{
|
||||
auto& io = ImGui::GetIO();
|
||||
switch (m_require_extra)
|
||||
{
|
||||
case eAlphabetType::CHINESE: return io.Fonts->GetGlyphRangesChineseFull();
|
||||
case eAlphabetType::CYRILLIC: return io.Fonts->GetGlyphRangesCyrillic();
|
||||
case eAlphabetType::JAPANESE: return io.Fonts->GetGlyphRangesJapanese();
|
||||
case eAlphabetType::KOREAN: return io.Fonts->GetGlyphRangesKorean();
|
||||
|
||||
default:
|
||||
case eAlphabetType::LATIN: return io.Fonts->GetGlyphRangesDefault();
|
||||
}
|
||||
}
|
||||
}
|
39
src/renderer/font_mgr.hpp
Normal file
39
src/renderer/font_mgr.hpp
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include "alphabet_types.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class font_mgr final
|
||||
{
|
||||
private:
|
||||
const std::vector<std::pair<float, ImFont**>> m_extra_font_sizes;
|
||||
// each alphabet type should have a couple of fallback fonts
|
||||
const std::unordered_map<eAlphabetType, std::vector<std::string>> m_fonts;
|
||||
// extra alphabet types that may be required to be added to the atlas
|
||||
eAlphabetType m_require_extra;
|
||||
// prevent crashes when doing thread unsafe actions
|
||||
std::mutex m_update_lock;
|
||||
|
||||
public:
|
||||
font_mgr(std::vector<std::pair<float, ImFont**>> extra_font_sizes = {{28.f, &g.window.font_title},
|
||||
{24.f, &g.window.font_sub_title},
|
||||
{18.f, &g.window.font_small}});
|
||||
virtual ~font_mgr() = default;
|
||||
|
||||
bool can_use();
|
||||
void release_use();
|
||||
|
||||
/**
|
||||
* @brief Updates the required underlying glyph ranges to be loaded into the font atlas. Fonts will be updated on the next frame.
|
||||
*
|
||||
* @param type
|
||||
*/
|
||||
void update_required_alphabet_type(eAlphabetType type);
|
||||
|
||||
private:
|
||||
void rebuild();
|
||||
|
||||
file get_available_font_file_for_alphabet_type();
|
||||
const ImWchar* get_imgui_alphabet_type();
|
||||
};
|
||||
}
|
133
src/renderer/renderer.cpp
Normal file
133
src/renderer/renderer.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
#include "renderer.hpp"
|
||||
|
||||
#include "file_manager.hpp"
|
||||
#include "fonts/fonts.hpp"
|
||||
#include "gui.hpp"
|
||||
#include "pointers.hpp"
|
||||
|
||||
#include <backends/imgui_impl_dx11.h>
|
||||
#include <backends/imgui_impl_win32.h>
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
|
||||
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
namespace big
|
||||
{
|
||||
renderer::renderer() :
|
||||
m_font_mgr()
|
||||
{
|
||||
}
|
||||
|
||||
bool renderer::init()
|
||||
{
|
||||
if (!g_pointers->m_gta.m_swapchain || !*g_pointers->m_gta.m_swapchain)
|
||||
{
|
||||
LOG(FATAL) << "Invalid swapchain ptr";
|
||||
|
||||
return false;
|
||||
}
|
||||
m_dxgi_swapchain = *g_pointers->m_gta.m_swapchain;
|
||||
|
||||
if (m_dxgi_swapchain->GetDevice(__uuidof(ID3D11Device), reinterpret_cast<void**>(&m_d3d_device)) < 0)
|
||||
{
|
||||
LOG(FATAL) << "Failed to get D3D device.";
|
||||
|
||||
return false;
|
||||
}
|
||||
m_d3d_device->GetImmediateContext(&m_d3d_device_context);
|
||||
|
||||
auto file_path = g_file_manager.get_project_file("./imgui.ini").get_path();
|
||||
|
||||
ImGuiContext* ctx = ImGui::CreateContext();
|
||||
|
||||
static std::string path = file_path.make_preferred().string();
|
||||
ctx->IO.IniFilename = path.c_str();
|
||||
|
||||
ImGui_ImplDX11_Init(m_d3d_device, m_d3d_device_context);
|
||||
ImGui_ImplWin32_Init(g_pointers->m_hwnd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void renderer::destroy()
|
||||
{
|
||||
ImGui_ImplWin32_Shutdown();
|
||||
ImGui_ImplDX11_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
bool renderer::add_dx_callback(dx_callback callback, uint32_t priority)
|
||||
{
|
||||
if (!m_dx_callbacks.insert({priority, callback}).second)
|
||||
{
|
||||
LOG(WARNING) << "Duplicate priority given on DX Callback!";
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void renderer::add_wndproc_callback(wndproc_callback callback)
|
||||
{
|
||||
m_wndproc_callbacks.emplace_back(callback);
|
||||
}
|
||||
|
||||
void renderer::on_present()
|
||||
{
|
||||
if (m_font_mgr.can_use())
|
||||
{
|
||||
new_frame();
|
||||
for (const auto& cb : m_dx_callbacks | std::views::values)
|
||||
cb();
|
||||
end_frame();
|
||||
|
||||
m_font_mgr.release_use();
|
||||
}
|
||||
}
|
||||
|
||||
void renderer::rescale(float rel_size)
|
||||
{
|
||||
pre_reset();
|
||||
g_gui->restore_default_style();
|
||||
|
||||
if (rel_size != 1.0f)
|
||||
ImGui::GetStyle().ScaleAllSizes(rel_size);
|
||||
|
||||
ImGui::GetStyle().MouseCursorScale = 1.0f;
|
||||
ImGui::GetIO().FontGlobalScale = rel_size;
|
||||
post_reset();
|
||||
}
|
||||
|
||||
void renderer::pre_reset()
|
||||
{
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
}
|
||||
|
||||
void renderer::post_reset()
|
||||
{
|
||||
ImGui_ImplDX11_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
void renderer::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
for (const auto& cb : m_wndproc_callbacks)
|
||||
cb(hwnd, msg, wparam, lparam);
|
||||
|
||||
|
||||
ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
void renderer::new_frame()
|
||||
{
|
||||
ImGui_ImplDX11_NewFrame();
|
||||
ImGui_ImplWin32_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
|
||||
void renderer::end_frame()
|
||||
{
|
||||
ImGui::Render();
|
||||
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
#include "font_mgr.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
@ -10,7 +11,15 @@ namespace big
|
||||
{
|
||||
public:
|
||||
explicit renderer();
|
||||
~renderer();
|
||||
~renderer() = default;
|
||||
|
||||
font_mgr& get_font_mgr()
|
||||
{
|
||||
return m_font_mgr;
|
||||
}
|
||||
|
||||
bool init();
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief Add a callback function to draw your ImGui content in
|
||||
@ -48,7 +57,9 @@ namespace big
|
||||
|
||||
std::map<uint32_t, dx_callback> m_dx_callbacks;
|
||||
std::vector<wndproc_callback> m_wndproc_callbacks;
|
||||
|
||||
font_mgr m_font_mgr;
|
||||
};
|
||||
|
||||
inline renderer* g_renderer{};
|
||||
inline auto g_renderer = renderer();
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
#include "gui.hpp"
|
||||
#include "network/ChatData.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "renderer/renderer.hpp"
|
||||
#include "util/teleport.hpp"
|
||||
|
||||
namespace big
|
||||
@ -37,7 +37,7 @@ namespace big
|
||||
register_hotkey("waypoint", g.settings.hotkeys.teleport_waypoint, RAGE_JOAAT("waypointtp"));
|
||||
register_hotkey("highlighttp", g.settings.hotkeys.teleport_selected, RAGE_JOAAT("highlighttp"));
|
||||
|
||||
g_renderer->add_wndproc_callback([this](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
g_renderer.add_wndproc_callback([this](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
wndproc(static_cast<eKeyState>(msg), wparam);
|
||||
});
|
||||
|
||||
@ -115,4 +115,4 @@ namespace big
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "renderer/alphabet_types.hpp"
|
||||
#include "translation_entry.hpp"
|
||||
|
||||
namespace big
|
||||
@ -7,12 +8,13 @@ namespace big
|
||||
{
|
||||
public:
|
||||
int version = -1;
|
||||
eAlphabetType alphabet_type = eAlphabetType::LATIN;
|
||||
std::string selected_language;
|
||||
std::string fallback_default_language;
|
||||
std::map<std::string, translation_entry> fallback_languages;
|
||||
bool default_language_set = false;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(local_index, version, selected_language, fallback_default_language, fallback_languages, default_language_set)
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(local_index, version, alphabet_type, selected_language, fallback_default_language, fallback_languages, default_language_set)
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -12,4 +12,4 @@ namespace big
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(remote_index, version, default_lang, translations)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,16 @@
|
||||
#pragma once
|
||||
#include "renderer/alphabet_types.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class translation_entry
|
||||
{
|
||||
public:
|
||||
// default to latin as most languages make use of it
|
||||
eAlphabetType alphabet_type = eAlphabetType::LATIN;
|
||||
std::string name;
|
||||
std::string file;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(translation_entry, name, file)
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(translation_entry, alphabet_type, name, file)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "file_manager.hpp"
|
||||
#include "http_client/http_client.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "renderer/renderer.hpp"
|
||||
#include "thread_pool.hpp"
|
||||
|
||||
namespace big
|
||||
@ -140,6 +141,10 @@ namespace big
|
||||
}
|
||||
}
|
||||
|
||||
// local index is saved below so this is prime location to update a value and be sure to have it persisted!
|
||||
m_local_index.alphabet_type = m_remote_index.translations[m_local_index.selected_language].alphabet_type;
|
||||
g_renderer.get_font_mgr().update_required_alphabet_type(m_local_index.alphabet_type);
|
||||
|
||||
save_local_index();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "gui.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "renderer/renderer.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
namespace big
|
||||
@ -8,7 +8,7 @@ namespace big
|
||||
{
|
||||
components::sub_title("SETTINGS_UI_SCALE"_T);
|
||||
if (ImGui::SliderFloat("##gui-scale", &g.window.gui_scale, 0.75f, 1.5f, "%.2f"))
|
||||
g_renderer->rescale(g.window.gui_scale);
|
||||
g_renderer.rescale(g.window.gui_scale);
|
||||
|
||||
components::sub_title("SETTINGS_UI_COLOR"_T);
|
||||
static ImVec4 col_gui = ImGui::ColorConvertU32ToFloat4(g.window.background_color);
|
||||
@ -86,4 +86,4 @@ namespace big
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user