Author Topic: Text along a curve  (Read 64851 times)

dcrosby

  • Newbie
  • *
  • Posts: 12
Re: Text along a curve
« Reply #60 on: January 27, 2016, 03:15:11 PM »
I Query the position of the points on the spline relative to (t) to get the Vector3, I imagine that's a transform, and then feed that into Stephan's Text Distortion algorithm, and the letters have an offset. So either I have to subtract the transform of the object that has all the TextMeshPro and Spline on it, but I also tried putting it at the origin, didn't help.

-Derek

Jasper Flick

  • Global Moderator
  • Jr. Member
  • *****
  • Posts: 85
Re: Text along a curve
« Reply #61 on: January 28, 2016, 03:09:32 AM »
The spline operates in its own local space, while the TextMeshPro vertices are in their own local space. So if their transform settings aren't identical, you'd indeed get an offset. You could make both children of the same object so they always stay in sync as you move the parent around.

dcrosby

  • Newbie
  • *
  • Posts: 12
Re: Text along a curve
« Reply #62 on: January 28, 2016, 10:44:02 AM »
I Did, I attached both to the same GameObject / Transform.

Jasper Flick

  • Global Moderator
  • Jr. Member
  • *****
  • Posts: 85
Re: Text along a curve
« Reply #63 on: January 30, 2016, 03:29:42 AM »
The tutorial curve evaluation produces a position, which should replace VertexCurve.Evaluate. I'm not sure what kind of offset you are getting. A screenshot would help.

dcrosby

  • Newbie
  • *
  • Posts: 12
Re: Text along a curve
« Reply #64 on: February 05, 2016, 05:20:34 PM »

dcrosby

  • Newbie
  • *
  • Posts: 12
Re: Text along a curve
« Reply #65 on: February 06, 2016, 07:42:02 AM »
Messing around with the code a little-bit more yesterday, I found that because the way the animation script uses the length of the curve, the curve length is being calculated based on the bounding box of the text, and if I want to substitute the length of the spline, I have to sample the length of the curve to get a distance value, this wasn't part of the code you supplied so I'm now trying to implement my own. I hope then I'll be able to sample along the world space positions of the curve by feeding in the length values, and getting world-space positions back.

Jasper Flick

  • Global Moderator
  • Jr. Member
  • *****
  • Posts: 85
Re: Text along a curve
« Reply #66 on: February 08, 2016, 10:58:31 AM »
One way to get rid of the unwanted offset, is to transform the evaluation points into the text object's local space. So use transform.InverseTransformPoint(spline.GetPoint(x0)) instead of only spline.GetPoint(x0). Same for x1. Then you can independently move, rotate, and even scale both objects, yet the text will always stick to the curve.

Alternatively, make both objects children of another object, which would be the one you move around. Don't transform them locally. Then adjust the spline to no longer transform to world space in GetPoint. Or add a GetLocalPoint variant method.

Check my reply to Miha about using something else than the bounds of the text.
« Last Edit: February 08, 2016, 11:04:45 AM by Jasper Flick »

dcrosby

  • Newbie
  • *
  • Posts: 12
Re: Text along a curve
« Reply #67 on: February 12, 2016, 10:18:19 AM »
So my hierarchy is basically flat. I have my TextMeshPro object, with it's MshRenderer, RectTransform, TextContainer, TextMeshProScript, then I added a BezierSpline script (CatLikeCoding), and added my own SplineText script. Which is loosely based on the Animated Text script, but with the ability to run in editor, to be able to get feedback for placement without having to run the game.

My issue currently and it may very well have to do with the RectTransform, is that RectTransform is like a screen space object, in world position, and doesn't have a concept of "World Space" even though the GetPosition(t) function returns a worldspace position, the RectTransform seems to interpret it relative to itself. So When I move the RectTransform, I get a DoubleTransform on Z from the objects position, and one from the worldspace values which it's interpreting relative to itself. Also as I move the object the RectTransform, and the spline stay together, but the letters move further and further away from the original transform.

I've checked that the spline stuff is solid, by attaching a cube to the output from GetPosition(t) and it works, sticks to the spline like glue no matter where it moves. So something is wrong in the vertex position calculation. I've looked at the offsetToMidBaseline value and changed it to a Vector3, to see if I was having issues with the cast from Vector2 to Vector3, and it didn't solve much. I suppose I'm just not that well versed on how an animation curve which exists in 2D is implemented and what assumptions you're making as to it's Z position, relative to what I have to do / calculate to make it work no matter what the orientation / position.

I also looked at the x0 and x1 Positions, and can see you're calculating the midpoint of the letter, and then adding a tiny offset to get it's directionality along x, to then later calculate it's rotation, and plug it into a matrix to manipulate the vertex values.
« Last Edit: February 12, 2016, 01:40:42 PM by dcrosby »

Jasper Flick

  • Global Moderator
  • Jr. Member
  • *****
  • Posts: 85
Re: Text along a curve
« Reply #68 on: February 13, 2016, 02:35:51 AM »
The original spline.GetPoint() method returns a point in world space. If you use that directly as vertex position, you transplant a world point to local space, which then gets transformed to world space again on render. The result is a double translation, as you described.

So use transform.InverseTransformPoint(spline.GetPoint()) instead, or remove the TransformPoint invocation from GetPoint().

If you can't get that to work, please attach your project so I can look at it.

dcrosby

  • Newbie
  • *
  • Posts: 12
Re: Text along a curve
« Reply #69 on: February 13, 2016, 11:06:44 AM »
I apologize, you mentioned that before, and I tried it, but possibly in a state where other things didn't work (Too many changes) so I will revert some of my changes and see if I can get it to work. Also I didn't want to change the CatLikeCoding stuff too much, so that once I release my script someone could piece it together, by downloading the finished project from that website, and TextMesh Pro. I think it should be between TextMeshPro and CatlikeCoding to agree upon whatever usage / changes you need to include it in a paid for project. Personally I just needed this feature and TextMeshPro was the closest to that feature set with this particular piece missing. I'll keep you posted on my progress....

-D

dcrosby

  • Newbie
  • *
  • Posts: 12
Re: Text along a curve
« Reply #70 on: February 15, 2016, 09:08:52 PM »
Here's my code so far... I have an editor script as well, which was educational to figure out, but it (this code) still doesn't work:

Code: [Select]
using UnityEngine;
using System.Collections;
using TMPro;

[ExecuteInEditMode]
public class SplineText : MonoBehaviour {   

    public float curveScale = 1.0f;
    //public float length; // Currently Only Informational
    public BezierSpline vertexCurve;
    public TextMeshPro m_TextComponent;

    void Awake ()
    {
        // Make sure I have the thigs I need to get the data to deform text
        if (m_TextComponent == null)
            m_TextComponent = gameObject.GetComponent<TextMeshPro>();
        if (vertexCurve == null)
            vertexCurve = gameObject.GetComponent<BezierSpline>(); 
    }
    // Use this for initialization
    void Start () {
       
}

// Update is called once per frame
void Update () {

}

       
    void OnRenderObject()
    {
        // Make sure I have the thigs I need to get the data to deform text
        if (m_TextComponent == null)
            m_TextComponent = gameObject.GetComponent<TextMeshPro>();
        if (vertexCurve == null)
            vertexCurve = gameObject.GetComponent<BezierSpline>();

        if (m_TextComponent)
        {

            Vector3[] vertexPositions;
            Matrix4x4 matrix;

            m_TextComponent.ForceMeshUpdate(); // Generate the mesh and populate the textInfo with data we can use and manipulate.

            //length = vertexCurve.length(1,0.1f); // Added this to CatLikeCOding's code in case I needed it.

            m_TextComponent.renderMode = TextRenderFlags.DontRender; // Instructing TextMesh Pro not to upload the mesh as we will be modifying it.
            m_TextComponent.ForceMeshUpdate(); // Generate the mesh and populate the textInfo with data we can use and manipulate.

            TMP_TextInfo textInfo = m_TextComponent.textInfo;
            int characterCount = textInfo.characterCount;

            if (characterCount >= 0)
            {
                vertexPositions = textInfo.meshInfo[0].vertices;
                //int lastVertexIndex = textInfo.characterInfo[characterCount - 1].vertexIndex;

                // Thinking getting the bounds from the spline is more accurate since it's supposed to fit on there, but because the formatting happens in the text box
                // That may not be the best idea, so I left it in here commented out in case I wanted to go back.
                float boundsMinX = m_TextComponent.bounds.min.x;
                float boundsMaxX = m_TextComponent.bounds.max.x;
                //float boundsMinX = transform.InverseTransformPoint(vertexCurve.GetPoint(1f)).x;
                //float boundsMaxX = transform.InverseTransformPoint(vertexCurve.GetPoint(0f)).x;
               


                for (int i = 0; i < characterCount; i++)
                {
                    if (!textInfo.characterInfo[i].isVisible)
                        continue;

                    int vertexIndex = textInfo.characterInfo[i].vertexIndex;

                    // Compute the baseline mid point for each character
                    Vector3 offsetToMidBaseline = new Vector2((vertexPositions[vertexIndex + 0].x + vertexPositions[vertexIndex + 2].x) / 2, (vertexPositions[vertexIndex + 0].y + vertexPositions[vertexIndex].y) / 2);//textInfo.characterInfo[i].baseLine);

                    // Apply offset to adjust our pivot point.
                    vertexPositions[vertexIndex + 0] += -offsetToMidBaseline;
                    vertexPositions[vertexIndex + 1] += -offsetToMidBaseline;
                    vertexPositions[vertexIndex + 2] += -offsetToMidBaseline;
                    vertexPositions[vertexIndex + 3] += -offsetToMidBaseline;

                    // Compute the angle of rotation for each character based on the animation curve
                    float x0 = (offsetToMidBaseline.x - transform.InverseTransformPoint(vertexCurve.GetPoint(0f)).x / (transform.InverseTransformPoint(vertexCurve.GetPoint(1f)).x - transform.InverseTransformPoint(vertexCurve.GetPoint(0f)).x));
                    float x1 = x0 + 0.0001f;

                    float z0 = transform.InverseTransformPoint(vertexCurve.GetPoint(x0)).z * curveScale;
                    float z1 = transform.InverseTransformPoint(vertexCurve.GetPoint(x1)).z * curveScale;
                    float y0 = transform.InverseTransformPoint(vertexCurve.GetPoint(x0)).y * curveScale;
                    float y1 = transform.InverseTransformPoint(vertexCurve.GetPoint(x1)).y * curveScale;
                   
                    Vector3 horizontal = new Vector3(1, 0, 0);
                    //Vector3 normal = new Vector3(-(y1 - y0), (x1 * (boundsMaxX - boundsMinX) + boundsMinX) - offsetToMidBaseline.x, 0);
                    Vector3 tangent = new Vector3(x1 * (boundsMaxX - boundsMinX) + boundsMinX, z1, y1) - new Vector3(offsetToMidBaseline.x, z0, y0);

                    float dot = Mathf.Acos(Vector3.Dot(horizontal, tangent.normalized)) * 57.2957795f;
                    Vector3 cross = Vector3.Cross(horizontal, tangent);
                    float angle = cross.y > 0 ? dot : 360 - dot;

                    float xPos = x0;
                    float yPos = y0;
                    float zPos = z0;
                   
                    matrix = Matrix4x4.TRS(new Vector3(xPos, yPos, zPos), Quaternion.Euler(0, -angle, 0), Vector3.one);

                    vertexPositions[vertexIndex + 0] = matrix.MultiplyPoint3x4(vertexPositions[vertexIndex + 0]);
                    vertexPositions[vertexIndex + 1] = matrix.MultiplyPoint3x4(vertexPositions[vertexIndex + 1]);
                    vertexPositions[vertexIndex + 2] = matrix.MultiplyPoint3x4(vertexPositions[vertexIndex + 2]);
                    vertexPositions[vertexIndex + 3] = matrix.MultiplyPoint3x4(vertexPositions[vertexIndex + 3]);

                    vertexPositions[vertexIndex + 0] += offsetToMidBaseline;
                    vertexPositions[vertexIndex + 1] += offsetToMidBaseline;
                    vertexPositions[vertexIndex + 2] += offsetToMidBaseline;
                    vertexPositions[vertexIndex + 3] += offsetToMidBaseline;
                }

                // Upload the mesh with the revised information
                m_TextComponent.mesh.vertices = vertexPositions;
                m_TextComponent.mesh.uv = textInfo.meshInfo[0].uvs0;
                m_TextComponent.mesh.uv2 = textInfo.meshInfo[0].uvs2;
                m_TextComponent.mesh.colors32 = textInfo.meshInfo[0].colors32;

                m_TextComponent.mesh.RecalculateBounds(); // We need to update the bounds of the text object.
            }
        }
    }
           
}

Jasper Flick

  • Global Moderator
  • Jr. Member
  • *****
  • Posts: 85
Re: Text along a curve
« Reply #71 on: February 18, 2016, 05:18:25 AM »
Here is a modified version of your script. The computations are greatly simplified so it's easier to see what's going on.

I used the width of the text container to distribute the text along the line. Made sense to me.

I don't think recalculating the whole thing every frame the text is visible is a good idea, but at least you get immediate feedback in the editor.

Code: [Select]
using UnityEngine;
using System.Collections;
using TMPro;

[ExecuteInEditMode]
public class SplineText : MonoBehaviour {   

public float curveScale = 1.0f;
//public float length; // Currently Only Informational
public BezierSpline vertexCurve;
public TextMeshPro m_TextComponent;

void Awake ()
{
// Make sure I have the thigs I need to get the data to deform text
if (m_TextComponent == null)
m_TextComponent = gameObject.GetComponent<TextMeshPro>();
if (vertexCurve == null)
vertexCurve = gameObject.GetComponent<BezierSpline>(); 
}
// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {

}


void OnRenderObject()
{
// Make sure I have the thigs I need to get the data to deform text
if (m_TextComponent == null)
m_TextComponent = gameObject.GetComponent<TextMeshPro>();
if (vertexCurve == null)
vertexCurve = gameObject.GetComponent<BezierSpline>();

if (m_TextComponent)
{

Vector3[] vertexPositions;

m_TextComponent.renderMode = TextRenderFlags.Render;
m_TextComponent.ForceMeshUpdate();
m_TextComponent.renderMode = TextRenderFlags.DontRender;

TMP_TextInfo textInfo = m_TextComponent.textInfo;
int characterCount = textInfo.characterCount;

if (characterCount >= 0)
{
vertexPositions = textInfo.meshInfo[0].vertices;

float boundsMaxX = m_TextComponent.rectTransform.rect.width * 0.5f;
float boundsMinX = -boundsMaxX;

for (int i = 0; i < characterCount; i++)
{
if (!textInfo.characterInfo[i].isVisible)
continue;

int vertexIndex = textInfo.characterInfo[i].vertexIndex;

// Compute the baseline mid point for each character
Vector3 offsetToMidBaseline = new Vector3((vertexPositions[vertexIndex + 0].x + vertexPositions[vertexIndex + 2].x) / 2, textInfo.characterInfo[i].baseLine);

Vector3 c0 = vertexPositions[vertexIndex + 0] - offsetToMidBaseline;
Vector3 c1 = vertexPositions[vertexIndex + 1] - offsetToMidBaseline;
Vector3 c2 = vertexPositions[vertexIndex + 2] - offsetToMidBaseline;
Vector3 c3 = vertexPositions[vertexIndex + 3] - offsetToMidBaseline;

float t = (offsetToMidBaseline.x - boundsMinX) / (boundsMaxX - boundsMinX);
Vector3 point = transform.InverseTransformPoint(vertexCurve.GetPoint(t)) * curveScale;
Vector3 xAxis = transform.InverseTransformDirection(vertexCurve.GetVelocity(t)).normalized;
Vector3 yAxis = (Vector3.up - xAxis * xAxis.y).normalized;

vertexPositions[vertexIndex + 0] = point + c0.x * xAxis + c0.y * yAxis;
vertexPositions[vertexIndex + 1] = point + c1.x * xAxis + c1.y * yAxis;
vertexPositions[vertexIndex + 2] = point + c2.x * xAxis + c2.y * yAxis;
vertexPositions[vertexIndex + 3] = point + c3.x * xAxis + c3.y * yAxis;
}

// Upload the mesh with the revised information
m_TextComponent.mesh.vertices = vertexPositions;
m_TextComponent.mesh.uv = textInfo.meshInfo[0].uvs0;
m_TextComponent.mesh.uv2 = textInfo.meshInfo[0].uvs2;
m_TextComponent.mesh.colors32 = textInfo.meshInfo[0].colors32;

m_TextComponent.mesh.RecalculateBounds(); // We need to update the bounds of the text object.
}
}
}

}

Update: I've modified the script to work with version 0.1.45 beta 1. It needed other renderflag settings to work in OnRenderObject. It also retrieves the width direction from the rect transform now, cutting out the container. For release work, I would not put this code in OnRenderObject, and only invoke it explicitly when necessary.

[ Guests cannot view attachments ]
« Last Edit: February 18, 2016, 12:58:01 PM by Jasper Flick »

dcrosby

  • Newbie
  • *
  • Posts: 12
Re: Text along a curve
« Reply #72 on: February 22, 2016, 05:45:13 PM »
Very Cool, yeah it has some issues with line wrapping, and other things if you scale the box, and formatting, but that gives me something to chew on. Now that I see it working it's easier for me to set breakpoints to see what's working, and what I need to change.

Thanks ! :)  8)

christougher

  • Newbie
  • *
  • Posts: 19
Re: Text along a curve
« Reply #73 on: March 01, 2016, 09:13:12 PM »
Here is a revised script with updated Warp functions.

The warping is done on the Z-Axis (up / down). In order to have the text wrap on the Y-Axis, make the following changes at line 591 or 697 depending on which TMP component you are using.

Code: C#
  1. matrix = Matrix4x4.TRS(new Vector3(0, 0, y0), Quaternion.Euler(0, -angle, 0), Vector3.one);
  2.  

Hi, brand new here.  I downloaded this script and dropped into my Unity 5.1.4 project and immediately get 36 red errors, the first of which is this:
Assets/TextMesh Pro/Examples/Scripts/VertexAttributeModifier.cs(122,46): error CS0021: Cannot apply indexing with [] to an expression of type `TMPro.TMP_MeshInfo'

Any reason why this wouldn't work just dropping it in?

Stephan B.

  • Administrator
  • Hero Member
  • *****
  • Posts: 5687
Re: Text along a curve
« Reply #74 on: March 01, 2016, 09:15:14 PM »
Here is a revised script with updated Warp functions.

The warping is done on the Z-Axis (up / down). In order to have the text wrap on the Y-Axis, make the following changes at line 591 or 697 depending on which TMP component you are using.

Code: C#
  1. matrix = Matrix4x4.TRS(new Vector3(0, 0, y0), Quaternion.Euler(0, -angle, 0), Vector3.one);
  2.  

Hi, brand new here.  I downloaded this script and dropped into my Unity 5.1.4 project and immediately get 36 red errors, the first of which is this:
Assets/TextMesh Pro/Examples/Scripts/VertexAttributeModifier.cs(122,46): error CS0021: Cannot apply indexing with [] to an expression of type `TMPro.TMP_MeshInfo'

Any reason why this wouldn't work just dropping it in?

You need to get an older version of the script for Unity 5.1 since Unity made massive changes to their API in Unity 5.2 which the latest version of this script would address.

What version of TextMesh Pro are you using? The version number is located in the header of the TextMeshPro.cs file.