prime31/UIToolkit — Issue #48: Plan to support Trim feature of TexturePacker
Author: nsxdavid
Created: 2011-12-03 05:16:55+00:00
State: open

This is a proposed plan to support TexturePacker's trim feature in UIToolkit. The changes would touch a lot of files, so looking for feedback before attempting.

The current code does not support the Trim feature of TexturePacker. This is perhaps one of the more important ones.

Trim finds the smallest rect that encompasses the pixels of an image that are not alpha=0 for each sprite that is packed into the atlas. Many times artists will animate things in sprites and rely on trim to remove this information during packing. This can result in huge savings in texture space.

However, to properly display trimmed sprites from the atlas, the sprites vertices must be moved to account for the discarded part of the image.

To make this work in UIToolkit, I've identified the following changes so far:

The UITextureInfo structure needs to keep track of the original size of the sprite (before it as packed and trimmed). TexturePacker emits this as "sourceSize". Unfortunately the identifier sourceSize was improperly already used in UITextureInfo for the property "sourceSpriteSize" in the control file. But to keep things simple, I suggest:

public struct UITextureInfo
{
public UIUVRect uvRect;
public Rect frame;
public Rect sourceSize;
public Vector2 originalSize;
}

Original size will need to be parsed (snipet from loadTexturesFromTexturePackerJSON):

var originalSize = (Hashtable)((Hashtable)item.Value)["sourceSize"];
var originalW = int.Parse( originalSize["w"].ToString() );
var originalH = int.Parse(originalSize["h"].ToString());

var ti = new UITextureInfo();
ti.frame = new Rect( frameX, frameY, frameW, frameH );
ti.uvRect = new UIUVRect( frameX, frameY, frameW, frameH, textureSize );
ti.sourceSize = new Rect( sourceSizeX, sourceSizeY, sourceSizeW, sourceSizeH );
ti.originalSize = new Vector2(originalW, originalH);

Optimally, an optimization would be to also maintain a bool which tells us if the image was trimmed. This is the "trimmed" property in the control file.

To use this information the code will have to be refactored to understand that when the UVs are changed the vertices might have to also change.

The following code is an example of how this logic would work for a centerized sprite. This is the code I use for camera facing sprites in 3D space (my own custom extension to UIToolkit):

void AssignUV(UITextureInfo textureInfo) // EXAMPLE ONLY
{

float w = textureInfo.originalSize.x;
float h = textureInfo.originalSize.y;

float hw = w / 2.0f;
float hh = h / 2.0f;

float left = textureInfo.sourceSize.left - hw;
float right = textureInfo.sourceSize.right + hw;
float top = textureInfo.sourceSize.top - hh;
float bottom = textureInfo.sourceSize.bottom + hh;

Vector3[] vertices = new Vector3[4];
vertices[0] = new Vector3(left, top, 0);
vertices[1] = new Vector3(left, bottom, 0);
vertices[2] = new Vector3(right, bottom, 0);
vertices[3] = new Vector3(right, top, 0);

OurMesh.vertices = vertices;

Vector2[] UVs = new Vector2[4];

UVs[0] = textureInfo.uvRect.lowerLeftUV + Vector2.up * textureInfo.uvRect.uvDimensions.y; // Upper-left
UVs[1] = textureInfo.uvRect.lowerLeftUV; // Lower-left
UVs[2] = textureInfo.uvRect.lowerLeftUV + Vector2.right * textureInfo.uvRect.uvDimensions.x; // Lower-right
UVs[3] = textureInfo.uvRect.lowerLeftUV + textureInfo.uvRect.uvDimensions; // Upper-right

OurMesh.uv = UVs;

}

This basic strategy would be adapted in UIToolkit.

The difficulty in this change is that UIToolkit currently makes the assumption that UV changes are very distinct of Vertex changes. And in many places where it will need to keep track of UV information it keeps and passes the UIUVRect of the UITextureInfo struct. For example, the Sprite Animaiton system has an array of UIUVRects for the animation frames. This will have to be refactored to an array of UITextureInfo structs. Similarly with many other places in the code.

It is the passing/tracking ofUI UVRects instead of UITextureInfo throughout the code that makes this refactor tricky.

I've not looked into how clipping might be impacted by this as I don't quite yet have my head around the clipping implementation.

There are other implications, as the actual "size" of the sprite is represented by something other than how the verts are positioned on the screen. This might impact touchable areas as well.

It is worth noting that support for the rotated property (another key feature of TexturePacker) can easily be part of this refactor. In fact to support rotated, moving from passing/storing UITextureInfo's instead of UIUVRects is pretty much required anyway.