Author Topic: Ingredient list for supporting TIGHT sprite packing with UI2DSprite  (Read 3311 times)

Wisteso

  • Full Member
  • ***
  • Thank You
  • -Given: 21
  • -Receive: 3
  • Posts: 103
    • View Profile
ArenMook or whomever might know:

Our game uses a lot of sprites that benefit hugely from using tight packing. Rectangle based packing is wasting a lot of valuable texture memory and forces us to mix in SpriteRender components with our NGUI components in cases where we simply cannot afford rectangle based packing in our texture budget.

Edit: Read the last most post for the semi hacky approach that mostly works for using tightly packed sprites with UI2DSprite. This seems like the most realistic way to do it until/unless NGUI supports drawing meshes based on triangles rather than quads.
« Last Edit: August 26, 2016, 07:32:16 PM by Wisteso »

Wisteso

  • Full Member
  • ***
  • Thank You
  • -Given: 21
  • -Receive: 3
  • Posts: 103
    • View Profile
Re: Ingredient list for supporting TIGHT sprite packing with UI2DSprite
« Reply #1 on: August 26, 2016, 06:12:16 PM »
After using the following code I found some issues with NGUI's support for geometry that isnt a quad...

  1. public override void OnFill (BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color> cols)
  2. {
  3.         Texture tex = mainTexture;
  4.         if (tex == null) return;
  5.  
  6. #if UNITY_EDITOR
  7.         if (mSprite.packed && mSprite.packingMode == SpritePackingMode.Tight)
  8.         {
  9.             var gc = drawingColor;
  10.             var sprUVs = mSprite.uv;
  11.             var sprVerts = mSprite.vertices;
  12.             var tris = mSprite.triangles;
  13.             var ppu = mSprite.pixelsPerUnit * pixelSize;
  14.  
  15.             for (int i = 0; i < tris.Length; ++i)
  16.             {
  17.                 verts.Add(sprVerts[tris[i]] * ppu);
  18.                 uvs.Add(sprUVs[tris[i]]);
  19.                 cols.Add(gc);
  20.             }
  21.  
  22.             return;
  23.         }
  24. #endif
  25.  
  26. (rest of method was unchanged)
  27.  

UIDrawCall.GenerateCachedIndexBuffer runs into an IndexOutofRangeException at line 557. I'm guessing this is due to the draw call expecting quads and making certain assumptions in how it reads the vertices. Seems to be something that can NOT be ignored.

UIDrawCall.UpdateGeometry assumed the geometry is bad due to part of the IF check that looks for "(verts.size % 4) == 0". I had to remove that one check and then it seemed to behave fine.

UI2DSpriteEditor.OnPreviewGUI cannot render a preview at line 89 since it also expects a quad based sprite. Though this is easy to skip for tightly packed sprites, for the short term, by adding more conditions in the IF check.

Lastly, the mesh looks 75% correct but the mesh is drawn out of order with several extra triangles even though I'm pretty sure I have the right ordering code for mesh rendering. I'm guessing this is also due to NGUI expecting quads for the mesh, like GenerateCachedIndexBuffer.

(for attachment: left image is UI2DSprite, right image is SpriteRenderer)

Wisteso

  • Full Member
  • ***
  • Thank You
  • -Given: 21
  • -Receive: 3
  • Posts: 103
    • View Profile
Re: Ingredient list for supporting TIGHT sprite packing with UI2DSprite
« Reply #2 on: August 26, 2016, 07:12:09 PM »
Found a workaround for the "vertices % 4 == 0" rule which is implicitly required / enforced in many spots with NGUI.

Unity's tightly packed Sprites are based on triangles. By adding the first vertex of each tri twice, it effectively turns every triangle into a quad. A very deformed quad with one half effectively invisible (a volume of ~0), but still technically a quad and it seems to work for NGUI (even works with soft clipping panels).

It would be awesome if I can find a way to make NGUI play nice with multiple-of-three vertices meshes, but at least this looks 100% correct and allows for basic use of tight sprites with UI2DSprite. Hopefully the extra phantom vertex doesn't hurt performance much.

  1. if (mSprite.packed && mSprite.packingMode == SpritePackingMode.Tight)
  2. {
  3.     var gc = drawingColor;
  4.     var sprUVs = mSprite.uv;
  5.     var sprVerts = mSprite.vertices;
  6.     var tris = mSprite.triangles;
  7.     var ppu = mSprite.pixelsPerUnit * pixelSize;
  8.  
  9.     for (int i = 0; i < tris.Length; ++i)
  10.     {
  11.         var index = tris[i];
  12.         verts.Add(sprVerts[index] * ppu);
  13.         uvs.Add(sprUVs[index]);
  14.         cols.Add(gc);
  15.  
  16.         if (i % 3 == 0)
  17.         {
  18.             verts.Add(sprVerts[index] * ppu);
  19.             uvs.Add(sprUVs[index]);
  20.             cols.Add(gc);
  21.         }
  22.     }
  23.  
  24.     return;
  25. }
  26.  
« Last Edit: August 26, 2016, 07:27:39 PM by Wisteso »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Ingredient list for supporting TIGHT sprite packing with UI2DSprite
« Reply #3 on: August 30, 2016, 07:25:39 PM »
The UI is generally made of quads, which is why NGUI was designed around 4 vertex rule (4 vertices = 2 triangles), since NGUI generates the triangles itself. Back when it was set in stone there were no Unity2D or sprites of any kind. Your solution is an interesting work-around, and it's unlikely that it will cause any noticeable performance degradation. I agree that it would be nice if it wasn't necessary though.