diff --git a/ogsr_engine/Layers/xrRender/dxFontRender.cpp b/ogsr_engine/Layers/xrRender/dxFontRender.cpp index 3eb76b26ec..a5fcadc238 100644 --- a/ogsr_engine/Layers/xrRender/dxFontRender.cpp +++ b/ogsr_engine/Layers/xrRender/dxFontRender.cpp @@ -42,13 +42,13 @@ void dxFontRender::OnRender(CGameFont& owner) // calculate first-fit int count = 1; - int length = xr_strlen(owner.strings[i].string); + int length = owner.smart_strlen(owner.strings[i].string); while ((i + count) < owner.strings.size()) { - int L = xr_strlen(owner.strings[i + count].string); + int L = owner.smart_strlen(owner.strings[i + count].string); - if ((L + length) < 4096) // MAX_MB_CHARS ?? + if ((L + length) < MAX_MB_CHARS) { count++; length += L; @@ -67,8 +67,11 @@ void dxFontRender::OnRender(CGameFont& owner) for (; i < last; i++) { CGameFont::String& PS = owner.strings[i]; + wide_char wsStr[MAX_MB_CHARS]; - int len = xr_strlen(PS.string); + int len = owner.IsMultibyte() ? mbhMulti2Wide(wsStr, NULL, MAX_MB_CHARS, PS.string) : + + xr_strlen(PS.string); if (len) { @@ -81,7 +84,7 @@ void dxFontRender::OnRender(CGameFont& owner) float fSize = 0; if (PS.align) - fSize = owner.SizeOf_(PS.string); // уже с * owner.GetWidthScale() + fSize = owner.IsMultibyte() ? owner.SizeOf_(wsStr) : owner.SizeOf_(PS.string); switch (PS.align) { @@ -115,7 +118,7 @@ void dxFontRender::OnRender(CGameFont& owner) { Fvector l; - l = owner.GetCharTC((u16)(u8)PS.string[j]); + l = owner.IsMultibyte() ? owner.GetCharTC(wsStr[1 + j]) : owner.GetCharTC((u16)(u8)PS.string[j]); float scw = l.z * g_current_font_scale.x * owner.GetWidthScale(); @@ -144,6 +147,13 @@ void dxFontRender::OnRender(CGameFont& owner) } X += scw * owner.vInterval.x; + + if (owner.IsMultibyte()) + { + X -= 2; + if (IsNeedSpaceCharacter(wsStr[1 + j])) + X += owner.fXStep; + } } } } diff --git a/ogsr_engine/xrGame/ui/UIEditKeyBind.cpp b/ogsr_engine/xrGame/ui/UIEditKeyBind.cpp index 87e98b5421..d459bd1f1e 100644 --- a/ogsr_engine/xrGame/ui/UIEditKeyBind.cpp +++ b/ogsr_engine/xrGame/ui/UIEditKeyBind.cpp @@ -21,6 +21,15 @@ CUIEditKeyBind::~CUIEditKeyBind() { delete_data(m_pAnimation); } u32 cut_string_by_length(CGameFont* pFont, LPCSTR src, LPSTR dst, u32 dst_size, float length) { + if (pFont->IsMultibyte()) + { + u16 nPos = pFont->GetCutLengthPos(length, src); + VERIFY(nPos < dst_size); + strncpy(dst, src, nPos); + dst[nPos] = '\0'; + return nPos; + } + else { float text_len = pFont->SizeOf_(src); UI()->ClientToScreenScaledWidth(text_len); diff --git a/ogsr_engine/xrGame/ui/UILines.cpp b/ogsr_engine/xrGame/ui/UILines.cpp index 57a212ec59..f606623a3a 100644 --- a/ogsr_engine/xrGame/ui/UILines.cpp +++ b/ogsr_engine/xrGame/ui/UILines.cpp @@ -161,11 +161,92 @@ void CUILines::ParseText() line->AddSubLine(&subline); } + BOOL bNewLines = FALSE; + if (uFlags.test(flRecognizeNewLine)) + if (m_pFont->IsMultibyte()) + { + CUILine* ptmp_line = xr_new(); + int vsz = line->m_subLines.size(); + VERIFY(vsz); + for (int i = 0; i < vsz; i++) + { + char* pszTemp = NULL; + const u32 tcolor = line->m_subLines[i].m_color; + char szTempLine[MAX_MB_CHARS], *pszSearch = NULL; + VERIFY(strlen(line->m_subLines[i].m_text.c_str()) < MAX_MB_CHARS); + strcpy_s(szTempLine, line->m_subLines[i].m_text.c_str()); + pszSearch = szTempLine; + while ((pszTemp = strstr(pszSearch, "\\n")) != NULL) + { + bNewLines = TRUE; + *pszTemp = '\0'; + ptmp_line->AddSubLine(pszSearch, tcolor); + pszSearch = pszTemp + 2; + } + ptmp_line->AddSubLine(pszSearch, tcolor); + } + line->Clear(); + xr_free(line); + line = ptmp_line; + } + else + line->ProcessNewLines(); // process "\n" + + if (m_pFont->IsMultibyte()) { - line->ProcessNewLines(); // process "\n" +#define UBUFFER_SIZE 100 + u16 aMarkers[UBUFFER_SIZE]; + CUILine tmp_line; + char szTempLine[MAX_MB_CHARS]; + float fTargetWidth = 1.0f; + UI()->ClientToScreenScaledWidth(fTargetWidth); + VERIFY((m_wndSize.x > 0) && (fTargetWidth > 0)); + fTargetWidth = m_wndSize.x / fTargetWidth; + int vsz = line->m_subLines.size(); + VERIFY(vsz); + if ((vsz > 1) && (!bNewLines)) + { // only colored line, pizdets + for (int i = 0; i < vsz; i++) + { + const char* pszText = line->m_subLines[i].m_text.c_str(); + const u32 tcolor = line->m_subLines[i].m_color; + VERIFY(pszText); + tmp_line.AddSubLine(pszText, tcolor); + } + m_lines.push_back(tmp_line); + tmp_line.Clear(); + } + else + { + for (int i = 0; i < vsz; i++) + { + const char* pszText = line->m_subLines[i].m_text.c_str(); + const u32 tcolor = line->m_subLines[i].m_color; + u16 uFrom = 0, uPartLen = 0; + VERIFY(pszText); + u16 nMarkers = m_pFont->SplitByWidth(aMarkers, UBUFFER_SIZE, fTargetWidth, pszText); + for (u16 j = 0; j < nMarkers; j++) + { + uPartLen = aMarkers[j] - uFrom; + VERIFY((uPartLen > 0) && (uPartLen < MAX_MB_CHARS)); + strncpy_s(szTempLine, pszText + uFrom, uPartLen); + szTempLine[uPartLen] = '\0'; + tmp_line.AddSubLine(szTempLine, tcolor); + m_lines.push_back(tmp_line); + tmp_line.Clear(); + uFrom += uPartLen; + } + strncpy_s(szTempLine, pszText + uFrom, MAX_MB_CHARS); + tmp_line.AddSubLine(szTempLine, tcolor); + m_lines.push_back(tmp_line); + tmp_line.Clear(); + } + } } - + else + { + float max_width = m_wndSize.x; CUILine tmp_line; @@ -228,6 +309,8 @@ void CUILines::ParseText() } } + } + xr_delete(line); uFlags.set(flNeedReparse, FALSE); } diff --git a/ogsr_engine/xr_3da/GameFont.cpp b/ogsr_engine/xr_3da/GameFont.cpp index 32e10295d5..aa9dbfff22 100644 --- a/ogsr_engine/xr_3da/GameFont.cpp +++ b/ogsr_engine/xr_3da/GameFont.cpp @@ -84,7 +84,56 @@ void CGameFont::Initialize(LPCSTR cShader, LPCSTR cTextureName) nNumChars = 0x100; TCMap = (Fvector*)xr_realloc((void*)TCMap, nNumChars * sizeof(Fvector)); - if (ini->section_exist("symbol_coords")) + if (ini->section_exist("mb_symbol_coords")) + { + nNumChars = 0x10000; + TCMap = (Fvector*)xr_realloc((void*)TCMap, nNumChars * sizeof(Fvector)); + uFlags |= fsMultibyte; + fHeight = ini->r_float("mb_symbol_coords", "height"); + + fXStep = ceil(fHeight / 2.0f); + + // Searching for the first valid character + + Fvector vFirstValid = {0, 0, 0}; + + if (ini->line_exist("mb_symbol_coords", "09608")) + { + Fvector v = ini->r_fvector3("mb_symbol_coords", "09608"); + vFirstValid.set(v.x, v.y, 1 + v[2] - v[0]); + } + else + for (u32 i = 0; i < nNumChars; i++) + { + xr_sprintf(buf, sizeof(buf), "%05d", i); + if (ini->line_exist("mb_symbol_coords", buf)) + { + Fvector v = ini->r_fvector3("mb_symbol_coords", buf); + vFirstValid.set(v.x, v.y, 1 + v[2] - v[0]); + break; + } + } + + // Filling entire character table + + for (u32 i = 0; i < nNumChars; i++) + { + xr_sprintf(buf, sizeof(buf), "%05d", i); + if (ini->line_exist("mb_symbol_coords", buf)) + { + Fvector v = ini->r_fvector3("mb_symbol_coords", buf); + TCMap[i].set(v.x, v.y, 1 + v[2] - v[0]); + } + else + TCMap[i] = vFirstValid; // "unassigned" unprintable characters + } + + // Special case for space + TCMap[0x0020].set(0, 0, 0); + // Special case for ideographic space + TCMap[0x3000].set(0, 0, 0); + } + else if (ini->section_exist("symbol_coords")) { //float d = READ_IF_EXISTS(ini, r_float, "width_correction", "value", 0.0f); // Это раньше не работало, теперь работает, и чтоб старые кривые конфиги не работали криво, имя параметра переименовано. float d = READ_IF_EXISTS(ini, r_float, "font_width_correction", "value", 0.0f); @@ -156,12 +205,76 @@ void CGameFont::OutSet(float x, float y) void CGameFont::OutSetI(float x, float y) { OutSet(DI2PX(x), DI2PY(y)); } +u32 CGameFont::smart_strlen(const char* S) { return (IsMultibyte() ? mbhMulti2Wide(NULL, NULL, 0, S) : xr_strlen(S)); } + void CGameFont::OnRender() { pFontRender->OnRender(*this); strings.clear(); } +u16 CGameFont::GetCutLengthPos(float fTargetWidth, const char* pszText) +{ + VERIFY(pszText); + + wide_char wsStr[MAX_MB_CHARS], wsPos[MAX_MB_CHARS]; + float fCurWidth = 0.0f, fDelta = 0.0f; + + u16 len = mbhMulti2Wide(wsStr, wsPos, MAX_MB_CHARS, pszText); + u16 i = 1; + + for (; i <= len; i++) + { + fDelta = GetCharTC(wsStr[i]).z - 2; + + if (IsNeedSpaceCharacter(wsStr[i])) + fDelta += fXStep; + + if ((fCurWidth + fDelta) > fTargetWidth) + break; + else + fCurWidth += fDelta; + } + + return wsPos[i - 1]; +} + +u16 CGameFont::SplitByWidth(u16* puBuffer, u16 uBufferSize, float fTargetWidth, const char* pszText) +{ + VERIFY(puBuffer && uBufferSize && pszText); + + wide_char wsStr[MAX_MB_CHARS], wsPos[MAX_MB_CHARS]; + float fCurWidth = 0.0f, fDelta = 0.0f; + u16 nLines = 0; + + u16 len = mbhMulti2Wide(wsStr, wsPos, MAX_MB_CHARS, pszText); + + for (u16 i = 1; i <= len; i++) + { + fDelta = (GetCharTC(wsStr[i]).z * GetWidthScale()) - 2; + + if (IsNeedSpaceCharacter(wsStr[i])) + fDelta += fXStep; + + if (((fCurWidth + fDelta) > fTargetWidth) && // overlength + (!IsBadStartCharacter(wsStr[i])) && // can start with this character + (i < len) && // is not the last character + ((i > 1) && (!IsBadEndCharacter(wsStr[i - 1]))) // && // do not stop the string on a "bad" character + ) + { + fCurWidth = fDelta; + + VERIFY(nLines < uBufferSize); + + puBuffer[nLines++] = wsPos[i - 1]; + } + else + fCurWidth += fDelta; + } + + return nLines; +} + void CGameFont::MasterOut(BOOL bCheckDevice, BOOL bUseCoords, BOOL bScaleCoords, BOOL bUseSkip, float _x, float _y, float _skip, LPCSTR fmt, va_list p) { if (bCheckDevice && (!RDEVICE.b_is_Active)) @@ -207,13 +320,22 @@ void __cdecl CGameFont::OutNext(LPCSTR fmt, ...) { MASTER_OUT(TRUE, FALSE, FALSE void CGameFont::OutSkip(float val) { fCurrentY += val * CurrentHeight_(); } -float CGameFont::SizeOf_(const char cChar) { return (GetCharTC((u16)(u8)(cChar)).z * vInterval.x * GetWidthScale()); } +float CGameFont::SizeOf_(const char cChar) { return (GetCharTC((u16)(u8)(((IsMultibyte() && cChar == ' ')) ? 0 : cChar)).z * vInterval.x * GetWidthScale()); } float CGameFont::SizeOf_(LPCSTR s) { if (!(s && s[0])) return 0; + if (IsMultibyte()) + { + wide_char wsStr[MAX_MB_CHARS]; + + mbhMulti2Wide(wsStr, NULL, MAX_MB_CHARS, s); + + return SizeOf_(wsStr); + } + int len = xr_strlen(s); float X = 0; if (len) @@ -223,6 +345,26 @@ float CGameFont::SizeOf_(LPCSTR s) return (X * vInterval.x) * GetWidthScale(); } +float CGameFont::SizeOf_(const wide_char* wsStr) +{ + if (!(wsStr && wsStr[0])) + return 0; + + unsigned int len = wsStr[0]; + float X = 0.0f, fDelta = 0.0f; + + if (len) + for (unsigned int j = 1; j <= len; j++) + { + fDelta = GetCharTC(wsStr[j]).z - 2; + if (IsNeedSpaceCharacter(wsStr[j])) + fDelta += fXStep; + X += fDelta; + } + + return (X * vInterval.x); +} + float CGameFont::CurrentHeight_() { return fCurrentHeight * vInterval.y * GetHeightScale(); } void CGameFont::SetHeightI(float S) diff --git a/ogsr_engine/xr_3da/GameFont.h b/ogsr_engine/xr_3da/GameFont.h index 7e18cee97a..a3a162fc8a 100644 --- a/ogsr_engine/xr_3da/GameFont.h +++ b/ogsr_engine/xr_3da/GameFont.h @@ -2,6 +2,8 @@ #define GameFontH #pragma once +#include "MbHelpers.h" + #include "../Include/xrRender/FontRender.h" class ENGINE_API CGameFont @@ -57,6 +59,7 @@ class ENGINE_API CGameFont fsGradient = (1 << 0), fsDeviceIndependent = (1 << 1), fsValid = (1 << 2), + fsMultibyte = (1 << 3), fsForceDWORD = u32(-1) }; @@ -86,6 +89,7 @@ class ENGINE_API CGameFont IC void SetAligment(EAligment aligment) { eCurrentAlignment = aligment; } float SizeOf_(LPCSTR s); + float SizeOf_(const wide_char* wsStr); float SizeOf_(const char cChar); // only ANSII float CurrentHeight_(); @@ -97,6 +101,11 @@ class ENGINE_API CGameFont void MasterOut(BOOL bCheckDevice, BOOL bUseCoords, BOOL bScaleCoords, BOOL bUseSkip, float _x, float _y, float _skip, LPCSTR fmt, va_list p); + u32 smart_strlen(const char* S); + BOOL IsMultibyte() { return (uFlags & fsMultibyte); }; + u16 SplitByWidth(u16* puBuffer, u16 uBufferSize, float fTargetWidth, const char* pszText); + u16 GetCutLengthPos(float fTargetWidth, const char* pszText); + void OutI(float _x, float _y, LPCSTR fmt, ...); void Out(float _x, float _y, LPCSTR fmt, ...); void OutNext(LPCSTR fmt, ...); diff --git a/ogsr_engine/xr_3da/MbHelpers.cpp b/ogsr_engine/xr_3da/MbHelpers.cpp new file mode 100644 index 0000000000..5d5de0f013 --- /dev/null +++ b/ogsr_engine/xr_3da/MbHelpers.cpp @@ -0,0 +1,154 @@ +#include "stdafx.h" + +#include "MbHelpers.h" + +#define BITS1_MASK 0x80 // 10000000b +#define BITS2_MASK 0xC0 // 11000000b +#define BITS3_MASK 0xE0 // 11100000b +#define BITS4_MASK 0xF0 // 11110000b + +#define BITS1_EXP 0x00 // 00000000b +#define BITS2_EXP 0x80 // 10000000b +#define BITS3_EXP 0xC0 // 11000000b +#define BITS4_EXP 0xE0 // 11100000b + +// MB_DUMB_CONVERSION - ignore non UTF-8 compliant sequences, as-is conversion +#define MB_DUMB_CONVERSION + +#ifdef MB_DUMB_CONVERSION + +unsigned short int mbhMulti2WideDumb(wide_char* WideStr, wide_char* WidePos, const unsigned short int WideStrSize, const char* MultiStr) +{ + unsigned short int spos = 0; + unsigned short int dpos = 0; + unsigned char b1; + wide_char wc = 0; + + VERIFY(MultiStr); + + if (!MultiStr[0]) + return 0; + + if (WideStr || WidePos) + VERIFY2(((WideStrSize > 0) && (WideStrSize < 0xFFFF)), make_string("'WideStrSize'=%hu", WideStrSize)); + + while ((b1 = MultiStr[spos++]) != 0x00) + { + if (WidePos) + WidePos[dpos] = spos; + + dpos++; + + wc = b1; + + if (WideStr) + { + VERIFY2((dpos < WideStrSize), make_string("S1: '%s',%hu<%hu", MultiStr, dpos, WideStrSize)); + WideStr[dpos] = wc; + } + } + + if (WidePos) + WidePos[dpos] = spos; + + if (WideStr) + { + VERIFY2((dpos < WideStrSize), make_string("S2: '%s',%hu<%hu", MultiStr, dpos, WideStrSize)); + WideStr[dpos + 1] = 0x0000; + } + + if (WideStr) + WideStr[0] = dpos; + + return dpos; +} + +#endif // MB_DUMB_CONVERSION + +ENGINE_API unsigned short int mbhMulti2Wide(wide_char* WideStr, wide_char* WidePos, const unsigned short int WideStrSize, const char* MultiStr) +{ + unsigned short int spos = 0; + unsigned short int dpos = 0; + unsigned char b1, b2, b3; + wide_char wc = 0; + + VERIFY(MultiStr); + + if (!MultiStr[0]) + return 0; + + if (WideStr || WidePos) + VERIFY2(((WideStrSize > 0) && (WideStrSize < 0xFFFF)), make_string("'WideStrSize'=%hu", WideStrSize)); + + while ((b1 = MultiStr[spos]) != 0x00) + { + if (WidePos) + WidePos[dpos] = spos; + + spos++; + + if ((b1 & BITS1_MASK) == BITS1_EXP) + { + wc = b1; + } + else if ((b1 & BITS3_MASK) == BITS3_EXP) + { + b2 = MultiStr[spos++]; +#ifdef MB_DUMB_CONVERSION + if (!(b2 && ((b2 & BITS2_MASK) == BITS2_EXP))) + return mbhMulti2WideDumb(WideStr, WidePos, WideStrSize, MultiStr); +#else // MB_DUMB_CONVERSION + VERIFY2((b2 && ((b2 & BITS2_MASK) == BITS2_EXP)), make_string("B2: '%s',@%hu,[%hc][%hc]", MultiStr, spos, b1, b2)); +#endif // MB_DUMB_CONVERSION + wc = ((b1 & ~BITS3_MASK) << 6) | (b2 & ~BITS2_MASK); + } + else if ((b1 & BITS4_MASK) == BITS4_EXP) + { + b2 = MultiStr[spos++]; +#ifdef MB_DUMB_CONVERSION + if (!(b2 && ((b2 & BITS2_MASK) == BITS2_EXP))) + return mbhMulti2WideDumb(WideStr, WidePos, WideStrSize, MultiStr); +#else // MB_DUMB_CONVERSION + VERIFY2((b2 && ((b2 & BITS2_MASK) == BITS2_EXP)), make_string("B31: '%s',@%hu,[%hc][%hc]", MultiStr, spos, b1, b2)); +#endif // MB_DUMB_CONVERSION + b3 = MultiStr[spos++]; +#ifdef MB_DUMB_CONVERSION + if (!(b3 && ((b3 & BITS2_MASK) == BITS2_EXP))) + return mbhMulti2WideDumb(WideStr, WidePos, WideStrSize, MultiStr); +#else // MB_DUMB_CONVERSION + VERIFY2((b3 && ((b3 & BITS2_MASK) == BITS2_EXP)), make_string("B32: '%s',@%hu,[%hc][%hc][%hc]", MultiStr, spos, b1, b2, b3)); +#endif // MB_DUMB_CONVERSION + wc = ((b1 & ~BITS4_MASK) << 12) | ((b2 & ~BITS2_MASK) << 6) | (b3 & ~BITS2_MASK); + } + else + { +#ifdef MB_DUMB_CONVERSION + return mbhMulti2WideDumb(WideStr, WidePos, WideStrSize, MultiStr); +#else // MB_DUMB_CONVERSION + VERIFY2(0, make_string("B1: '%s',@%hu,[%hc]", MultiStr, spos, b1)); +#endif // MB_DUMB_CONVERSION + } + + dpos++; + + if (WideStr) + { + VERIFY2((dpos < WideStrSize), make_string("S1: '%s',%hu<%hu", MultiStr, dpos, WideStrSize)); + WideStr[dpos] = wc; + } + } + + if (WidePos) + WidePos[dpos] = spos; + + if (WideStr) + { + VERIFY2((dpos < WideStrSize), make_string("S2: '%s',%hu<%hu", MultiStr, dpos, WideStrSize)); + WideStr[dpos + 1] = 0x0000; + } + + if (WideStr) + WideStr[0] = dpos; + + return dpos; +} diff --git a/ogsr_engine/xr_3da/MbHelpers.h b/ogsr_engine/xr_3da/MbHelpers.h new file mode 100644 index 0000000000..e6854156ab --- /dev/null +++ b/ogsr_engine/xr_3da/MbHelpers.h @@ -0,0 +1,50 @@ +#ifndef _MB_HELPERS_H_INCLUDED +#define _MB_HELPERS_H_INCLUDED + +#define MAX_MB_CHARS 4096 + +typedef unsigned short int wide_char; + +ENGINE_API unsigned short int mbhMulti2Wide(wide_char* WideStr, wide_char* WidePos, const unsigned short int WideStrSize, const char* MultiStr); + +__inline BOOL IsNeedSpaceCharacter(wide_char wc) +{ + return ((wc == 0x0020) || + + (wc == 0xFF01) || (wc == 0xFF0C) || + // ( wc == 0xFF0D ) || + (wc == 0xFF0E) || (wc == 0xFF1A) || (wc == 0xFF1B) || (wc == 0xFF1F) || + + (wc == 0x2026) || + + (wc == 0x3002) || (wc == 0x3001)); +} + +__inline BOOL IsBadStartCharacter(wide_char wc) +{ + return (IsNeedSpaceCharacter(wc) || + + (wc == 0x0021) || (wc == 0x002C) || + // ( wc == 0x002D ) || + (wc == 0x002E) || (wc == 0x003A) || (wc == 0x003B) || (wc == 0x003F) || + + (wc == 0x0029) || (wc == 0xFF09)); +} + +__inline BOOL IsBadEndCharacter(wide_char wc) +{ + return ((wc == 0x0028) || + + (wc == 0xFF08) || + + (wc == 0x4E00)); +} + +__inline BOOL IsAlphaCharacter(wide_char wc) +{ + return (((wc >= 0x0030) && (wc <= 0x0039)) || ((wc >= 0x0041) && (wc <= 0x005A)) || ((wc >= 0x0061) && (wc <= 0x007A)) || + + ((wc >= 0xFF10) && (wc <= 0xFF19)) || ((wc >= 0xFF21) && (wc <= 0xFF3A)) || ((wc >= 0xFF41) && (wc <= 0xFF5A))); +} + +#endif // _MB_HELPERS_H_INCLUDED diff --git a/ogsr_engine/xr_3da/XR_3DA.vcxproj b/ogsr_engine/xr_3da/XR_3DA.vcxproj index b974af385e..d0a1a3aa1b 100644 --- a/ogsr_engine/xr_3da/XR_3DA.vcxproj +++ b/ogsr_engine/xr_3da/XR_3DA.vcxproj @@ -439,6 +439,7 @@ + @@ -521,6 +522,7 @@ + diff --git a/ogsr_engine/xr_3da/XR_3DA.vcxproj.filters b/ogsr_engine/xr_3da/XR_3DA.vcxproj.filters index d5717ec46e..14fba60b28 100644 --- a/ogsr_engine/xr_3da/XR_3DA.vcxproj.filters +++ b/ogsr_engine/xr_3da/XR_3DA.vcxproj.filters @@ -237,6 +237,9 @@ Interfaces\Render\Font output + + Interfaces\Render\Font output + Interfaces\Render\Execution & 3D @@ -468,6 +471,9 @@ Interfaces\Render\Font output + + Interfaces\Render\Font output + Interfaces\Render\Execution & 3D