FlashPunk TextureAtlas


(Zachary Lewis) #1

Continuing the discussion from Spritemap with different sized sub-images?:

I’ve been looking into setting up a TextureAtlas class for FlashPunk, and I’ve made some good progress!

flashpunktextureatlas.swf(127.3 KB)

The above flash file only has a single texture and some XML.

<TextureAtlas>
	<SubTexture name='player_walk_0' x='0' y='0' width='67' height='92' frameX='4' frameY='5' frameWidth='73' frameHeight='97' />
	<SubTexture name='player_walk_1' x='67' y='0' width='66' height='93' frameX='5' frameY='4' frameWidth='73' frameHeight='97' />
	<SubTexture name='player_walk_2' x='133' y='0' width='67' height='92' frameX='5' frameY='3' frameWidth='73' frameHeight='97' />
	<SubTexture name='player_walk_3' x='0' y='93' width='67' height='93' frameX='6' frameY='1' frameWidth='73' frameHeight='97' />
	<SubTexture name='player_walk_4' x='67' y='93' width='66' height='93' frameX='7' frameY='0' frameWidth='73' frameHeight='97' />
	<SubTexture name='player_walk_5' x='133' y='93' width='71' height='92' frameX='2' frameY='0' frameWidth='73' frameHeight='97' />
	<SubTexture name='player_walk_6' x='0' y='186' width='71' height='93' frameX='2' frameY='0' frameWidth='73' frameHeight='97' />
	<SubTexture name='player_walk_7' x='71' y='186' width='71' height='93' frameX='1' frameY='2' frameWidth='73' frameHeight='97' />
	<SubTexture name='player_walk_8' x='142' y='186' width='70' height='93' frameX='1' frameY='3' frameWidth='73' frameHeight='97' />
	<SubTexture name='player_walk_9' x='0' y='279' width='71' height='93' frameX='0' frameY='4' frameWidth='73' frameHeight='97' />
	<SubTexture name='player_walk_10' x='71' y='279' width='67' height='92' frameX='4' frameY='5' frameWidth='73' frameHeight='97' />
	<SubTexture name='background' x='212' y='0' width='256' height='256'/>
	<SubTexture name='tileset' x='212' y='256' width='280' height='140'/>
	<SubTexture name='tile_0' x='212' y='256' width='70' height='70'/>
	<SubTexture name='tile_1' x='282' y='256' width='70' height='70'/>
	<SubTexture name='tile_2' x='352' y='256' width='70' height='70'/>
	<SubTexture name='tile_3' x='422' y='256' width='70' height='70'/>
	<SubTexture name='tile_4' x='212' y='326' width='70' height='70'/>
	<SubTexture name='tile_5' x='282' y='326' width='70' height='70'/>
	<SubTexture name='tile_6' x='352' y='326' width='70' height='70'/>
	<SubTexture name='item_coin' x='468' y='0' width='36' height='36'/>
	<SubTexture name='item_star' x='468' y='36' width='34' height='32' frameX='1' frameY='2' frameWidth='36' frameHeight='36'/>
	<SubTexture name='item_fireball' x='468' y='68' width='22' height='24' frameX='7' frameY='6' frameWidth='36' frameHeight='36'/>
</TextureAtlas>

Currently, only support for Image exists, but I’ve got plans to allow a user create any of FlashPunk’s graphics from a TextureAtlas.

The XML is similar to that of Starling, making cross-compatibility easy.

Take a look at the TextureAtlas testbed for usage of TextureAtlas, or take a look at the texture-atlas branch of the FlashPunk repository to see my changes. Feel free to fork and contribute!


Sprite Atlas in FlashPunk
Spritemap with different sized sub-images?
Spritemap with different sized sub-images?
(Zachary Lewis) #2

I did a bit more work done, and added atlased texture support for Backdrop and Tileset!

Check out the fruits! flashpunk-texture-atlas.swf(142.6 KB) :banana::apple::pear:


(rostok) #3

Guys, can you give some insight on why texture atlases are so cool? I know that it makes sense in 3d where texture switching takes time, and this approach is particularly useful with foliage objects. I also know about conserving memory and reusing space with transparent alpha. But is there a real gain in addition to things aforementioned?

@zachwlewis - I noticed your TextureAtlas will make extra BitmapData getImage() since Image must create buffer. So resources are doubled in mem. Am I right?

Don’t get me wrong, I am not making a fuss but trying to get the idea behind this concept.


(Zachary Lewis) #4

1. Resource Savings

As of right now, FlashPunk (for the most part) uses smart BitmapData caching (take a look at FP.getBitmap).

Essentially, if a BitmapData exists for a given name, it will return a previously generated one. Similarly, if a Graphic is given a BitmapData, it doesn’t have to search for anything, it just sets its source as a pointer to the provided BitmapData.

Picture this scenario: I have two images, myCoin.png and myPlayer.png, and I want to add a bunch of coins to the level, as well as a player.

for (var i:uint = 0; i < 100; i++)
{
  addGraphic(new Image(COIN_ART), 0, FP.rand(FP.width), FP.rand(FP.height));
}

addGraphic(new Image(PLAYER_ART));

Even though I’m making a bunch of coins, they all simply have a reference to the single BitmapData created the first time I made a coin.

Now, take your entire game’s assets at put them on a single BitmapData. Draw calls have never been more happy.

FlashPunk does weird stuff with the Tilemap, making them not as resource friendly. Additionally, TextureAtlas is only on its best behavior when creating an Image (although, there are plans to clean up its act in the todo list.

2. It’s All About The Atlas

It’s fine cramming tons of images into a single picture and cutting them out, but the power comes from the atlas describing all the different images stored inside.

Have you ever tried to make an animation where a ball explodes, only to realize that your final explosion would have to be 300px wide? This means that all of your sprites would have to be 300px wide for Spritemap to function properly. That’s a lot of wasted space.

You can’t diff a binary file (like an image); however, you can diff a dang ol’ XML file. Suddenly, there’s context in your repository. You can see what images have been (or were supposed to have been) added.

Plus, there’s something slightly more satisfying letting your TextureAtlas do the work.

// Uh-oh, I hope I remember what my offsets were or things are gonna' get bad.
var img:Image = new Image(HEALTH_BAR, new Rectangle(2, 5, 100, 8));

I’m bored already!

// Alright TextureAtlas, you got this. I'm getting coffee.
var img:Image = atlas.getImage("healthbar");

3. Pipelines & Wrenches

There are currently tons of tools for generating texture atlases, and many game development pipelines encourage the use of texture atlases. It would be silly not to leverage this overwhelming urge to use texture atlases to our advantage.

In six months, if someone is shopping for a 2D engine in Actionscript and they “want to use this texture atlas,” they’re gonna’ have to go elsewhere (and I don’t want that! Come back, future-devs! What does the future hold!? WHAT IS MY KLOUT!?).


(Draknek) #5

@zachwlewis Just want to say that I’m not a fan of adding anything to FlashPunk that requires you to use a specific file-format. Your TextureAtlas code is a standalone class and I’d like to keep it that way rather than officially integrating it into FlashPunk.


(Zachary Lewis) #6

@Draknek Do you mean .xml files, or the actual atlas markup?

<SubTexture name='textureName' x='xOrigin' y='yOrigin' width='imageWidth' height='imageHeight'/>

It would be easy enough to abstract all that out, but it’s the same format that Starling uses and works well with texture atlas creation tools.

As I mentioned previously, texture atlases are a very popular method of packaging assets for pretty much any medium (mobile, web, desktop) and I believe it would be a framework limitation to not support them.

The current implementation of TextureAtlas is not part of the official FlashPunk repository: It’s simply a development branch; however, it can’t exist as a standalone class. The fact that many of the FlashPunk Graphic classes don’t have support for clipping masks is something that I’d like to fix. Ideally, I believe that functionality should be present in the current Graphic children making them more powerful and lean, but that would require modifications to said classes.

Alternatively, I could create a set of transparent classes that fulfill the code contracts of the existing Graphic classes, but that would leave the current classes just as lacking. It’s definitely something worth discussing.

@Draknek What are your suggestions as to how to fix the current method of listing textures in the atlas?


(Draknek) #7

@zachwlewis I don’t know what you mean by “the actual atlas markup”, but I meant requiring you to use xml files of a specific format. Takes us down the road of “you must do things FlashPunk’s way” rather than “FlashPunk will let you do things how you want to do them”.

Honestly, I’m not completely convinced this feature adds much to FlashPunk: you’re not going to get a noticeable difference in memory usage or performance. There is a minor benefit of being able to use the same assets or workflow as on other projects for other platforms, but that’s all really.

That said, making improvements to some of the Graphics to allow this is fine as long as it doesn’t detract from the experience of people who don’t know/care about this feature.

Regarding the use/non-use of xml, I’d suggest having a TextureAtlas.addTexture(name, x, y, w, h) method as the primary way of defining textures. The use of an xml file can be described in the tutorial for the feature.


(Zachary Lewis) #8

If done correctly, it will reduce the memory use (since all Graphic objects will reference a shared BitmapData as opposed to keeping their own) and, as you’d mentioned, it will allow people to reuse assets, making FlashPunk an attractive option for both ports and prototypes.

I intend for the addition of texture atlases to be completely backwards compatible, so we’re on the same page there!

I agree completely. I’d mentioned this in the other post, then completely forgot about it! This should be the way it works. That way, users can add textures manually or through a custom loop reading from XML if they’d like.


(Draknek) #9

This is probably not worth debating, but I really don’t see the argument for this reducing memory use. Whenever a graphic is getting its data from a single shared texture atlas, it would otherwise have been getting that data from a single shared embedded image. There’s no difference!


(Zachary Lewis) #10

You’re right, but in the example above, there’s only one BitmapData. Without the atlas, there would be a BitmapData for the player, a BitmapData for the coin, a BitmapData for the fireball, a BitmapData for the background and a BitmapData for the tileset. That’s an improvement of 500%!


(Draknek) #11

You can’t seriously be suggesting that “number of bitmaps used” is a valid metric for memory usage…

The total memory used by one texture atlas containing five images is, at minimum, equal to the total memory used by five images. In all likelihood, you have wasted space so it uses more memory but either way the difference is negligible.


(Jeff Williams) #12

This sentence hurts me in the brain space :cold_sweat:

Take the above tile map as an example:


(Alex Larioza) #13

I have to agree with Draknek here. For example, this atlas is 39,320 bytes:

While the total of all the individual images is 38,454 bytes. As you can see the difference is quite small, but the atlas will end up using more memory. The only time atlases would make a noticeable performance improvement is on mobile where texture swapping becomes more of an issue.


(Draknek) #15

@blueryth Ugh, I considered pointing out that possibility but then decided “nah, we can all be reasonable here and assume I’m not talking about contrived scenarios that aren’t relevant to how this feature would actually be used, right?”

@SHiLLySiT It’s worth noting that FlashPunk on mobile doesn’t work in a way that texture swapping is a thing. So yes, for many engines, texture atlases will give you a performance boost, but FlashPunk is not one of them.

EDIT: and I’m going to pre-emptively reply to another point because this discussion isn’t going to go quietly is it? Yes, the image posted above includes an expanding explosion which would take slightly more memory in a Spritemap-style image but A) most animations aren’t going to change their size that much and B) who cares this stuff has zero actual impact on the game seriously this discussion is getting totally sidetracked by irrelevant points


(Jeff Williams) #16

@SHiLLySiT If we’re going to split hairs on the size, let’s split hairs on atlas packing:

Its only going to save you about 500~1000 bytes in PNG depending on the library doing the compression, but that would put the two in a dead heat for separated versus combined size. Negative space is super cheap, but it adds up over a couple hundred pixels.

@Draknek I know my ‘unreasonable’ use is not doing it the FlashPunk way, but I think that makes the point of the tools all the more important. Either there is a single right way to do things, or empower the user to take his own approach.

I would rather pack textures together and keep one anchor to my content in a project than clutter the code with asset dependencies. This is the process I’m used to in 3D packages, and as such is more familiar. I get that its all preference, but that really speaks to the power of a platform if it can support my approach, or force me into its paradigm.


(Draknek) #17

Except you can’t actually pack them that tightly because you’ll get one sprite bleeding into another at the corners.

I have no problem with anyone having a personal preference about how their assets are stored! Different people find different workflows convenient. Just don’t argue that texture atlases save memory or improve performance, because they don’t!


(Zachary Lewis) #18

tl;dr

SUDDENLY


(Jeff Williams) #19

=/


(Zachary Lewis) #20

As per @Draknek’s suggestion, I’ve made TextureAtlas more flexible by allowing the user to roll their own atlas generation.

Here’s the latest build of my testbed. flashpunktextureatlas.swf(144.7 KB)

Using TextureAtlas

To define a new Subtexture on the TextureAtlas, use defineSubtexture. To create an Image, use getImage.

// Create a TextureAtlas with a given texture.
var myAtlas:TextureAtlas = new TextureAtlas(TEXTURE);

// Define a Subtexture for the player's graphic.
myAtlas.defineSubtexture("thePlayer", 128, 64, 32, 56, 8, 4, 48, 64);

// Create an Image for the player from the TextureAtlas.
var playerImage:Image = myAtlas.getImage("thePlayer");

Implementation

Users can now implement subtexture definition however they choose. In my testbed, I currently iterate through all the subtextures defined in an XML file (like what would be generated with TexturePacker or a similar tool); however, you could simply define all your regions in your code without having to mess with XML if you’d like.


(Tareq) #21

Umm, I’m one of those people where you can explain something so easy to understand, that I when I read it I’ll completely miss the point so I do apologise for this very silly question I’m about to ask; So, is a TextureAtlas just one big image that contains sprites which include animations as well as static images, and all you need to do is provided a width and height as well as a frame number?