Tiled (TMX) Loader

Any community contributions to libgdx go here! Some may get included in the core API when permission is granted.

Re: Tiled (TMX) Loader

Postby mzechner » Mon Nov 22, 2010 5:12 pm

Ya, i looked at Andengines TMX loader/renderer today as well. No wonder it performs so bad.

I think your idea sounds fine.
mzechner
Site Admin
 
Posts: 4875
Joined: Sat Jul 10, 2010 3:50 pm

Re: Tiled (TMX) Loader

Postby BurningHand » Mon Nov 22, 2010 8:59 pm

Having some time to work on this today, I created a class to build and render a partial map based on the camera position. It is not currently hooked up to any TMX Loader, but it just takes a 2D int array (and a few size parameters) so anything that can generate a 2D int array can load and render tile maps.
IRC: nexsoftware / mobidevelop; GitHub: MobiDevelop;
BurningHand
 
Posts: 2812
Joined: Mon Oct 25, 2010 4:35 am

Re: Tiled (TMX) Loader

Postby NateS » Mon Nov 22, 2010 10:18 pm

aksarfevad wrote:If I use this method, are the sprites returned from SpriteSheet guaranteed to be in the same texture/file? So for my map I could bind the texture from one sprite and then just use the coordinates to get each TileSet? I would have to get the location of each TileSet and then calculate the locations of the tiles.


You point SpriteSheetPacker at a directory. Recursively, for each directory of images it finds, it tries to pack them into a single image. If they don't fit on 1024x1024, then it'll end up with multiple images for a single directory. Example:
Code: Select all
input/tileset1/tileA.png
input/tileset1/tileB.png
input/tileset1/tileC.png
input/tileset2/tileD.png
input/tileset2/tileE.png
input/tileset2/tileF.png


This would output:
Code: Select all
output/pack
output/tileset1.png
output/tileset2.png


In your game, you create a SpriteSheet and point it at the "pack" file. You can then call spriteSheet.get("tileB") and you'll get a Sprite for that tile. If you want to modify the tile images, just repack them and, because you reference them by name, your code doesn't care how they are packed and everything will still work.

For drawing, I imagine the tilemap would be broken into manageable chunks, and Sprites for each chunk that is visible would be put into a SpriteCache.

I agree for each tilemap layer, only one tileset should be used, and it should fit on a single texture (max size of 1024x1024). SpriteSheetPacker and SpriteSheet could still be used, they just wouldn't enforce this rule. IMO, it is enough to document that each tileset should fit packed on one image for performance reasons, and people can break the rules if they want crap performance.

SpriteSheetPacker and SpriteSheet are only a few days old, but are being used to load the images in my 2D RTS game, so they work at least that much. :) If you need additional functionality, I'm all for improving them.
NateS
 
Posts: 1980
Joined: Fri Nov 12, 2010 11:08 am

Re: Tiled (TMX) Loader

Postby aksarfevad » Tue Nov 23, 2010 12:00 am

Thanks for the info Nate. Tiled actually expects all of the tiles in a TileSet to be a single image file. Then, any number of these TileSet images can be in any given layer. These tileSets are then numbered in the map so that each tile has a unique number (even though they are on different images). What I would do is have a similar processing program to SpriteSheetPacker, but have a region for each tile. Then, I would load the tiles into an array with a process similar to SpriteSheet, with the tiles indexed rather than using a string comparison.

If you wanted to modify the SpriteSheetPacker (maybe as a different "TileSheetPacker/TileSheet" set of classes) to work in this way (taking tileWidth, tileHeight, margin, spacing, and firstgid as arguments for each added image) I would be able to use this directly. If you don't want to do it I will probably do this later on after I'm satisfied with how my code works on a single tileSet. It would be very little work to modify it to accept this change anyways.
aksarfevad
 
Posts: 275
Joined: Fri Nov 19, 2010 4:19 am

Re: Tiled (TMX) Loader

Postby mzechner » Tue Nov 23, 2010 1:58 am

Oi, lots of design progress. Keep up the good work people!
mzechner
Site Admin
 
Posts: 4875
Joined: Sat Jul 10, 2010 3:50 pm

Re: Tiled (TMX) Loader

Postby NateS » Tue Nov 23, 2010 4:30 am

aksarfevad wrote:Tiled actually expects all of the tiles in a TileSet to be a single image file. Then, any number of these TileSet images can be in any given layer.

Are you planning to allow this as Tiled does? Is the onus on the user to prevent performance degradation by using only one tileset per layer?

You could allow them to build their tile map any way they want, then build an image per layer that contains all the tiles for that layer. However, the user wouldn't know they messed up and put more tiles on a single layer than fit in a single 1024x1024 image until they run your tool to convert the Tiled format.

These tileSets are then numbered in the map so that each tile has a unique number (even though they are on different images).

Tiles are numbered instead of named, otherwise its the same thing.

What I would do is have a similar processing program to SpriteSheetPacker, but have a region for each tile.

This doesn't make sense to me. Each image packed by SpriteSheetPacker has it's region stored in the "pack" file.

Then, I would load the tiles into an array with a process similar to SpriteSheet, with the tiles indexed rather than using a string comparison.

Whether the images are looked up by string or integer isn't really important. It isn't advised to call SpriteSheet#get to retrieve images per frame. Images should be gotten out and stored somewhere more convenient for repeated use.

If you wanted to modify the SpriteSheetPacker (maybe as a different "TileSheetPacker/TileSheet" set of classes) to work in this way (taking tileWidth, tileHeight, margin, spacing, and firstgid as arguments for each added image) I would be able to use this directly. If you don't want to do it I will probably do this later on after I'm satisfied with how my code works on a single tileSet. It would be very little work to modify it to accept this change anyways.

I'm not convinced any work needs to be done. SpriteSheetPacker can take a directory of tile images, put them all into a single image in an optimal way, and allow you to retrieve those images in your game. How is what you want different from this?
NateS
 
Posts: 1980
Joined: Fri Nov 12, 2010 11:08 am

Re: Tiled (TMX) Loader

Postby BurningHand » Tue Nov 23, 2010 5:12 am

Is there something fundamentally wrong with using a mesh for each layer? It seems to work fine (at least for my needs).
IRC: nexsoftware / mobidevelop; GitHub: MobiDevelop;
BurningHand
 
Posts: 2812
Joined: Mon Oct 25, 2010 4:35 am

Re: Tiled (TMX) Loader

Postby aksarfevad » Tue Nov 23, 2010 5:20 am

Well this is going to get messy. I just want to note from the start that I don't absolutely need this change to happen. I am just stating what I would do in a perfect world to increase performance. There are other ways to get around it, and I had thought of the workarounds you had stated. You said you could improve it if need be so I offered my ideal setup (I hope this didn't come off as pushy).

Nates wrote:
aksarfevad wrote:Tiled actually expects all of the tiles in a TileSet to be a single image file. Then, any number of these TileSet images can be in any given layer.

Are you planning to allow this as Tiled does? Is the onus on the user to prevent performance degradation by using only one tileset per layer?

You could allow them to build their tile map any way they want, then build an image per layer that contains all the tiles for that layer. However, the user wouldn't know they messed up and put more tiles on a single layer than fit in a single 1024x1024 image until they run your tool to convert the Tiled format.


1) I'm not drawing each tile individually. I'm trying to get the best performance possible by using a few triangle strips to draw the map. This will allow for fewer calls to render, thus better performance. This definitely limits me to using only 1 texture per layer. I don't want to limit the user to 1 tileset per layer, so I was hoping to combine multiple tilesets into one image so that users can still fully utilize the tiled format. I could change to using separate sprites for each tile and remove the limitation of using only 1 texture, but I don't feel like that is a good idea. It would slow things down even for users who understand that they shouldn't use multiple textures.

The eventual plan is to just put all tiles for the entire map onto one texture. This is the only way I could figure out how to not waste a ton of memory storing and loading multiple tiles (if say 2 layers both used a tileset). I didn't think about the fact that they wouldn't know if they were about to hit that 1024x1024 limit, that is a valid point and would probably need to be dealt with somehow.

Nates wrote:
aksarfevad wrote:These tileSets are then numbered in the map so that each tile has a unique number (even though they are on different images).

Tiles are numbered instead of named, otherwise its the same thing.


2) Just trying to explain that it would be easy to map the numbered tiles to the sheet. I wasn't sure how much you knew about the Tiled format. I can calculate where each tile is based on your image regions, it would just mean longer load times during these calculations (probably minimal).

Nates wrote:
aksarfevad wrote:What I would do is have a similar processing program to SpriteSheetPacker, but have a region for each tile.

This doesn't make sense to me. Each image packed by SpriteSheetPacker has it's region stored in the "pack" file.


3) SpriteSheetPacker puts each image region. I would want to do each tile of which there are multiple on each image. I can honestly do subregions of each image for each tile, again it would just slow down loading times.

Nates wrote:
aksarfevad wrote:Then, I would load the tiles into an array with a process similar to SpriteSheet, with the tiles indexed rather than using a string comparison.

Whether the images are looked up by string or integer isn't really important. It isn't advised to call SpriteSheet#get to retrieve images per frame. Images should be gotten out and stored somewhere more convenient for repeated use.


4) I agree, again if I were to modify it myself I would go with an indexed array since it makes sense for the Tiled format (not necessarily best for general use).

Nates wrote:
aksarfevad wrote:If you wanted to modify the SpriteSheetPacker (maybe as a different "TileSheetPacker/TileSheet" set of classes) to work in this way (taking tileWidth, tileHeight, margin, spacing, and firstgid as arguments for each added image) I would be able to use this directly. If you don't want to do it I will probably do this later on after I'm satisfied with how my code works on a single tileSet. It would be very little work to modify it to accept this change anyways.

I'm not convinced any work needs to be done. SpriteSheetPacker can take a directory of tile images, put them all into a single image in an optimal way, and allow you to retrieve those images in your game. How is what you want different from this?


5) Again, I hope nothing I say seems like I want you to necessarily do the work. I'm not saying that modifying it is the best use of your time. I realize that there are probably bigger eggs to fry. I'm just offering my opinions and if I'm still interested in it by the time I'm done with what I have I'll probably challenge myself to modify it myself. Your code is probably much tighter than mine could ever be on that anyways.
aksarfevad
 
Posts: 275
Joined: Fri Nov 19, 2010 4:19 am

Re: Tiled (TMX) Loader

Postby aksarfevad » Tue Nov 23, 2010 7:05 am

You can actually ignore my last post... it is probably quicker to just load the regions you have and calculate the tile regions from there. I was stupidly not taking into account the file access time and the fact that I would have to read the pack file more if I tried my way. I'll probably end up using your class as is. Thanks for essentially setting me straight :mrgreen:
aksarfevad
 
Posts: 275
Joined: Fri Nov 19, 2010 4:19 am

Re: Tiled (TMX) Loader

Postby NateS » Tue Nov 23, 2010 8:34 am

aksarfevad wrote:Well this is going to get messy. I just want to note from the start that I don't absolutely need this change to happen. I am just stating what I would do in a perfect world to increase performance. There are other ways to get around it, and I had thought of the workarounds you had stated. You said you could improve it if need be so I offered my ideal setup (I hope this didn't come off as pushy).

Not at all. My e-feelings are all intact. If I were offended, you would know. ;)

1) I'm not drawing each tile individually. I'm trying to get the best performance possible by using a few triangle strips to draw the map. This will allow for fewer calls to render, thus better performance.

I would opt for the GDX classes that handle this for you, rather than dealing with the triangle strips yourself.

This definitely limits me to using only 1 texture per layer. I don't want to limit the user to 1 tileset per layer, so I was hoping to combine multiple tilesets into one image so that users can still fully utilize the tiled format.

Ok. I think this is a reasonable way to give users flexibility without losing performance. It takes up more video memory since the same tile might be on multiple layers, but that is not a huge deal.

I could change to using separate sprites for each tile and remove the limitation of using only 1 texture, but I don't feel like that is a good idea.

To be clear, the GDX class "Sprite" represent a rectangular region of a texture. You can use many Sprite instances from the same texture and you won't have any extra texture binds.

The eventual plan is to just put all tiles for the entire map onto one texture. This is the only way I could figure out how to not waste a ton of memory storing and loading multiple tiles (if say 2 layers both used a tileset). I didn't think about the fact that they wouldn't know if they were about to hit that 1024x1024 limit, that is a valid point and would probably need to be dealt with somehow.

I think it is safe to assume all tiles for all layers in the map will NOT fit on one texture.

3) SpriteSheetPacker puts each image region. I would want to do each tile of which there are multiple on each image.

Ah, I didn't know you had your tile images already packed onto a larger image. In that case you will need to write code that takes a Tiled map and a bunch of "tileset images", which are packed full of tile images. The code would cut the tile images out of the larger tileset image and, for each layer in the Tiled map, write a new image up to 1024x1024 in size that contains all tiles images for that layer. If you run out of room, throw a big error telling the user not to use so many tiles per layer. There is no need to do packing like SpriteSheetPacker, as all your tile images will be the same size and can just be arranged in a grid.

Your code will also need to write out a file that describes where each tile image is in each layer image file. This may be a good chance to rewrite the entire Tiled map file format into something more suitable to the GDX tile map loader. I've never looked at the Tiled format.

I can honestly do subregions of each image for each tile, again it would just slow down loading times.

This is confusing. Region/subregion/image/tile... all of those can mean the same thing. I can't guess at what you mean.

Again, I hope nothing I say seems like I want you to necessarily do the work.

I'm not shy of doing work. I enjoy discussing design. A tile map renderer is complex enough to benefit from lots of eyes on the design before tons of code gets written.

BurningHand wrote:Is there something fundamentally wrong with using a mesh for each layer? It seems to work fine (at least for my needs).

There are a few ways you could use Mesh.

You could add all the tiles to the mesh at the start of your app and then draw the mesh over and over. Your mesh will be very large. I am guessing we want to only draw the part of the map that is visible.

You could add only the visible tiles to the mesh every frame. This is what SpriteBatch does and is easiest to implement, but would repeatedly upload the same geometry to the GPU, which is wasteful.

You could add only the visible tiles to the mesh and then not modify the mesh until new tiles are visible. This, or a variant of this, seems like the best approach.

One mesh per layer would require one VBO bind/unbind for each layer every frame. Anyone know if this is a big deal? How many layers are typical? Note that Mesh#render takes an offset and a count. You could use a single mesh for all layers, so that you only have to bind/unbind once. I don't think there is any advantage to using one mesh per layer.

Every time the tilemap is scrolled one tile width or height, a new column or row becomes visible and you have to redo all layers. Is it worth avoiding this? What is a typical tile size? For 64x64 tiles at 800x480 that's 104+ tiles, for 32x32 it's 375+ tiles. That is per layer, but I'm guessing not all layers are 100% full of tiles? I guess we are dealing with something like 150 tiles on screen at once, up to what? 400+? Is it worth breaking the tile map into smaller pieces? Eg, you could load each 6x6 section of tiles. When the screen scrolls, you wouldn't have to load new tiles as often, and when you did, you'd load considerably fewer tiles. Each 6x6 section could be stored in the same mesh.

To scroll, since you aren't changing the geometry every frame, you'd have to use glTranslate or modify the perspective matrix.

While Mesh is a low level class and could obviously handle tile map rendering, I think it would be great if the SpriteCache class could be used. SpriteCache uses Mesh under the covers and lets you store a bunch of geometry, then draw it repeatedly without changing the geometry. Like SpriteBatch, SpriteCache can be used with Textures or Sprites. A Sprite is just a subregion of a texture, so it makes sense to use a Sprite to represent each tile. SpriteCache works like this:

Code: Select all
cache = new SpriteCache();
cache.beginCache();
cache.add(sprite);
cache.add(texture, x, y, width, height, ...);
int handle = cache.endCache();
// Then later:
cache.draw(handle);
// You can redefine a cache:
cache.beginCache(handle);
cache.add(sprite);
cache.add(texture, x, y, width, height, ...);
cache.endCache();

I just added the ability to redefine an existing cache ID, so be sure to get the latest. SpriteCache seems ideal for tile map rendering. If you find you can't use it, let's please discuss it so that maybe SpriteCache can be fixed.
NateS
 
Posts: 1980
Joined: Fri Nov 12, 2010 11:08 am

PreviousNext

Return to Libgdx Contributions

Who is online

Users browsing this forum: No registered users and 3 guests