Skip to content

Instantly share code, notes, and snippets.

@dotsquid
Created October 26, 2020 07:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dotsquid/7b855916723ff0771355c929de6b1cd8 to your computer and use it in GitHub Desktop.
Save dotsquid/7b855916723ff0771355c929de6b1cd8 to your computer and use it in GitHub Desktop.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using SC.Utilities;
using TMPro;
[ExecuteAlways]
[RequireComponent(typeof(TMP_Text))]
public class TextMeshProSubdivisionEffect : MonoBehaviour
{
private static readonly Vector2 kVector2Up = Vector2.up;
private static readonly Vector2 kVector2One = Vector2.one;
private static readonly Vector2 kVector2Right = Vector2.right;
[SerializeField]
private int _subdivisionLevel = 2;
private TMP_Text _text;
private bool _isModifying;
#if UNITY_EDITOR
protected void OnValidate()
{
_subdivisionLevel = Mathf.Max(_subdivisionLevel, 1);
SubdivideText();
}
#endif
private void Awake()
{
TryCaptureComponent();
SubdivideText();
}
private void OnDestroy()
{
if (_text != null)
{
_text.OnPreRenderText -= OnPreRenderText;
}
}
private void OnEnable()
{
TMPro_EventManager.TEXT_CHANGED_EVENT.Add(OnTextChanged);
}
private void OnDisable()
{
TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(OnTextChanged);
}
private void TryCaptureComponent()
{
if (TryGetComponent(out _text))
{
_text.OnPreRenderText += OnPreRenderText;
}
}
private void OnTextChanged(Object obj)
{
if (obj == _text)
SubdivideText();
}
private void OnPreRenderText(TMP_TextInfo textInfo)
{
#if NOPE
var meshInfos = textInfo.meshInfo;
for (int i = 0, count = meshInfos.Length; i < count; ++i)
{
var meshInfo = meshInfos[i];
SubdivideMesh(ref meshInfo, _subdivisionLevel);
}
#endif
}
private void SubdivideText()
{
if (!_isModifying && _text != null)
{
_isModifying = true;
//_text.ForceMeshUpdate();
_text.UpdateVertexData();
var textInfo = _text.textInfo;
var meshInfo = textInfo.meshInfo;
for (int i = 0, count = meshInfo.Length; i < count; ++i)
{
var mesh = meshInfo[i].mesh;
using (var vh = new VertexHelper(mesh))
{
SubdivideMesh(vh, _subdivisionLevel);
vh.FillMesh(mesh);
_text.UpdateGeometry(mesh, i);
}
}
_isModifying = false;
}
}
#if NOPE
private void SubdivideMesh(ref TMP_MeshInfo meshInfo, int level)
{
int vertsCount = meshInfo.vertexCount;
int quadsCount = vertsCount / 6;
int subQuadsPerAxis = level;
int subQuadsPerQuad = subQuadsPerAxis * subQuadsPerAxis;
int totalQuads = quadsCount * subQuadsPerQuad;
float quadIndexStep = 1.0f / (totalQuads - 1);
float t = 1.0f / subQuadsPerAxis;
var shuffledQuadIndicies = GetContinuousArray(totalQuads);
var quadVerts = new UIVertex[4];
var subQuadVerts = new UIVertex[4];
for (int i = 0; i < quadsCount; ++i)
{
quadVerts[0] = Extract(ref meshInfo, i * 6 + 0);
quadVerts[1] = Extract(ref meshInfo, i * 6 + 1);
quadVerts[2] = Extract(ref meshInfo, i * 6 + 2);
quadVerts[3] = Extract(ref meshInfo, i * 6 + 4);
for (int y = 0; y < subQuadsPerAxis; ++y)
{
for (int x = 0; x < subQuadsPerAxis; ++x)
{
float tx0 = t * x;
float ty0 = t * y;
float tx1 = t + tx0;
float ty1 = t + ty0;
Lerp(ref subQuadVerts[0], quadVerts, tx0, ty0);
Lerp(ref subQuadVerts[1], quadVerts, tx1, ty0);
Lerp(ref subQuadVerts[2], quadVerts, tx1, ty1);
Lerp(ref subQuadVerts[3], quadVerts, tx0, ty1);
int quadIndex = x + y * subQuadsPerAxis + i * subQuadsPerQuad;
float invQuadIndex = shuffledQuadIndicies[quadIndex] * quadIndexStep;
var uv2 = invQuadIndex * kVector2One;
subQuadVerts[0].uv2 = uv2;
subQuadVerts[1].uv2 = uv2;
subQuadVerts[2].uv2 = uv2;
subQuadVerts[3].uv2 = uv2;
//SetUV2(subQuadVerts, uv2);
//vh.AddUIVertexQuadFull(subQuadVerts);
}
}
}
}
#endif
private void SubdivideMesh(VertexHelper vh, int level)
{
var verts = new List<UIVertex>();
vh.GetUIVertexStream(verts);
vh.Clear();
int vertsCount = verts.Count;
int quadsCount = vertsCount / 6;
int subQuadsPerAxis = level;
int subQuadsPerQuad = subQuadsPerAxis * subQuadsPerAxis;
int totalQuads = quadsCount * subQuadsPerQuad;
float quadIndexStep = 1.0f / (totalQuads - 1);
var shuffledQuadIndicies = GetContinuousArray(totalQuads);
var quadVerts = new UIVertex[4];
var subQuadVerts = new UIVertex[4];
for (int i = 0; i < quadsCount; ++i)
{
quadVerts[0] = verts[i * 6 + 0];
quadVerts[1] = verts[i * 6 + 1];
quadVerts[2] = verts[i * 6 + 2];
quadVerts[3] = verts[i * 6 + 4];
float t = 1.0f / subQuadsPerAxis;
// Since this effect is intended to be used with text
// which has the same color for all vertices
// we can use the color of the first vertex.
// But proper color interpolation for vertices
// being inserted is highly welcome.
var vertexPreset = new UIVertex()
{
color = quadVerts[0].color
};
for (int y = 0; y < subQuadsPerAxis; ++y)
{
for (int x = 0; x < subQuadsPerAxis; ++x)
{
float tx0 = t * x;
float ty0 = t * y;
float tx1 = t + tx0;
float ty1 = t + ty0;
Lerp(ref subQuadVerts[0], quadVerts, tx0, ty0);
Lerp(ref subQuadVerts[1], quadVerts, tx1, ty0);
Lerp(ref subQuadVerts[2], quadVerts, tx1, ty1);
Lerp(ref subQuadVerts[3], quadVerts, tx0, ty1);
int quadIndex = x + y * subQuadsPerAxis + i * subQuadsPerQuad;
float invQuadIndex = shuffledQuadIndicies[quadIndex] * quadIndexStep;
var uv2 = invQuadIndex * kVector2One;
subQuadVerts[0].uv2 = uv2;
subQuadVerts[1].uv2 = uv2;
subQuadVerts[2].uv2 = uv2;
subQuadVerts[3].uv2 = uv2;
//SetUV2(subQuadVerts, uv2);
vh.AddUIVertexQuadFull(subQuadVerts);
}
}
}
}
private static void SetUV2(UIVertex[] verts, Vector2 uv2)
{
{
var vert = verts[0];
vert.uv2 = uv2;
verts[0] = vert;
}
{
var vert = verts[1];
vert.uv2 = uv2;
verts[1] = vert;
}
{
var vert = verts[2];
vert.uv2 = uv2;
verts[2] = vert;
}
{
var vert = verts[3];
vert.uv2 = uv2;
verts[3] = vert;
}
}
private static void Lerp(ref UIVertex result, UIVertex[] verts, float tx, float ty)
{
UIVertex v01 = Lerp(ref verts[0], ref verts[1], tx);
UIVertex v32 = Lerp(ref verts[3], ref verts[2], tx);
Lerp(ref result, ref v01, ref v32, ty);
}
private static void Lerp(ref UIVertex result, ref UIVertex a, ref UIVertex b, float t)
{
var aPos = a.position;
var aUV0 = a.uv0;
var aUV1 = a.uv1;
var aUV2 = a.uv2;
var aUV3 = a.uv3;
var aC = a.color;
var bPos = b.position;
var bUV0 = b.uv0;
var bUV1 = b.uv1;
var bUV2 = b.uv2;
var bUV3 = b.uv3;
var bC = b.color;
result.position = Vector2.Lerp(aPos, bPos, t);
result.uv0 = Vector2.Lerp(aUV0, bUV0, t);
result.uv1 = Vector2.Lerp(aUV1, bUV1, t);
result.uv2 = Vector2.Lerp(aUV2, bUV2, t);
result.uv3 = Vector2.Lerp(aUV3, bUV3, t);
result.color = Color.Lerp(aC, bC, t);
}
private static UIVertex Lerp(ref UIVertex a, ref UIVertex b, float t)
{
UIVertex result = default;
Lerp(ref result, ref a, ref b, t);
return result;
}
private static UIVertex Lerp(UIVertex[] verts, float tx, float ty)
{
UIVertex result = default;
Lerp(ref result, verts, tx, ty);
return result;
}
private static UIVertex Extract(ref TMP_MeshInfo meshInfo, int index)
{
return new UIVertex()
{
position = meshInfo.vertices[index],
normal = meshInfo.normals[index],
tangent = meshInfo.tangents[index],
color = meshInfo.colors32[index],
uv0 = meshInfo.uvs0[index],
uv1 = meshInfo.uvs2[index]
};
}
private int[] GetContinuousArray(int count, int startValue = 0)
{
int[] array = new int[count];
for (int i = 0; i < count; ++i)
{
array[i] = startValue + i;
}
return array;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment