Search Unity

onFillVBO to OnPopulateMesh help?

Discussion in 'Unity UI (uGUI) & TextMesh Pro' started by zge, Sep 10, 2015.

  1. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    Upgrading to 5.2 my onFillVBO's are no longer working.

    I have a script based on the one posted here:
    http://forum.unity3d.com/threads/draw-circles-or-primitives-on-the-new-ui-canvas.272488/

    Within "protected override void OnFillVBO (List<UIVertex> vbo){" I'm calculating positions and UV coords for vertices and then adding them to the list.

    I'm still getting the polygons drawn to the screen, but they're no longer in the correct position and have incorrect UV coords.

    So is there a quick fix or do I need to change the entire thing to a mesh? If so, can someone point me in the right direction to replicate my current functionality? Everything I've tried has resulted in nothing being drawn to the screen.

    Oh, and also from 5.1 to 5.2 my frame rate has dropped from 300fps to 75fps with only a few UI objects in the scene :(
     
  2. RC_ID3I

    RC_ID3I

    Joined:
    Dec 10, 2014
    Posts:
    3
    Same here ; we are using override for MaskableGraphic.OnFillVBO(List<UIVertex> vbo) to draw lines and it seems that it's no more called ???

    Is there a quick workaround ?
     
  3. wikmanyo

    wikmanyo

    Joined:
    Nov 17, 2012
    Posts:
    49
    Its all been replaced with Mesh rendering... use OnPopulateMesh instead
     
    RC_ID3I likes this.
  4. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,634
    dval, zge, Tim-C and 2 others like this.
  5. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    Thanks Simon. I looked for this yesterday, but didn't find the 5.2 version.
     
  6. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,634
    Was working late last night finishing the 5.2 updates. The LineRenderer especially caused me some grief, the new vertex drawing way of doing things in 5.2 is very different. (but there are helpers)
     
  7. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    Much appreciated :)
     
  8. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    Is there any chance you could help me out with this? I'm not sure why this isn't filling. There seem to be the correct number of vertices being generated, but it's not creating the mesh...

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections.Generic;
    4.  
    5.  
    6. [ExecuteInEditMode]
    7. public class UICircle2 : Graphic
    8. {
    9.     [Range(0, 100)]
    10.     public int fillPercent;
    11.     public bool fill = true;
    12.     public int thikness = 5;
    13.     [Range(0, 360)]
    14.     public int segments = 360;
    15.  
    16.     void Update()
    17.     {
    18.         this.thikness = (int)Mathf.Clamp(this.thikness, 0, rectTransform.rect.width / 2);
    19.     }
    20.  
    21.     //    protected override void OnFillVBO(List<UIVertex> vbo)
    22.     protected override void OnPopulateMesh(Mesh toFill)
    23.     {
    24.         float outer = -rectTransform.pivot.x * rectTransform.rect.width;
    25.         float inner = -rectTransform.pivot.x * rectTransform.rect.width + this.thikness;
    26.  
    27.         //        vbo.Clear();
    28.         toFill.Clear();
    29.         var vbo = new VertexHelper(toFill);
    30.  
    31.  
    32.         UIVertex vert = UIVertex.simpleVert;
    33.         Vector2 prevX = Vector2.zero;
    34.         Vector2 prevY = Vector2.zero;
    35.  
    36.         float f = (this.fillPercent / 100f);
    37.         float degrees = 360f / segments;
    38.         int fa = (int)((segments + 1) * f);
    39.  
    40.  
    41.         for (int i = 0; i < fa; i++)
    42.         {
    43.             float rad = Mathf.Deg2Rad * (i * degrees);
    44.             float c = Mathf.Cos(rad);
    45.             float s = Mathf.Sin(rad);
    46.             float x = outer * c;
    47.             float y = inner * c;
    48.             vert.color = color;
    49.             vert.position = prevX;
    50.             vbo.AddVert(vert);
    51.             prevX = new Vector2(outer * c, outer * s);
    52.             vert.position = prevX;
    53.             vbo.AddVert(vert);
    54.             if (this.fill)
    55.             {
    56.                 vert.position = Vector2.zero;
    57.                 vbo.AddVert(vert);
    58.                 vbo.AddVert(vert);
    59.             }
    60.             else
    61.             {
    62.                 vert.position = new Vector2(inner * c, inner * s); ;
    63.                 vbo.AddVert(vert);
    64.                 vert.position = prevY;
    65.                 vbo.AddVert(vert);
    66.                 prevY = new Vector2(inner * c, inner * s);
    67.             }
    68.         }
    69.         vbo.FillMesh(toFill);
    70.         int verts = vbo.currentVertCount;
    71.         Debug.Log(verts);
    72.     }
    73. }
    74.  
     
  9. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    I figured it out with some more major rewrites:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections.Generic;
    4.  
    5.  
    6. [ExecuteInEditMode]
    7. public class UICircle2 : Graphic
    8. {
    9.     [SerializeField]
    10.     Texture m_Texture;
    11.     [Range(0, 100)]
    12.     public int fillPercent;
    13.     public bool fill = true;
    14.     public int thikness = 5;
    15.     [Range(0, 360)]
    16.     public int segments = 360;
    17.  
    18.     public override Texture mainTexture
    19.     {
    20.         get
    21.         {
    22.             return m_Texture == null ? s_WhiteTexture : m_Texture;
    23.         }
    24.     }
    25.  
    26.  
    27.     /// <summary>
    28.     /// Texture to be used.
    29.     /// </summary>
    30.     public Texture texture
    31.     {
    32.         get
    33.         {
    34.             return m_Texture;
    35.         }
    36.         set
    37.         {
    38.             if (m_Texture == value)
    39.                 return;
    40.  
    41.             m_Texture = value;
    42.             SetVerticesDirty();
    43.             SetMaterialDirty();
    44.         }
    45.     }
    46.  
    47.  
    48.     void Update()
    49.     {
    50.         this.thikness = (int)Mathf.Clamp(this.thikness, 0, rectTransform.rect.width / 2);
    51.     }
    52.  
    53.     protected UIVertex[] SetVbo(Vector2[] vertices, Vector2[] uvs)
    54.     {
    55.         UIVertex[] vbo = new UIVertex[4];
    56.         for (int i = 0; i < vertices.Length; i++)
    57.         {
    58.             var vert = UIVertex.simpleVert;
    59.             vert.color = color;
    60.             vert.position = vertices[i];
    61.             vert.uv0 = uvs[i];
    62.             vbo[i] = vert;
    63.         }
    64.         return vbo;
    65.     }
    66.  
    67.  
    68.     //    protected override void OnFillVBO(List<UIVertex> vbo)
    69.     protected override void OnPopulateMesh(Mesh toFill)
    70.     {
    71.         float outer = -rectTransform.pivot.x * rectTransform.rect.width;
    72.         float inner = -rectTransform.pivot.x * rectTransform.rect.width + this.thikness;
    73.  
    74.         //        vbo.Clear();
    75.         toFill.Clear();
    76.         var vbo = new VertexHelper(toFill);
    77.  
    78.  
    79.         UIVertex vert = UIVertex.simpleVert;
    80.         Vector2 prevX = Vector2.zero;
    81.         Vector2 prevY = Vector2.zero;
    82.         Vector2 uv0 = new Vector2(0, 0);
    83.         Vector2 uv1 = new Vector2(0, 1);
    84.         Vector2 uv2 = new Vector2(1, 1);
    85.         Vector2 uv3 = new Vector2(1, 0);
    86.         Vector2 pos0;
    87.         Vector2 pos1;
    88.         Vector2 pos2;
    89.         Vector2 pos3;
    90.  
    91.         float f = (this.fillPercent / 100f);
    92.         float degrees = 360f / segments;
    93.         int fa = (int)((segments + 1) * f);
    94.  
    95.  
    96.         for (int i = 0; i < fa; i++)
    97.         {
    98.             float rad = Mathf.Deg2Rad * (i * degrees);
    99.             float c = Mathf.Cos(rad);
    100.             float s = Mathf.Sin(rad);
    101.             float x = outer * c;
    102.             float y = inner * c;
    103.  
    104.             uv0 = new Vector2(0, 1);
    105.             uv1 = new Vector2(1, 1);
    106.             uv2 = new Vector2(1, 0);
    107.             uv3 = new Vector2(0, 0);
    108.  
    109.             pos0 = prevX;
    110.             pos1 = new Vector2(outer * c, outer * s);
    111.  
    112.             if (fill)
    113.             {
    114.                 pos2 = Vector2.zero;
    115.                 pos3 = Vector2.zero;
    116.             }
    117.             else
    118.             {
    119.                 pos2 = new Vector2(inner * c, inner * s);
    120.                 pos3 = prevY;
    121.             }
    122.  
    123.             prevX = pos1;
    124.             prevY = pos2;
    125.  
    126.             vbo.AddUIVertexQuad(SetVbo(new[] { pos0, pos1, pos2, pos3 }, new[] { uv0, uv1, uv2, uv3 }));
    127.  
    128.         }
    129.  
    130.         if (vbo.currentVertCount > 3)
    131.         {
    132.             vbo.FillMesh(toFill);
    133.         }
    134.  
    135.     }
    136. }
    137.  


    Thank you again for your LineRenderer example. But I'm still not sure why the previous one wasn't working?
     
    Last edited: Sep 12, 2015
  10. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,634
    I had the same issue updating the LineRenderer, had to use DrawQuad instead of AddVert. no idea why it failed but the mesh was reporting a valid number of vertices, possibly a winding order issue maybe?
    Looks like you did the same thing.

    Now back to work for the other primitives :D
     
  11. ddf

    ddf

    Joined:
    Jul 9, 2011
    Posts:
    54
    Did a miss a version where OnFillVBO was deprecated? I'm sort of astounded that it was Obsoleted like this and that the doc page still has an example using OnFillVBO.
     
  12. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    That was the issue I came up against. Once again, I just copied your code and it worked :p

    @ddf OnFillVBO was depreciated in 5.2. Documentation still needs to catch up, but you can find examples at bitbucket linked to by Simon earlier in the thread.
     
    SimonDarksideJ likes this.
  13. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,634
    @ddf it was deprecated for a fairly good reason, they moved from a custom vertices approach to using regular Unity 3D meshes, so the old functions (from 5.2 onwards) didn't make sense any more.
    So all the functions are now mesh based and all the vertext / color / uv information is now stored in separate arrays. kind of makes sense to unify it so there is less to manage / maintain going forward. Docs will take a while to update as @zge stated
     
  14. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    525
    I agree with @SimonDarksideJ, and this seems to be a great move so far, but I also think @ddf has a point in that an "in-between" version where both methods worked would have been nice and less abrupt, giving us some more leeway to adapt. I mean, we're not totally unified yet anyway, as UIVertex still exists. So no, you didn't miss a version, it just went straight to Obsolete. =)
     
    SimonDarksideJ likes this.
  15. koohddang

    koohddang

    Joined:
    May 14, 2013
    Posts:
    2
    I had same problem.
    I solved it.
    Code (CSharp):
    1.  
    2. [ExecuteInEditMode]
    3. public class DiamondGraph : Graphic
    4. {
    5. public float a;
    6. public float b;
    7. public float c;
    8. public float d;
    9.  
    10. protected override void OnPopulateMesh(Mesh m)
    11. {
    12. List<UIVertex> verts = new List<UIVertex>();
    13.  
    14. float wHalf = rectTransform.rect.width / 2;
    15. float hHalf = rectTransform.rect.height / 2;
    16. a = Math.Min(1, Math.Max(0, a));
    17. b = Math.Min(1, Math.Max(0, b));
    18. c = Math.Min(1, Math.Max(0, c));
    19. d = Math.Min(1, Math.Max(0, d));
    20.  
    21. Color32 color32 = color;
    22. using (var vh = new VertexHelper())
    23. {
    24. vh.AddVert(new Vector3(-wHalf * a, 0), color32, new Vector2(0f, 0f));
    25. vh.AddVert(new Vector3(0, wHalf * b), color32, new Vector2(0f, 1f));
    26. vh.AddVert(new Vector3(wHalf * c, 0), color32, new Vector2(1f, 1f));
    27. vh.AddVert(new Vector3(0, -wHalf * d), color32, new Vector2(1f, 0f));
    28.  
    29. vh.AddTriangle(0, 1, 2);
    30. vh.AddTriangle(2, 3, 0);
    31. vh.FillMesh(m);
    32. }
    33. }
    34. }
     
    Last edited: Sep 17, 2015
    KalebBK likes this.
  16. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    Ah, thanks koohddang.

    I suspected triangles weren't being automatically generated as with onFillVBO. There's probably more control your way but I was too lazy to find out...
     
  17. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,634
    You ok with your diamond primitive being added to the UI Extensions @koohddang ?
    Great work digging out that little nugget.
     
    MrEsquire likes this.
  18. wikmanyo

    wikmanyo

    Joined:
    Nov 17, 2012
    Posts:
    49
    Good luck with this guys, they just changed the API for OnPopulateMesh() in 5.2.1p1

    Go back to 5.1
     
  19. wikmanyo

    wikmanyo

    Joined:
    Nov 17, 2012
    Posts:
    49
    ^^^ OnPopulateMesh(Mesh tofill) is now obsolete too. It lasted all of two weeks! Thanks Unity.
     
  20. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    525
    Wait, for real...?! In the meantime, I adopted my Asset to be both 4.6-5.0 and 5.1+ compatible before submitting. I s*** you not, they actually managed to change the API before my Asset even got approved... sigh Guess I'll have to take a look at this new API then... Thanks for the heads-up @wikmanyo.
     
  21. wikmanyo

    wikmanyo

    Joined:
    Nov 17, 2012
    Posts:
    49
    It gets worse... I've looked at the changes made in 5.2.1p1 using ILSpy, and its bad bad bad.

    They are now forcing us to call VertexHelper.AddVert for each vertex (SLOW)
    They are now forcing us to call VertexHelper.AddTriangle for each triangle (SLOW)

    It's an idiotic change.

    Mesh building is Mesh building. It should be the same regardless if it is for the UI or the Game. The API should be standard for all meshes. The VertexHelper class was clearly supposed to be a temp class to help get the UI using meshes without modifying too much code. I can't believe they have changed the API and are now forcing us to use this method for creating meshes. ITS TOO SLOW.

    5.2 was supposed to be all about optimizing the UI not crippling it!
     
    bluescrn and Lermy-KefSensei like this.
  22. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    525
    Oh wow... In my specific case that's actually good news though, as I am procedurally generating the meshes and was using this exact approach already. In general though... Yikes. I'm not sure how much this will cost performance-wise, but it doesn't seem like the best design. That said, I haven't looked at the specifics yet (and don't have Beta access), so perhaps it's not as bad as it seems. We'll see though.
     
  23. wikmanyo

    wikmanyo

    Joined:
    Nov 17, 2012
    Posts:
    49
    I've re-written my mesh generation code to use the VertexHelper and the results are:

    Good News: The memory leaks are fixed & the Outline/Shadow memory allocations are fixed.

    Bad News: Mesh Generation is now slow as hell because we are forced to use the VertexHelper Class.
    Outlines / Shadows are slow as hell.

    So, far from 5.2 giving me any kind of speed increase, it's actually resulted in a massive CPU bottleneck for me because I am having to call VertexHelper.SetUIVertex() several thousand times per frame.
     
  24. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    525
    ...Ok, that's bad. Since you have a real-world example, I would suggest submitting a bug report. Maybe it's just me, but 5.1+ seems to have a lot of regression bugs compared to before.
     
  25. zge

    zge

    Joined:
    Sep 18, 2014
    Posts:
    35
    I'm only updating when necessary, every 1-5 secs, so I can live with it, although agree that there should be a better way. I'm more concerned with whether it's going to change again with the next update?

    I'm also wondering would it be more efficient to just use a mesh? I'm not sure what benefits the UI is bringing in my case...
     
  26. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,634
    That is an interesting proposition @zge building the UI mesh once and only updating / changing it when there is actual change like adding points, vertices or triangles, or when the Rect Transform changes.
     
  27. Kretin1

    Kretin1

    Joined:
    Sep 29, 2015
    Posts:
    6
    Yeah, that's just what I'm doing. Otherwise I'm not sure how often it gets rebuilt, but way more often than I need. At least I think it's working that way:

    Code (CSharp):
    1.  
    2.   void Update()
    3.    {
    4.         if (condition != conditionCheck)
    5.         {
    6.             if (CanvasUpdateRegistry.IsRebuildingGraphics() || CanvasUpdateRegistry.IsRebuildingLayout())
    7.                 UpdateGeometry();
    8.             else
    9.                 SetAllDirty();
    10.  
    11.             conditionCheck = condition;
    12.         }
    13.    }
    14.  
    (I was @zge )
     
  28. ddf

    ddf

    Joined:
    Jul 9, 2011
    Posts:
    54
    I also hit the performance bottleneck of using VertextHelper pretty much right away. I'm pretty sure it calls OnPopulateMesh every time the RectTransform changes, meaning if you are moving something or changing the width or height or something, it wants the mesh rebuilt.

    My solution was to use VertextHelper the first time OnPopulateMesh is ever called and then cache all the mesh arrays. On subsequent calls to OnPopulateMesh all I have to do is update all the positions and colors because, at least in my case, triangles, uvs, normals, etc never change over the lifetime of the object. I've got an AddRect method that looks like this:

    Code (CSharp):
    1.     void AddRect(VertexHelper vbo, Vector2 corner1, Vector2 corner2, Vector2 corner3, Vector2 corner4, Color32 outColor, Color32 inColor )
    2.     {          
    3.         if ( hasCache )
    4.         {
    5.             positions[rebuildIndex] = corner1;
    6.             colors[rebuildIndex]    = outColor;
    7.  
    8.             positions[rebuildIndex+1] = corner2;
    9.             colors[rebuildIndex+1]     = inColor;
    10.  
    11.             positions[rebuildIndex+2] = corner3;
    12.             colors[rebuildIndex+2]       = inColor;
    13.  
    14.             positions[rebuildIndex+3] = corner4;
    15.             colors[rebuildIndex+3]       = outColor;
    16.  
    17.             rebuildIndex += 4;
    18.         }
    19.         else
    20.         {
    21.             UIVertex vert = UIVertex.simpleVert;
    22.  
    23.             vert.position = corner1;
    24.             vert.color = outColor;
    25.             quad[0] = vert;
    26.          
    27.             vert.position = corner2;
    28.             vert.color = inColor;
    29.             quad[1] = vert;
    30.          
    31.             vert.position = corner3;
    32.             vert.color = inColor;
    33.             quad[2] = vert;
    34.          
    35.             vert.position = corner4;
    36.             vert.color = outColor;
    37.             quad[3] = vert;
    38.  
    39.             vbo.AddUIVertexQuad(quad);
    40.         }
    41.     }
    And then at the end of OnPopulateMesh I do this:

    Code (CSharp):
    1.         if ( hasCache )
    2.         {
    3.             m.Clear();
    4.             m.vertices     = positions;
    5.             m.colors32     = colors;
    6.             m.uv          = uv0s;
    7.             m.uv2         = uv1s;
    8.             m.normals     = normals;
    9.             m.tangents  = tangents;
    10.             m.triangles = triangles;
    11.             m.RecalculateBounds();
    12.         }
    13.         else
    14.         {
    15.             vbo.FillMesh(m);
    16.             positions = m.vertices;
    17.             colors       = m.colors32;
    18.             uv0s      = m.uv;
    19.             uv1s       = m.uv2;
    20.             normals   = m.normals;
    21.             tangents  = m.tangents;
    22.             triangles = m.triangles;
    23.             hasCache  = true;
    24.         }
     
    Senshi likes this.
  29. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,634
    Interesting solution to caching the updates @ddf
    However, how would you invalidate the cache if the mesh changed. This works if the mesh is unaltered but doesn't account for a changing mesh.

    Also, you say you hit the perf issue. Do you have any stats on that, if So, please raise a Unity Bug report on this :D
     
  30. ddf

    ddf

    Joined:
    Jul 9, 2011
    Posts:
    54
    I always rebuild the vertices and colors when OnPopulateMesh is called, it's just that assigning new values to my local position and color arrays is much faster than using the VertexHelper every time. This works in my particular case because the triangles never change. Obviously this wouldn't work if a UI element was generating a different number of verts or triangles based on current settings.

    As for the Mesh changing, it doesn't matter what the engine might do to the Mesh because I always set all the properties of the Mesh when it is passed to OnPopulateMesh. Basically my cache simply always overwrites whatever might be there already. Given the purpose for the method I would not expect the engine to make any assumptions about what kind of state the Mesh should be in before passing it.
     
  31. Dave-Carlile

    Dave-Carlile

    Joined:
    Sep 16, 2012
    Posts:
    961
    Not sure when these functions became available, but VertexHelper has AddUIVertexStream and AddUIVertexTriangleStream methods that allow passing lists of vertices/indices so you don't have to add them one at a time.
     
    Senshi likes this.
  32. Kadaj

    Kadaj

    Joined:
    Jul 24, 2014
    Posts:
    18
    Thank to @zge and @SimonDarksideJ, to have shared your codes and for your comments!

    Thanks to you, I managed to update the UILineRenderer! ;)
     
    Last edited: Feb 15, 2016
  33. ddf

    ddf

    Joined:
    Jul 9, 2011
    Posts:
    54
    How do we get Unity to fix the documentation about this?

    The example on the Graphic documentation page still shows OnFillVBO, which is Obsoleted (http://docs.unity3d.com/ScriptReference/UI.Graphic.html).

    Apparently the correct method to override is now OnPopulateMesh(VertexHelper), but OnPopulateMesh is not shown on the Graphic documentation page because OnPopulateMesh(Mesh) is still around and obsoleted, so I had to google it to turn up this essentially hidden documentation page:
    http://docs.unity3d.com/ScriptReference/UI.Graphic.OnPopulateMesh.html

    And *that* documentation lacks an example and utterly fails to mention that (in Unity 5.3.1 at least) you have to Clear the VertexHelper passed to the method, which also just seems like a bug.

    Did the UI team quit or something?
     
  34. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,634
    No, it's more a documentation team resource thing. I know Unity are trying to tackle the documentation gap issue, so I would expect a lot more movement as we move toward 5.4
     
  35. CaseJnr

    CaseJnr

    Joined:
    May 14, 2014
    Posts:
    43
    I have custom graphics drawn in a scrollrect. Every movement of the scrollrect requires each on the graphics vbo's to replaced rather than simply modifying the matrix. Has anybody figured out how to cache the vbo's. This seems crazy.
     
  36. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,634
    Sadly no. The latest change to the API only made that worse.

    Best solution is to override it completely and cache it yourself at this point in time. The scripts expect change, so if it's causing you a perf bottleneck, then best to handle it yourself. (general 80/20 rule, in 80% of cases it's not an issue :S)
     
unityunity