This repository has been archived on 2025-08-02. You can view files and clone it, but cannot push or open issues or pull requests.
Files
Cryville.EEW.Unity/Assets/Cryville.Common/Unity/UI/TMPLocalizedText.cs

152 lines
6.7 KiB
C#

using Cryville.Common.Font;
using Cryville.Culture;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using TMPro;
using UnityEngine;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
using AtlasPopulationMode = TMPro.AtlasPopulationMode;
namespace Cryville.Common.Unity.UI {
[RequireComponent(typeof(TMP_Text))]
public class TMPLocalizedText : MonoBehaviour {
public static Shader DefaultShader;
public static FontMatcher FontMatcher;
public static int MaxFallbackCount = 4;
static readonly Dictionary<CultureInfo, TMP_FontAsset> _cachedFonts = new();
[SerializeField]
Shader m_shader;
public TMP_Text Text { get; private set; }
void Awake() {
Text = GetComponent<TMP_Text>();
}
public void SetText(string text, CultureInfo culture = null) {
Text.text = text;
SetCulture(culture ?? CultureInfo.CurrentCulture);
}
void SetCulture(CultureInfo culture) {
if (FontMatcher == null) return;
string cultureName = culture.Name;
if (string.IsNullOrEmpty(cultureName)) cultureName = CultureInfo.CurrentCulture.Name;
if (!_cachedFonts.TryGetValue(culture, out var font)) {
foreach (var typeface in FontMatcher.MatchLanguage(new LanguageId(cultureName), true)) {
try {
var ifont = CreateFontAsset(typeface.File.FullName, typeface.IndexInFile);
if (m_shader) ifont.material.shader = m_shader;
else if (DefaultShader) ifont.material.shader = DefaultShader;
if (font == null) {
font = ifont;
if (MaxFallbackCount <= 0) break;
}
else {
font.fallbackFontAssetTable ??= new List<TMP_FontAsset>();
font.fallbackFontAssetTable.Add(ifont);
if (font.fallbackFontAssetTable.Count >= MaxFallbackCount) break;
}
}
catch (Exception) { }
}
_cachedFonts.Add(culture, font);
}
Text.font = font;
}
static TMP_FontAsset CreateFontAsset(string path, int index) => CreateFontAsset(path, index, 90, 9, GlyphRenderMode.SDFAA, 1024, 1024, AtlasPopulationMode.Dynamic);
static readonly Lazy<FieldInfo> _f_m_Version = new(() => typeof(TMP_FontAsset).GetField("m_Version", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
static readonly Lazy<PropertyInfo> _p_atlasWidth = new(() => typeof(TMP_FontAsset).GetProperty("atlasWidth", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
static readonly Lazy<PropertyInfo> _p_atlasHeight = new(() => typeof(TMP_FontAsset).GetProperty("atlasHeight", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
static readonly Lazy<PropertyInfo> _p_atlasPadding = new(() => typeof(TMP_FontAsset).GetProperty("atlasPadding", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
static readonly Lazy<PropertyInfo> _p_atlasRenderMode = new(() => typeof(TMP_FontAsset).GetProperty("atlasRenderMode", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
static readonly Lazy<PropertyInfo> _p_freeGlyphRects = new(() => typeof(TMP_FontAsset).GetProperty("freeGlyphRects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
static readonly Lazy<PropertyInfo> _p_usedGlyphRects = new(() => typeof(TMP_FontAsset).GetProperty("usedGlyphRects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
static readonly Lazy<PropertyInfo> _p_ShaderRef_MobileBitmap = new(() => typeof(ShaderUtilities).GetProperty("ShaderRef_MobileBitmap", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
static readonly Lazy<PropertyInfo> _p_ShaderRef_MobileSDF = new(() => typeof(ShaderUtilities).GetProperty("ShaderRef_MobileSDF", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
public static TMP_FontAsset CreateFontAsset(string path, int faceIndex, int samplingPointSize, int atlasPadding, GlyphRenderMode renderMode, int atlasWidth, int atlasHeight, AtlasPopulationMode atlasPopulationMode = AtlasPopulationMode.Dynamic, bool enableMultiAtlasSupport = true) {
// Initialize FontEngine
FontEngine.InitializeFontEngine();
// Load Font Face
if (FontEngine.LoadFontFace(path, samplingPointSize, faceIndex) != FontEngineError.Success) {
Debug.LogWarning("Unable to load font face at path " + path);
return null;
}
// Create new font asset
TMP_FontAsset fontAsset = ScriptableObject.CreateInstance<TMP_FontAsset>();
_f_m_Version.Value.SetValue(fontAsset, "1.1.0");
fontAsset.faceInfo = FontEngine.GetFaceInfo();
fontAsset.atlasPopulationMode = atlasPopulationMode;
_p_atlasWidth.Value.SetValue(fontAsset, atlasWidth);
_p_atlasHeight.Value.SetValue(fontAsset, atlasHeight);
_p_atlasPadding.Value.SetValue(fontAsset, atlasPadding);
_p_atlasRenderMode.Value.SetValue(fontAsset, renderMode);
// Initialize array for the font atlas textures.
fontAsset.atlasTextures = new Texture2D[1];
// Create and add font atlas texture.
var texture = new Texture2D(0, 0, TextureFormat.Alpha8, false);
fontAsset.atlasTextures[0] = texture;
fontAsset.isMultiAtlasTexturesEnabled = enableMultiAtlasSupport;
// Add free rectangle of the size of the texture.
int packingModifier;
if (((int)renderMode & 0x10) != 0) {
packingModifier = 0;
// Optimize by adding static ref to shader.
var tmp_material = new Material((Shader)_p_ShaderRef_MobileBitmap.Value.GetValue(null));
//tmp_material.name = texture.name + " Material";
tmp_material.SetTexture(ShaderUtilities.ID_MainTex, texture);
tmp_material.SetFloat(ShaderUtilities.ID_TextureWidth, atlasWidth);
tmp_material.SetFloat(ShaderUtilities.ID_TextureHeight, atlasHeight);
fontAsset.material = tmp_material;
}
else {
packingModifier = 1;
// Optimize by adding static ref to shader.
var tmp_material = new Material((Shader)_p_ShaderRef_MobileSDF.Value.GetValue(null));
//tmp_material.name = texture.name + " Material";
tmp_material.SetTexture(ShaderUtilities.ID_MainTex, texture);
tmp_material.SetFloat(ShaderUtilities.ID_TextureWidth, atlasWidth);
tmp_material.SetFloat(ShaderUtilities.ID_TextureHeight, atlasHeight);
tmp_material.SetFloat(ShaderUtilities.ID_GradientScale, atlasPadding + packingModifier);
tmp_material.SetFloat(ShaderUtilities.ID_WeightNormal, fontAsset.normalStyle);
tmp_material.SetFloat(ShaderUtilities.ID_WeightBold, fontAsset.boldStyle);
fontAsset.material = tmp_material;
}
_p_freeGlyphRects.Value.SetValue(fontAsset, new List<GlyphRect>(8) { new(0, 0, atlasWidth - packingModifier, atlasHeight - packingModifier) });
_p_usedGlyphRects.Value.SetValue(fontAsset, new List<GlyphRect>(8));
// TODO: Consider adding support for extracting glyph positioning data
fontAsset.ReadFontAssetDefinition();
return fontAsset;
}
}
}