TextMeshProUGUI & IMeshModifier

Hi.
I’m currently in the process of switching from UI.Text to TextMesh Pro in the live project and I encountered a problem with that TextMeshProUGUI does not work with IMeshModifier.
Some of the text effects in our game relate on IMeshModifier. Specifically one of the implementations provides a subdivided mesh with quad indices in uv2. This is required for the following effect x.com
I tried some custom hacky implementations of BaseMeshEffect which intended to work with TextMesh Pro, but they are not perfect: poor performance due to constant mesh rebuilding in LateUpdate, random errors inside TMP due to mismatching of vertices / indices size, randomly fail in case several effects are using on the same game object.
Since TextMesh Pro is a part of Unity and should be fully integrated, is it possible to make it work with IMeshModifier please?

Take a look at the example scene Animating Vertex Attributes and related scripts included in the TMP Examples & Extras. This provides a good example of how to modify the text geometry.

There is also a new callback OnPreRenderText() referenced in the following thread .

Using the above callback would enable you to modify the text geometry when the text changes. This works with both and text components.

Thank you Stephan. I’ll give it a try.
However I still don’t understand why TextMeshProUGUI does not respect IMeshModifier like other UGUI components?

One of the text effects used in our game requires subdividing mesh quads into subquads and filling extra information into uv3 (while uv0 and uv2 are internally used by TMP). So I can’t modify TMP_MeshInfo like it’s done in VertexJitter example because it does not contain a required uv-set.
That’s why I try to modify the mesh directly after TMP fills it.

TMPro_EventManager.TEXT_CHANGED_EVENT.Add(OnTextChanged);

private void OnTextChanged(Object obj)
{
    if (obj == _text)
        SubdivideText();
}

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;
    }
}

Everything looks fine until I change the text in the Inspector. The mesh becomes more and more subdivided like if TMP does not fill it from scratch. I tried to force TMP to update the mesh by calling ForceMeshUpdate and UpdateVertexData without any luck.

@Stephan_B any suggestions?

I’ll try taking a closer look later today / within the next day or so and follow up with you shortly thereafter.

1 Like

Hi, any luck?

Just in case, here is what I ended up on TextMeshProSubdivisionEffect.cs · GitHub
It requires the following utility class with extension methods for VertexHelper UIUtility.cs · GitHub

Although you appear to have a working solution, I am still planning on taking a closer to look to provide an example of how to make this work easily with the OnPreRenderText() callback.

It’s totally a NOT working solution. I just decided to share the entire script, not the short extract as above.
So I’m passionately awaiting for your example.

Sorry for pushing, but it’s been two weeks and this problem is kinda important and urgent.

New version of TMP has been released. I will work on this tonight and over the weekend if needed.

Had a chance to look at this last night and a bit today.

The subdivision is now working as seen in the image below.


There are still changes / improvements that I want to make. I am planning on working some more on this later tonight but should have something for you for Monday.

1 Like

Here is a revised script using the new OnPreRenderText callback which should enable you to move forward.

Further improvements and optimizations could be made to reduce allocations but again, this should enable you to move forward.

Currently, the OnPreRenderText callback allows a user to modify an existing textInfo which is fine for many uses cases but not ideal in this case where we would want to return a copy while having the text object continue to reference the source textInfo. This is something that I will explore when I have time and perhaps make changes to this callback.

In terms of why you initial script was resulting in increased subdivision of subsequent characters, it was due to the first character subdividing the geometry of subsequent characters which would then further subdivide the next and so on.

9914010–1433016–TMP_MeshTessellation.cs (6.23 KB)

Thanks. But how can I use an extra uv-set which I mentioned here?

Also how to force subdivision whenever I want (not in OnPreRenderText)?
This is required because of 2 reasons:

  1. I want subdivision to reset when my component is disabled;
  2. I want to force subdivision in OnEnable. Consider a situation when the text for TextMeshPro is provided by serialization from the scene and not changed via scripts. In this case my subdivision will work only if OnEnable of my component will be executed before OnPreRenderText is invoked. Otherwise my component will subscribe this event behind time. I’m not aware of the execution order of TMP’s insides, so it’s just a concern.

Whenever text objects are awaken or enabled or any of the properties changed, the text object will be marked as dirty and get updated / processed which happens just before culling and the camera is rendered. This update process consists of a text parsing and layout phase which ends with the resulting geometry getting pushed to the renderer.

OnPreRenderText() is called at the end of the above process and just before the geometry is pushed to the renderer. This is great for cases like in the VertexJitter example where you are modifying the existing geometry. However, this does not provide for the opportunity to add new vertex attributes like extra UV set.

In your case, in OnEnable() or OnDisable() you should be able to call ForceMeshUpdate() to force the text object to be updated. Be sure to check the overloads for that function.