diff --git a/.gitignore b/.gitignore
index b7e11e554..30c2caeb2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,7 +11,6 @@ Source/*.Gen.*
Source/*.csproj
/Package_*/
!Source/Engine/Debug
-/Source/Platforms/Editor/Linux/Mono/etc/mono/registry
PackageEditor_Cert.command
PackageEditor_Cert.bat
PackagePlatforms_Cert.bat
diff --git a/Content/Editor/Fonts/NotoSansSC-Regular.flax b/Content/Editor/Fonts/NotoSansSC-Regular.flax
new file mode 100644
index 000000000..39964e6c5
--- /dev/null
+++ b/Content/Editor/Fonts/NotoSansSC-Regular.flax
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:19fa43eb7b31ee3936348b1f271c464c79d7020a21d33e3cdbe54f98c3b14304
+size 10560899
diff --git a/Source/Editor/EditorAssets.cs b/Source/Editor/EditorAssets.cs
index eb2f21356..8b2049ebd 100644
--- a/Source/Editor/EditorAssets.cs
+++ b/Source/Editor/EditorAssets.cs
@@ -54,6 +54,11 @@ namespace FlaxEditor
///
public static string PrimaryFont = "Editor/Fonts/Roboto-Regular";
+ ///
+ /// The secondary (fallback) font to use for missing characters rendering (CJK - Chinese/Japanese/Korean characters).
+ ///
+ public static string FallbackFont = "Editor/Fonts/NotoSansSC-Regular";
+
///
/// The Inconsolata Regular font.
///
diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs
index ca45ec6f5..3e2bb63bd 100644
--- a/Source/Editor/GUI/Row.cs
+++ b/Source/Editor/GUI/Row.cs
@@ -43,8 +43,9 @@ namespace FlaxEditor.GUI
{
Depth = -1;
- if (Height < Style.Current.FontMedium.Height)
- Height = Style.Current.FontMedium.Height + 4;
+ var fontHeight = Style.Current.FontMedium.Height;
+ if (Height < fontHeight)
+ Height = fontHeight + 4;
}
///
diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs
index 95a273f19..6180b5a66 100644
--- a/Source/Editor/Options/InterfaceOptions.cs
+++ b/Source/Editor/Options/InterfaceOptions.cs
@@ -172,9 +172,9 @@ namespace FlaxEditor.Options
set
{
if (value == null)
- _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont), 10);
+ _outputLogFont = new FontReference(ConsoleFont, 10);
else if (!value.Font)
- _outputLogFont.Font = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont);
+ _outputLogFont.Font = ConsoleFont;
else
_outputLogFont = value;
}
@@ -237,11 +237,19 @@ namespace FlaxEditor.Options
public int NumberOfGameClientsToLaunch = 1;
private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont);
+ private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont);
+
private FontReference _titleFont = new FontReference(DefaultFont, 18);
private FontReference _largeFont = new FontReference(DefaultFont, 14);
private FontReference _mediumFont = new FontReference(DefaultFont, 9);
private FontReference _smallFont = new FontReference(DefaultFont, 9);
- private FontReference _outputLogFont = new FontReference(FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont), 10);
+ private FontReference _outputLogFont = new FontReference(ConsoleFont, 10);
+
+ ///
+ /// The list of fallback fonts to use when main text font is missing certain characters. Empty to use fonts from GraphicsSettings.
+ ///
+ [EditorDisplay("Fonts"), EditorOrder(650)]
+ public FontAsset[] FallbackFonts = [FlaxEngine.Content.LoadAsyncInternal(EditorAssets.FallbackFont)];
///
/// Gets or sets the title font for editor UI.
diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs
index 1137e4c37..bb90be6d9 100644
--- a/Source/Editor/Options/OptionsModule.cs
+++ b/Source/Editor/Options/OptionsModule.cs
@@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
+using FlaxEditor.Content.Settings;
using FlaxEditor.Modules;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -217,12 +219,18 @@ namespace FlaxEditor.Options
if (styleName == ThemeOptions.LightDefault)
{
Style.Current = CreateLightStyle();
- }
+ }
else
{
Style.Current = CreateDefaultStyle();
}
}
+
+ // Set fallback fonts
+ var fallbackFonts = Options.Interface.FallbackFonts;
+ if (fallbackFonts == null || fallbackFonts.Length == 0 || fallbackFonts.All(x => x == null))
+ fallbackFonts = GameSettings.Load().FallbackFonts;
+ Font.FallbackFonts = fallbackFonts;
}
///
diff --git a/Source/Editor/Windows/Assets/FontWindow.cs b/Source/Editor/Windows/Assets/FontWindow.cs
index ff4135165..bc91ca5e2 100644
--- a/Source/Editor/Windows/Assets/FontWindow.cs
+++ b/Source/Editor/Windows/Assets/FontWindow.cs
@@ -144,7 +144,7 @@ namespace FlaxEditor.Windows.Assets
protected override void OnAssetLinked()
{
Asset.WaitForLoaded();
- _textPreview.Font = new FontReference(Asset.CreateFont(30));
+ _textPreview.Font = new FontReference(Asset, 30);
_inputText.Text = string.Format("This is a sample text using font {0}.", Asset.FamilyName);
var options = Asset.Options;
_proxy.Set(ref options);
diff --git a/Source/Engine/Core/Config/GameSettings.cs b/Source/Engine/Core/Config/GameSettings.cs
index 40d9f5dbc..76c968968 100644
--- a/Source/Engine/Core/Config/GameSettings.cs
+++ b/Source/Engine/Core/Config/GameSettings.cs
@@ -119,7 +119,7 @@ namespace FlaxEditor.Content.Settings
///
/// The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).
///
- [EditorOrder(1100), EditorDisplay("Other Settings"), Tooltip("The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).")]
+ [EditorOrder(1500), EditorDisplay("Other Settings"), Tooltip("The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair).")]
public Dictionary CustomSettings;
#if FLAX_EDITOR || PLATFORM_WINDOWS
diff --git a/Source/Engine/Core/Config/GraphicsSettings.h b/Source/Engine/Core/Config/GraphicsSettings.h
index 81d80cb35..b639b9e64 100644
--- a/Source/Engine/Core/Config/GraphicsSettings.h
+++ b/Source/Engine/Core/Config/GraphicsSettings.h
@@ -6,6 +6,8 @@
#include "Engine/Graphics/Enums.h"
#include "Engine/Graphics/PostProcessSettings.h"
+class FontAsset;
+
///
/// Graphics rendering settings.
///
@@ -13,6 +15,7 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings", NoConstructor) class
{
API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE_MINIMAL(GraphicsSettings);
+
public:
///
/// Enables rendering synchronization with the refresh rate of the display device to avoid "tearing" artifacts.
@@ -118,6 +121,12 @@ public:
API_FIELD(Attributes="EditorOrder(10000), EditorDisplay(\"Post Process Settings\", EditorDisplayAttribute.InlineStyle)")
PostProcessSettings PostProcessSettings;
+ ///
+ /// The list of fallback fonts used for text rendering. Ignored if empty.
+ ///
+ API_FIELD(Attributes="EditorOrder(5000), EditorDisplay(\"Text\")")
+ Array> FallbackFonts;
+
private:
///
/// Renamed UeeHDRProbes into UseHDRProbes
diff --git a/Source/Engine/Graphics/Graphics.cpp b/Source/Engine/Graphics/Graphics.cpp
index f91c58cea..a9bfef470 100644
--- a/Source/Engine/Graphics/Graphics.cpp
+++ b/Source/Engine/Graphics/Graphics.cpp
@@ -8,6 +8,7 @@
#include "Engine/Core/Config/GraphicsSettings.h"
#include "Engine/Engine/CommandLine.h"
#include "Engine/Engine/EngineService.h"
+#include "Engine/Render2D/Font.h"
bool Graphics::UseVSync = false;
Quality Graphics::AAQuality = Quality::Medium;
@@ -69,6 +70,9 @@ void GraphicsSettings::Apply()
Graphics::GIQuality = GIQuality;
Graphics::PostProcessSettings = ::PostProcessSettings();
Graphics::PostProcessSettings.BlendWith(PostProcessSettings, 1.0f);
+#if !USE_EDITOR // OptionsModule handles fallback fonts in Editor
+ Font::FallbackFonts = FallbackFonts;
+#endif
}
void Graphics::DisposeDevice()
diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp
index 90423f5ce..71eca1f59 100644
--- a/Source/Engine/Render2D/Font.cpp
+++ b/Source/Engine/Render2D/Font.cpp
@@ -7,6 +7,8 @@
#include "Engine/Threading/Threading.h"
#include "IncludeFreeType.h"
+Array, HeapAllocation> Font::FallbackFonts;
+
Font::Font(FontAsset* parentAsset, float size)
: ManagedScriptingObject(SpawnParams(Guid::New(), Font::TypeInitializer))
, _asset(parentAsset)
@@ -32,7 +34,7 @@ Font::~Font()
_asset->_fonts.Remove(this);
}
-void Font::GetCharacter(Char c, FontCharacterEntry& result)
+void Font::GetCharacter(Char c, FontCharacterEntry& result, bool enableFallback)
{
// Try to get the character or cache it if cannot be found
if (!_characters.TryGet(c, result))
@@ -44,6 +46,20 @@ void Font::GetCharacter(Char c, FontCharacterEntry& result)
if (_characters.TryGet(c, result))
return;
+ // Try to use fallback font if character is missing
+ if (enableFallback && !_asset->ContainsChar(c))
+ {
+ for (int32 fallbackIndex = 0; fallbackIndex < FallbackFonts.Count(); fallbackIndex++)
+ {
+ FontAsset* fallbackFont = FallbackFonts.Get()[fallbackIndex].Get();
+ if (fallbackFont && fallbackFont->ContainsChar(c))
+ {
+ fallbackFont->CreateFont(GetSize())->GetCharacter(c, result, enableFallback);
+ return;
+ }
+ }
+ }
+
// Create character cache
FontManager::AddNewEntry(this, c, result);
@@ -87,7 +103,7 @@ void Font::CacheText(const StringView& text)
FontCharacterEntry entry;
for (int32 i = 0; i < text.Length(); i++)
{
- GetCharacter(text[i], entry);
+ GetCharacter(text[i], entry, false);
}
}
@@ -104,12 +120,14 @@ void Font::Invalidate()
void Font::ProcessText(const StringView& text, Array& outputLines, const TextLayoutOptions& layout)
{
+ int32 textLength = text.Length();
+ if (textLength == 0)
+ return;
float cursorX = 0;
int32 kerning;
FontLineCache tmpLine;
FontCharacterEntry entry;
FontCharacterEntry previous;
- int32 textLength = text.Length();
float scale = layout.Scale / FontManager::FontScale;
float boundsWidth = layout.Bounds.GetWidth();
float baseLinesDistance = static_cast(_height) * layout.BaseLinesGapScale * scale;
@@ -157,7 +175,7 @@ void Font::ProcessText(const StringView& text, Array& outputLines
// Get kerning
if (!isWhitespace && previous.IsValid)
{
- kerning = GetKerning(previous.Character, entry.Character);
+ kerning = entry.Font->GetKerning(previous.Character, entry.Character);
}
else
{
@@ -178,8 +196,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines
if (lastWrapCharIndex != INVALID_INDEX)
{
// Skip moving twice for the same character
- int32 lastLineLasCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000;
- if (lastLineLasCharIndex == lastWrapCharIndex || lastLineLasCharIndex == lastWrapCharIndex - 1 || lastLineLasCharIndex == lastWrapCharIndex - 2)
+ int32 lastLineLastCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000;
+ if (lastLineLastCharIndex == lastWrapCharIndex || lastLineLastCharIndex == lastWrapCharIndex - 1 || lastLineLastCharIndex == lastWrapCharIndex - 2)
{
currentIndex = nextCharIndex;
lastMoveLine = moveLine;
@@ -238,7 +256,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines
lastMoveLine = moveLine;
}
- if (textLength != 0 && (tmpLine.LastCharIndex >= tmpLine.FirstCharIndex || text[textLength - 1] == '\n'))
+ // Check if an additional line should be created
+ if (tmpLine.LastCharIndex >= tmpLine.FirstCharIndex || text[textLength - 1] == '\n')
{
// Add line
tmpLine.Size.X = cursorX;
@@ -341,7 +360,7 @@ int32 Font::HitTestText(const StringView& text, const Float2& location, const Te
// Apply kerning
if (!isWhitespace && previous.IsValid)
{
- x += GetKerning(previous.Character, entry.Character);
+ x += entry.Font->GetKerning(previous.Character, entry.Character);
}
previous = entry;
@@ -415,7 +434,7 @@ Float2 Font::GetCharPosition(const StringView& text, int32 index, const TextLayo
// Apply kerning
if (!isWhitespace && previous.IsValid)
{
- x += GetKerning(previous.Character, entry.Character);
+ x += entry.Font->GetKerning(previous.Character, entry.Character);
}
previous = entry;
diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h
index 90f723cd8..cd92103cb 100644
--- a/Source/Engine/Render2D/Font.h
+++ b/Source/Engine/Render2D/Font.h
@@ -20,7 +20,7 @@ struct FontTextureAtlasSlot;
///
API_STRUCT(NoDefault) struct TextRange
{
-DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange);
+ DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange);
///
/// The start index (inclusive).
@@ -35,7 +35,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange);
///
/// Gets the range length.
///
- int32 Length() const
+ FORCE_INLINE int32 Length() const
{
return EndIndex - StartIndex;
}
@@ -43,7 +43,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange);
///
/// Gets a value indicating whether range is empty.
///
- bool IsEmpty() const
+ FORCE_INLINE bool IsEmpty() const
{
return (EndIndex - StartIndex) <= 0;
}
@@ -53,7 +53,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange);
///
/// The index.
/// true if range contains the specified character index; otherwise, false.
- bool Contains(int32 index) const
+ FORCE_INLINE bool Contains(int32 index) const
{
return index >= StartIndex && index < EndIndex;
}
@@ -90,7 +90,7 @@ struct TIsPODType
///
API_STRUCT(NoDefault) struct FontLineCache
{
-DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache);
+ DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache);
///
/// The root position of the line (upper left corner).
@@ -108,7 +108,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache);
API_FIELD() int32 FirstCharIndex;
///
- /// The last character index (from the input text).
+ /// The last character index (from the input text), inclusive.
///
API_FIELD() int32 LastCharIndex;
};
@@ -154,7 +154,7 @@ struct TIsPODType
///
API_STRUCT(NoDefault) struct FontCharacterEntry
{
-DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry);
+ DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry);
///
/// The character represented by this entry.
@@ -210,6 +210,11 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry);
/// The slot in texture atlas, containing the pixel data of the glyph.
///
API_FIELD() const FontTextureAtlasSlot* Slot;
+
+ ///
+ /// The owner font.
+ ///
+ API_FIELD() const class Font* Font;
};
template<>
@@ -223,10 +228,10 @@ struct TIsPODType
///
API_CLASS(Sealed, NoSpawn) class FLAXENGINE_API Font : public ManagedScriptingObject
{
-DECLARE_SCRIPTING_TYPE_NO_SPAWN(Font);
+ DECLARE_SCRIPTING_TYPE_NO_SPAWN(Font);
friend FontAsset;
-private:
+private:
FontAsset* _asset;
float _size;
int32 _height;
@@ -238,7 +243,6 @@ private:
mutable Dictionary _kerningTable;
public:
-
///
/// Initializes a new instance of the class.
///
@@ -252,6 +256,10 @@ public:
~Font();
public:
+ ///
+ /// The active fallback fonts.
+ ///
+ API_FIELD() static Array, HeapAllocation> FallbackFonts;
///
/// Gets parent font asset that contains font family used by this font.
@@ -302,13 +310,13 @@ public:
}
public:
-
///
/// Gets character entry.
///
/// The character.
/// The output character entry.
- void GetCharacter(Char c, FontCharacterEntry& result);
+ /// True if fallback to secondary font when the primary font doesn't contains this character.
+ void GetCharacter(Char c, FontCharacterEntry& result, bool enableFallback = true);
///
/// Gets the kerning amount for a pair of characters.
@@ -330,7 +338,6 @@ public:
API_FUNCTION() void Invalidate();
public:
-
///
/// Processes text to get cached lines for rendering.
///
@@ -524,7 +531,6 @@ public:
void FlushFaceSize() const;
public:
-
// [Object]
String ToString() const override;
};
diff --git a/Source/Engine/Render2D/FontAsset.cpp b/Source/Engine/Render2D/FontAsset.cpp
index a477e5f4d..53fbdc930 100644
--- a/Source/Engine/Render2D/FontAsset.cpp
+++ b/Source/Engine/Render2D/FontAsset.cpp
@@ -199,14 +199,29 @@ bool FontAsset::Save(const StringView& path)
#endif
+bool FontAsset::ContainsChar(Char c) const
+{
+ return FT_Get_Char_Index(_face, c) > 0;
+}
+
void FontAsset::Invalidate()
{
ScopeLock lock(Locker);
-
for (auto font : _fonts)
- {
font->Invalidate();
- }
+}
+
+uint64 FontAsset::GetMemoryUsage() const
+{
+ Locker.Lock();
+ uint64 result = BinaryAsset::GetMemoryUsage();
+ result += sizeof(FontAsset) - sizeof(BinaryAsset);
+ result += sizeof(FT_FaceRec);
+ result += _fontFile.Length();
+ for (auto font : _fonts)
+ result += sizeof(Font);
+ Locker.Unlock();
+ return result;
}
bool FontAsset::init(AssetInitData& initData)
diff --git a/Source/Engine/Render2D/FontAsset.h b/Source/Engine/Render2D/FontAsset.h
index 4dea84a5b..a93276bf1 100644
--- a/Source/Engine/Render2D/FontAsset.h
+++ b/Source/Engine/Render2D/FontAsset.h
@@ -93,6 +93,7 @@ API_CLASS(NoSpawn) class FLAXENGINE_API FontAsset : public BinaryAsset
{
DECLARE_BINARY_ASSET_HEADER(FontAsset, 3);
friend Font;
+
private:
FT_Face _face;
FontOptions _options;
@@ -174,11 +175,22 @@ public:
API_FUNCTION() bool Save(const StringView& path = StringView::Empty);
#endif
+ ///
+ /// Check if the font contains the glyph of a char.
+ ///
+ /// The char to test.
+ /// True if the font contains the glyph of the char, otherwise false.
+ API_FUNCTION() bool ContainsChar(Char c) const;
+
///
/// Invalidates all cached dynamic font atlases using this font. Can be used to reload font characters after changing font asset options.
///
API_FUNCTION() void Invalidate();
+public:
+ // [BinaryAsset]
+ uint64 GetMemoryUsage() const override;
+
protected:
// [BinaryAsset]
bool init(AssetInitData& initData) override;
diff --git a/Source/Engine/Render2D/FontManager.cpp b/Source/Engine/Render2D/FontManager.cpp
index bb7edf974..2e1ba8c6b 100644
--- a/Source/Engine/Render2D/FontManager.cpp
+++ b/Source/Engine/Render2D/FontManager.cpp
@@ -27,7 +27,6 @@ using namespace FontManagerImpl;
class FontManagerService : public EngineService
{
public:
-
FontManagerService()
: EngineService(TEXT("Font Manager"), -700)
{
@@ -155,6 +154,12 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry)
// Get the index to the glyph in the font face
const FT_UInt glyphIndex = FT_Get_Char_Index(face, c);
+#if !BUILD_RELEASE
+ if (glyphIndex == 0)
+ {
+ LOG(Warning, "Font `{}` doesn't contain character `\\u{:x}`, consider choosing another font. ", String(face->family_name), c);
+ }
+#endif
// Load the glyph
const FT_Error error = FT_Load_Glyph(face, glyphIndex, glyphFlags);
@@ -284,6 +289,7 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry)
entry.UVSize.X = static_cast(slot->Width - 2 * padding);
entry.UVSize.Y = static_cast(slot->Height - 2 * padding);
entry.Slot = slot;
+ entry.Font = font;
return false;
}
diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp
index b56c3e1a2..34cb2c693 100644
--- a/Source/Engine/Render2D/Render2D.cpp
+++ b/Source/Engine/Render2D/Render2D.cpp
@@ -27,11 +27,11 @@
#if USE_EDITOR
#define RENDER2D_CHECK_RENDERING_STATE \
- if (!Render2D::IsRendering()) \
- { \
- LOG(Error, "Calling Render2D is only valid during rendering."); \
- return; \
- }
+ if (!Render2D::IsRendering()) \
+ { \
+ LOG(Error, "Calling Render2D is only valid during rendering."); \
+ return; \
+ }
#else
#define RENDER2D_CHECK_RENDERING_STATE
#endif
@@ -180,7 +180,7 @@ struct ClipMask
Rectangle Bounds;
};
-Render2D::RenderingFeatures Render2D::Features = RenderingFeatures::VertexSnapping;
+Render2D::RenderingFeatures Render2D::Features = RenderingFeatures::VertexSnapping | RenderingFeatures::FallbackFonts;
namespace
{
@@ -1137,8 +1137,8 @@ void DrawBatch(int32 startIndex, int32 count)
}
// Draw
- Context->BindVB(ToSpan(&vb, 1)); // TODO: reduce bindings frequency
- Context->BindIB(ib); // TODO: reduce bindings frequency
+ Context->BindVB(ToSpan(&vb, 1));
+ Context->BindIB(ib);
Context->DrawIndexed(countIb, 0, d.StartIB);
}
@@ -1159,6 +1159,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
FontCharacterEntry previous;
int32 kerning;
float scale = 1.0f / FontManager::FontScale;
+ const bool enableFallbackFonts = EnumHasAllFlags(Features, RenderingFeatures::FallbackFonts);
// Render all characters
FontCharacterEntry entry;
@@ -1183,7 +1184,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
if (currentChar != '\n')
{
// Get character entry
- font->GetCharacter(currentChar, entry);
+ font->GetCharacter(currentChar, entry, enableFallbackFonts);
// Check if need to select/change font atlas (since characters even in the same font may be located in different atlases)
if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex)
@@ -1210,7 +1211,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
// Get kerning
if (!isWhitespace && previous.IsValid)
{
- kerning = font->GetKerning(previous.Character, entry.Character);
+ kerning = entry.Font->GetKerning(previous.Character, entry.Character);
}
else
{
@@ -1273,6 +1274,7 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
FontCharacterEntry previous;
int32 kerning;
float scale = layout.Scale / FontManager::FontScale;
+ const bool enableFallbackFonts = EnumHasAllFlags(Features, RenderingFeatures::FallbackFonts);
// Process text to get lines
Lines.Clear();
@@ -1299,10 +1301,14 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
// Render all characters from the line
for (int32 charIndex = line.FirstCharIndex; charIndex <= line.LastCharIndex; charIndex++)
{
- const Char c = text[charIndex];
- if (c != '\n')
+ // Cache current character
+ const Char currentChar = text[charIndex];
+
+ // Check if it isn't a newline character
+ if (currentChar != '\n')
{
- font->GetCharacter(c, entry);
+ // Get character entry
+ font->GetCharacter(currentChar, entry, enableFallbackFonts);
// Check if need to select/change font atlas (since characters even in the same font may be located in different atlases)
if (fontAtlas == nullptr || entry.TextureIndex != fontAtlasIndex)
@@ -1324,10 +1330,10 @@ void Render2D::DrawText(Font* font, const StringView& text, const Color& color,
}
// Get kerning
- const bool isWhitespace = StringUtils::IsWhitespace(c);
+ const bool isWhitespace = StringUtils::IsWhitespace(currentChar);
if (!isWhitespace && previous.IsValid)
{
- kerning = font->GetKerning(previous.Character, entry.Character);
+ kerning = entry.Font->GetKerning(previous.Character, entry.Character);
}
else
{
@@ -1931,7 +1937,7 @@ void Render2D::DrawBlur(const Rectangle& rect, float blurStrength)
void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span& vertices, const Span& uvs)
{
RENDER2D_CHECK_RENDERING_STATE;
- CHECK(vertices.Length() == uvs.Length())
+ CHECK(vertices.Length() == uvs.Length());
Render2DDrawCall& drawCall = DrawCalls.AddOne();
drawCall.Type = DrawCallType::FillTexture;
@@ -1977,7 +1983,7 @@ void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span& indices,
drawCall.StartIB = IBIndex;
drawCall.CountIB = indices.Length();
drawCall.AsTexture.Ptr = t;
-
+
for (int32 i = 0; i < indices.Length();)
{
const uint16 i0 = indices.Get()[i++];
diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h
index 2b890ced9..5e5e952dc 100644
--- a/Source/Engine/Render2D/Render2D.h
+++ b/Source/Engine/Render2D/Render2D.h
@@ -44,6 +44,11 @@ API_CLASS(Static) class FLAXENGINE_API Render2D
/// Enables automatic geometry vertices snapping to integer coordinates in screen space. Reduces aliasing and sampling artifacts. Might be disabled for 3D projection viewport or for complex UI transformations.
///
VertexSnapping = 1,
+
+ ///
+ /// Enables automatic characters usage from fallback fonts.
+ ///
+ FallbackFonts = 2,
};
struct CustomData
@@ -452,3 +457,5 @@ public:
/// The color.
API_FUNCTION() static void FillTriangle(const Float2& p0, const Float2& p1, const Float2& p2, const Color& color);
};
+
+DECLARE_ENUM_OPERATORS(Render2D::RenderingFeatures);
diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs
index 044c65044..b4351da75 100644
--- a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs
+++ b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs
@@ -280,7 +280,7 @@ namespace FlaxEngine.GUI
foreach (var id in ids)
{
var path = Content.GetEditorAssetPath(id);
- if (!string.IsNullOrEmpty(path) &&
+ if (!string.IsNullOrEmpty(path) &&
string.Equals(name, System.IO.Path.GetFileNameWithoutExtension(path), System.StringComparison.OrdinalIgnoreCase))
{
return Content.LoadAsync(id, type);
diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs
index 55b58634d..35819da79 100644
--- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs
+++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs
@@ -280,13 +280,13 @@ namespace FlaxEngine.GUI
///
[EditorDisplay("Border Style"), EditorOrder(2010), Tooltip("Whether to have a border."), ExpandGroups]
public bool HasBorder { get; set; } = true;
-
+
///
/// Gets or sets the border thickness.
///
[EditorDisplay("Border Style"), EditorOrder(2011), Tooltip("The thickness of the border."), Limit(0)]
public float BorderThickness { get; set; } = 1.0f;
-
+
///
/// Gets or sets the color of the border (Transparent if not used).
///
diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp
index aa49dac45..d190f1123 100644
--- a/Source/Engine/UI/TextRender.cpp
+++ b/Source/Engine/UI/TextRender.cpp
@@ -239,7 +239,7 @@ void TextRender::UpdateLayout()
const bool isWhitespace = StringUtils::IsWhitespace(c);
if (!isWhitespace && previous.IsValid)
{
- kerning = font->GetKerning(previous.Character, entry.Character);
+ kerning = entry.Font->GetKerning(previous.Character, entry.Character);
}
else
{