Search Unity

TextMesh Pro How could I create TMP_SpriteAsset programmatically from unity sprite asset

Discussion in 'UGUI & TextMesh Pro' started by jayatubi, Feb 13, 2019.

  1. jayatubi

    jayatubi

    Joined:
    Dec 9, 2013
    Posts:
    143
    I found there is a function TMPro.EditorUtilities.TMP_SpriteAssetMenu.CreateSpriteAsset could create a TMP_SpriteAsset from the selected unity sprite asset. However, it always create the TMP_SpriteAsset with the same name by the side of the unity sprite asset. I'm lookging for a way to create the TMP_SpriteAsset to a custom path without change the code of TMP.
     
    Last edited: Feb 13, 2019
  2. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    What is the use case for creating sprite assets at runtime?
     
  3. jayatubi

    jayatubi

    Joined:
    Dec 9, 2013
    Posts:
    143
    Actually the requirement is not for runtime but for editor. I want to the TMP_SpriteAsset could be auto generated or updated if its associated unity sprite asset has been created or updated.

    For example I would like to use TexturePacker to publish a unity sprite asset into my project. After I back to Unity editor the new published sprite asset will be auto import and I want a TMP_SpriteAsset could be auto generated or updated as well. Basically the
    TMPro.EditorUtilities.TMP_SpriteAssetMenu.CreateSpriteAsset
    could do this for me but I want to change generated TMP_SpriteAsset path according to my project management.

    Anyway, if the TMP could directly use the unity sprite object I could totally get rid of the TMP_SpriteAsset.
     
  4. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    I am planning on making improvements to the workflow related to Sprite Asset such as eventually supporting the Sprite Atlasing system.

    I already made one improvement to updating Sprite Assets which is available in the preview release of TMP version 1.4.0 where you can now Update the Sprite Asset via the context menu as seen below.

    upload_2019-2-12_22-55-32.png

    Provided your texture is still set to Sprite 2D (meaning the Sprite Asset still has a reference to the underlying Unity sprites, then any addition or removal of sprites via the Sprite Editor will be reflect in the Sprite Asset when using this Update Sprite Asset option. Changes / adjustment to the texture coordinates of the sprite will also be picked up. This is not automatic but should make updating sprite assets easier. Give that new feature a try and let me know how it feels.

    Sprite Assets will remain as Unity Sprites have no concept of text layout and relevant metrics like X & Y Bearing and Advance and relative scale to the text. But like I said already, there still could be improvements to the workflows and closer integration between those systems.
     
  5. jayatubi

    jayatubi

    Joined:
    Dec 9, 2013
    Posts:
    143
    As you mentioned this context menu item I want to report an issue to you. The following code stops me from using this function:

    Code (CSharp):
    1. int elementIndex = 0;
    2. // The tempGlyphTable may be empty and a negative index will cause an exception
    3. for (uint i = 0; i < tempGlyphTable[tempGlyphTable.Count - 1].index; i++)
    4. {
    5.     uint currentElementIndex = tempGlyphTable[elementIndex].index;
    6.  
    7.     if (i == currentElementIndex)
    8.         elementIndex += 1;
    9.     else
    10.         availableGlyphIndexes.Add(i);
    11. }
     
  6. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    I will need more information on this.

    What does your tempGlyphTable point to?

    The previous version of Sprite Assets used to have a single list of sprites which was accessed via the spriteInfoList property. However, this was changed in the latest release where Sprite Assets just like Font Assets now have a list of characters and glyphs which in the case of the sprite assets is the spriteCharacterTable and spriteGlyphTable.

    If you are trying to access the old list, it is set to null now after the sprite asset gets converted over to the new format.
     
  7. jayatubi

    jayatubi

    Joined:
    Dec 9, 2013
    Posts:
    143
    Reproduce steps:

    1. Reset the TMP_SpiteAsset via context menu
    2. Re-drag the sprite to the TMP_SpriteAsset's inspector
    3. Click Update Sprite Altas in the context menu

    The issue occurs.
     
  8. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Resetting the Sprite Asset via the Context Menu will blow up the sprite asset since that Reset function has no idea about Sprite Assets.

    Ie. Don't use Reset on Sprite Assets. Having said that, I will override that Reset function to make sure it behaves correctly in the future with sprite assets.
     
  9. jayatubi

    jayatubi

    Joined:
    Dec 9, 2013
    Posts:
    143
    The reason I do these steps is to totally update the TMP_SpriteAsset becuase I changed the pivots of the original sprites. The TMP_SpriteAsset save the pivots as glyph metrics when first create and won't update in the future.

    Otherwise I need to remove the existing TMP_SpriteAsset and re-create it which may cause the dependency lost issue.

    I just want to totally reset the TMP_SpriteAsset without recreated it, aka lose dependency.
     
  10. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    The reason why pivot changes done via the Sprite Editor are not reflected when updating the Sprite Asset via the Context menu is to prevent overriding manual changes that a user might have done to individual sprites in the Sprite Asset Editor.

    I'll take a look when I have time over the next few days to see if I can find some alternatives to allow pivot changes to also work.
     
  11. Umresh

    Umresh

    Joined:
    Oct 14, 2013
    Posts:
    56
    Hi, I want to show the downloaded images in between text and there are some tags in the text Like <Sub> which is supported in the TMP and we can show images in TMP but it has to be a TMP Sprite Asset. Is there a way to create a TMP sprite asset for the downloaded images?
     
  12. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    This has been done by some TMP users previously.

    Since Sprite Assets contains sprite characters and sprite glyphs which ultimately defines the position of a sprite in some texture, you can fill / write these downloaded images into a texture at runtime. Assuming these downloaded images were of the same size, you could create the sprite asset ahead of time where the sprite characters / glyphs are already defined but simply point to an empty texture.

    If the size of the downloaded images isn't known ahead of time then it is trickier as you would need to also modified the sprite data to include the correct metrics for layout and glyphrect to point to the right pixels in the texture.
     
  13. Umresh

    Umresh

    Joined:
    Oct 14, 2013
    Posts:
    56
    Hi, I did it like you said downloaded images and created a single image(spritesheet) and it worked. There is some issues in ordering the TMPsprite using the ID so I had to do with the unicode.
     
  14. moldywarpe

    moldywarpe

    Joined:
    Jan 18, 2017
    Posts:
    31
    Hi Stephan,
    Thought I would add to this thread as it somewhat relates.
    I was dynamically adding sprites to TextMeshPro_UGUI text based on html input.
    I create a new sprite asset then add my sprite to a spriteInfoList for all sprites and when finished add the spriteInfoList to the sprite asset.
    I then add this sprite asset as a fallbackSpriteAsset to a previously created TextMeshPro Sprite Asset that I created within the editor and had set as my default sprite asset within the TMP Settings.
    I then call TMP_SpriteAsset.UpdateLookupTables().
    Everything was working fine under 2018.1.7.

    I have now updated to Unity 2019.1.6 and I have an issue.
    At runtime TMP appears to identify the first of my sprites within my fallbackSpriteAsset as requiring update and calls TMP_SpriteAsset.UpgradeSpriteAsset() e.g.
    Upgrading sprite asset [cover.jpeg] to version 1.1.0.

    At this point all of my UI.Images within my scene throw errors:
    Trying to add Image1 (UnityEngine.UI.Image) for graphic rebuild while we are already inside a graphic rebuild loop. This is not supported.

    Stack :
    Trying to add Image1 (UnityEngine.UI.Image) for graphic rebuild while we are already inside a graphic rebuild loop. This is not supported.
    UnityEditor.AssetDatabase:SaveAssets()
    TMPro.TMP_SpriteAsset:UpgradeSpriteAsset() (at Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs:498)
    TMPro.TMP_SpriteAsset:UpdateLookupTables() (at Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs:129)
    TMPro.TMP_SpriteAsset:GetSpriteIndexFromUnicode(UInt32) (at Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs:203)
    TMPro.TMP_SpriteAsset:SearchForSpriteByUnicodeInternal(TMP_SpriteAsset, UInt32, Boolean, Int32&) (at Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs:318)
    TMPro.TMP_SpriteAsset:SearchForSpriteByUnicodeInternal(List`1, UInt32, Boolean, Int32&) (at Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs:296)
    TMPro.TMP_SpriteAsset:SearchForSpriteByUnicode(TMP_SpriteAsset, UInt32, Boolean, Int32&) (at Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs:262)
    TMPro.TextMeshProUGUI:SetArraySizes(UnicodeChar[]) (at Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_UGUI_Private.cs:1237)
    TMPro.TMP_Text:parseInputText() (at Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Text.cs:1716)
    TMPro.TextMeshProUGUI:OnPreRenderCanvas() (at Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_UGUI_Private.cs:1637)
    TMPro.TextMeshProUGUI:Rebuild(CanvasUpdate) (at Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshProUGUI.cs:209)
    UnityEngine.Canvas:SendWillRenderCanvases()


    After 177 of these errors I get the remainder of my dynamic sprites upgraded as well:
    Upgrading sprite asset [00004.jpeg] to version 1.1.0.
    Upgrading sprite asset [00005.jpeg] to version 1.1.0. etc. etc.

    I am not really sure that this sprite Asset needs upgrading as I am only adding these as fallbackSprites and don't actually have any glyphs or characters within the sprite asset tables.
    So is it possible for me to create sprites that do not require upgrade ... and how?
     
  15. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    The structure of Sprite Assets has changed where now similar to Font Assets, the Sprite Assets contains Sprite Characters which contain the ID, name and unicode of the sprite and reference the Sprite Glyph which contains the metric used for layout purposes. Again this is how characters and glyphs are structured in a font file and in font assets.

    This upgrade process convert from the old data structure which was using the spriteInfoList to the new structure. I would suggest taking a look at the TMP_SpriteAsset.cs file and more specifically the "private void UpgradeSpriteAsset()" function which handles the conversion and will allow you to understand the new structure and how to create sprite asset dynamically as you were before but using the new structure.
     
  16. moldywarpe

    moldywarpe

    Joined:
    Jan 18, 2017
    Posts:
    31
    Hi Stephan,
    I've looked at the code and have attempted to replicate.
    I create the spriteGlyphTable and spriteCharacterTable however cannot add these to the spriteAsset as they are read only. Previously I added the spriteInfoList to the spriteAsset.
    So do I need to modify the spriteGlyphTable and spriteCharacterTable from read only to allow me to set their values?
    Additionally, should I also be setting TMP_SpriteAsset.version (which is also read only) to 1.1.0 in order to avoid TMP calling the UpgradeSpriteAsset() function whenever it gets one of my sprites within the text?

    [Update]
    I modified spriteGlyphTable, spriteCharacterTable to enable me add them to a spriteAsset and this allows me to create the new format spriteAsset. I had to also modify the version to allow me to set the version to 1.1.0 on the dynamically created sprites which avoids TMP calling the UpgradeSpriteAsset().
    Any chance you can provide set accessors on these variables in the next release?
     
    Last edited: Jul 14, 2019
  17. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    I'll take a look and try to get that in the next release

    Similar to how you can create dynamic font assets at runtime, I guess I should also explore something similar for Sprite Assets.
     
  18. moldywarpe

    moldywarpe

    Joined:
    Jan 18, 2017
    Posts:
    31
    Thanks - that would be handy :)
     
  19. organick

    organick

    Joined:
    Jul 17, 2012
    Posts:
    17
    Any updates on creating sprite assets at runtime?
    In my use case, I'm generating a spritesheet/texture from some mod files which I would like to use for TMPro's sprite asset (so I can use these custom icons inline within texts). Seems like in order to do this I need to create a new sprite asset during runtime which still seems to be very difficult and hacky.
     
  20. tomtominc

    tomtominc

    Joined:
    Nov 7, 2013
    Posts:
    22
    @Stephan_B

    Not sure if this is the right forum to post in, but I'm also looking for a way to create sprite assets. Not at runtime but just for my own use. I use pixel fonts and use sprite assets sometimes for displaying custom sprite fonts/icons.

    Creating the sprite assets from the context menu (Create/TextmeshPro/Sprite Asset) gives me zero control over how I want all the sprites offsets set. Because of this, I then need to go into each Sprite Character Table item to set the correct values. This is extremely tedious work, so having my own script has helped a lot.

    For some reason, now my editor script doesn't work, it sets all the Glyph IDs to 0 which only shows the first sprite in the sprite sheet. I looked at how you created the sprite asset and it uses internal lists. I'm not really sure why you hide this info (I'm sure there's a good reason) but I think it would be good to give us control over being able to create our own sprite assets if we need advanced behaviors. Even just another function with parameters would be huge for us.

    Hoping this is something that can be solved, really appreciate your work here :)
     
  21. tobias_froihofer

    tobias_froihofer

    Joined:
    Jul 30, 2015
    Posts:
    56
    @Stephan_B

    Hi, I use the static method TMP_SpriteAssetMenu.CreateSpriteAsset() in the editor to programmatically create several sprite assets at once with the click of a button. The main reason for it is to save time and not have to select each sprite asset in a project individually. It appears that this static method is no longer public in the version 4.0.0-pre1. Is there a reason for this? If not, it would be great if you could make it accessible again like in the previous versions.

    Or is there a more common way to create sprite assets programatically in editor and I just don't know of it yet?
     
    SnarkyPixel likes this.
  22. SnarkyPixel

    SnarkyPixel

    Joined:
    Jul 13, 2018
    Posts:
    19
    Just adding my voice to this. I use a tool (published by tobias_froihofer above) that handily creates sprite assets automatically for you for any number of input devices. It would be a hassle to keep them up to date manually.

    I ran into the above issue after upgrading to TMPro 3.2.0-pre.3 for its SRP Lit material support. I'm currently using reflection (courtesy of Tobias) to bypass the access level, but it feels like a hack. Is there any proper solution for this?