import java.awt.Graphics;\r
import java.awt.image.BufferedImage;\r
import java.io.File;\r
+import java.io.FilenameFilter;\r
import java.io.IOException;\r
import java.util.ArrayList;\r
\r
import com.badlogic.gdx.Gdx;\r
import com.badlogic.gdx.backends.jogl.JoglApplication;\r
import com.badlogic.gdx.files.FileHandle;\r
+import com.badlogic.gdx.graphics.g2d.tiled.TileAtlas;\r
+import com.badlogic.gdx.graphics.g2d.tiled.TileMapRenderer;\r
import com.badlogic.gdx.graphics.g2d.tiled.TileSet;\r
import com.badlogic.gdx.graphics.g2d.tiled.TiledLayer;\r
import com.badlogic.gdx.graphics.g2d.tiled.TiledLoader;\r
import com.badlogic.gdx.utils.GdxRuntimeException;\r
\r
/**\r
- * Packs a Tiled Map, adding some properties to improve the speed of the {@link TiledMapRenderer}. Also runs the texture packer on\r
+ * Packs a Tiled Map, adding some properties to improve the speed of the {@link TileMapRenderer}. Also runs the texture packer on\r
* the tiles for use with a {@link TileAtlas}\r
* @author David Fraska\r
*/\r
\r
private TexturePacker packer;\r
private TiledMap map;\r
- private int tileCount = 0;\r
\r
private File outputDir;\r
- private FileHandle tmxFileHandle;\r
- private FileHandle imageDirHandle;\r
+ private ArrayList<String> processedTileSets = new ArrayList<String>();\r
\r
private ArrayList<Integer> blendedTiles = new ArrayList<Integer>();\r
\r
+ static private class TmxFilter implements FilenameFilter {\r
+\r
+ public TmxFilter () {\r
+ }\r
+\r
+ @Override public boolean accept (File dir, String name) {\r
+ if (name.endsWith(".tmx")) return true;\r
+\r
+ return false;\r
+ }\r
+\r
+ }\r
+\r
/**\r
- * Packs a Tiled Map, adding some properties to improve the speed of the {@link TiledMapRenderer}. Also runs the texture packer\r
- * on the tiles for use with a {@link TileAtlas}\r
- * @param tmxFile the map's tmx file\r
- * @param imageDir the directory containing tile set images\r
+ * Typically, you should run the {@link TiledMapPacker#main(String[])} method instead of this method.\r
+ * Packs a directory of Tiled Maps, adding properties to improve the speed of the {@link TileMapRenderer}. Also runs the\r
+ * texture packer on the tile sets for use with a {@link TileAtlas}\r
+ * @param inputDir the directory containing tile set images and tmx files\r
* @param outputDir the directory to output a fully processed map to\r
* @param settings the settings used in the TexturePacker\r
* */\r
- public void processMap (File tmxFile, File imageDir, File outputDir, Settings settings) throws IOException {\r
+ public void processMap (File inputDir, File outputDir, Settings settings) throws IOException {\r
this.outputDir = outputDir;\r
\r
- tmxFileHandle = Gdx.files.absolute(tmxFile.getAbsolutePath());\r
- imageDirHandle = Gdx.files.absolute(imageDir.getAbsolutePath());\r
+ FileHandle inputDirHandle = Gdx.files.absolute(inputDir.getAbsolutePath());\r
+ File[] files = inputDir.listFiles(new TmxFilter());\r
\r
- new File(outputDir, tmxFileHandle.name()).delete();\r
- if (outputDir.exists()) {\r
- String prefix = tmxFileHandle.nameWithoutExtension();\r
- for (File file : outputDir.listFiles())\r
- if (file.getName().startsWith(prefix)) file.delete();\r
- }\r
+ for (File file : files) {\r
+ map = TiledLoader.createMap(Gdx.files.absolute(file.getAbsolutePath()));\r
\r
- map = TiledLoader.createMap(tmxFileHandle);\r
+ for (TileSet set : map.tileSets) {\r
+ if (!processedTileSets.contains(set.imageName)) {\r
+ processedTileSets.add(set.imageName);\r
+ packTileSet(set, inputDirHandle, settings);\r
+ }\r
+ }\r
\r
- packMap(map, settings);\r
+ writeUpdatedTMX(map.tmxFile);\r
+ }\r
}\r
\r
- private void packMap (TiledMap map, Settings settings) throws IOException {\r
- packer = new TexturePacker(settings);\r
-\r
+ private void packTileSet (TileSet set, FileHandle inputDirHandle, Settings settings) throws IOException {\r
BufferedImage tile;\r
Vector2 tileLocation;\r
TileSetLayout packerTileSet;\r
Graphics g;\r
\r
- ArrayList<Integer> tilesOnMap = new ArrayList<Integer>();\r
-\r
- // Loop through all tiles on map\r
- for (TiledLayer layer : map.layers) {\r
- for (int row = 0; row < layer.height; row++) {\r
- for (int col = 0; col < layer.width; col++) {\r
- if (layer.tile[row][col] != 0) {\r
- tileCount++;\r
- if (!tilesOnMap.contains(layer.tile[row][col])) {\r
- tilesOnMap.add(layer.tile[row][col]);\r
- }\r
- }\r
- }\r
- }\r
- }\r
+ packer = new TexturePacker(settings);\r
+\r
+ TileSetLayout layout = new TileSetLayout(set, inputDirHandle);\r
\r
- for (int i = 0; i < tilesOnMap.size(); i++) {\r
- //FIXME: this is kind of brute force. We don't know when a tileSet is going to change, so we just assume\r
- //it changes every tile. Should probably keep track of how many tiles are in the tileSet and use that.\r
- packerTileSet = getTileSetLayout(tilesOnMap.get(i)); \r
- tileLocation = packerTileSet.getLocation(tilesOnMap.get(i));\r
- tile = new BufferedImage(packerTileSet.tileSet.tileWidth, packerTileSet.tileSet.tileHeight,\r
- BufferedImage.TYPE_4BYTE_ABGR);\r
+ for (int gid = layout.firstgid, i = 0; i < layout.numTiles; gid++, i++) {\r
+ tileLocation = layout.getLocation(gid);\r
+ tile = new BufferedImage(layout.tileWidth, layout.tileHeight, BufferedImage.TYPE_4BYTE_ABGR);\r
\r
g = tile.createGraphics();\r
- g.drawImage(packerTileSet.image, 0, 0, packerTileSet.tileSet.tileWidth, packerTileSet.tileSet.tileHeight,\r
- (int)tileLocation.x, (int)tileLocation.y, (int)tileLocation.x + packerTileSet.tileSet.tileWidth, (int)tileLocation.y\r
- + packerTileSet.tileSet.tileHeight, null);\r
+ g.drawImage(layout.image, 0, 0, layout.tileWidth, layout.tileHeight, (int)tileLocation.x, (int)tileLocation.y,\r
+ (int)tileLocation.x + layout.tileWidth, (int)tileLocation.y + layout.tileHeight, null);\r
\r
- if (isBlended(tile)) setBlended(tilesOnMap.get(i));\r
+ if (isBlended(tile)) setBlended(gid);\r
\r
- packer.addImage(tile, map.tmxFile.nameWithoutExtension() + "_" + tilesOnMap.get(i));\r
+ packer.addImage(tile, removeExtension(set.imageName) + "_" + i);\r
}\r
\r
- packer.process(outputDir, new File(outputDir, map.tmxFile.nameWithoutExtension() + " packfile"),\r
- tmxFileHandle.nameWithoutExtension());\r
- writeUpdatedTMX();\r
+ packer\r
+ .process(outputDir, new File(outputDir, removeExtension(set.imageName) + " packfile"), removeExtension(set.imageName));\r
+ }\r
+\r
+ private static String removeExtension (String s) {\r
+\r
+ String separator = System.getProperty("file.separator");\r
+ String filename;\r
+\r
+ // Remove the path up to the filename.\r
+ int lastSeparatorIndex = s.lastIndexOf(separator);\r
+ if (lastSeparatorIndex == -1) {\r
+ filename = s;\r
+ } else {\r
+ filename = s.substring(lastSeparatorIndex + 1);\r
+ }\r
+\r
+ // Remove the extension.\r
+ int extensionIndex = filename.lastIndexOf(".");\r
+ if (extensionIndex == -1) return filename;\r
+\r
+ return filename.substring(0, extensionIndex);\r
}\r
\r
private void setBlended (int tileNum) {\r
blendedTiles.add(tileNum);\r
- // System.out.println("TileNum " + tileNum + " is blended");\r
}\r
\r
- private void writeUpdatedTMX () throws IOException {\r
+ private void writeUpdatedTMX (FileHandle tmxFileHandle) throws IOException {\r
Document doc;\r
DocumentBuilder docBuilder;\r
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();\r
try {\r
docBuilder = docFactory.newDocumentBuilder();\r
doc = docBuilder.parse(tmxFileHandle.read());\r
- \r
+\r
Node map = doc.getFirstChild();\r
- while(map.getNodeType() != Node.ELEMENT_NODE || map.getNodeName() != "map"){\r
- if((map = map.getNextSibling()) == null){\r
+ while (map.getNodeType() != Node.ELEMENT_NODE || map.getNodeName() != "map") {\r
+ if ((map = map.getNextSibling()) == null) {\r
throw new GdxRuntimeException("Couldn't find map node!");\r
}\r
}\r
- \r
+\r
setProperty(doc, map, "blended tiles", toCSV(blendedTiles));\r
- setProperty(doc, map, "tile count", String.valueOf(tileCount));\r
\r
TransformerFactory transformerFactory = TransformerFactory.newInstance();\r
Transformer transformer = transformerFactory.newTransformer();\r
valueNode.setNodeValue(value);\r
}\r
}\r
- \r
+\r
private static String toCSV (ArrayList<Integer> values) {\r
String temp = "";\r
for (int i = 0; i < values.size() - 1; i++) {\r
}\r
\r
Node newNode = parent.getOwnerDocument().createElement(child);\r
- \r
+\r
if (childNodes.item(0) != null)\r
return parent.insertBefore(newNode, childNodes.item(0));\r
else\r
return false;\r
}\r
\r
- private TileSetLayout getTileSetLayout (int tileNum) throws IOException {\r
+ private TileSetLayout getTileSetLayout (int tileNum, FileHandle inputDirHandle) throws IOException {\r
int firstgid = 0;\r
int lastgid;\r
\r
for (TileSet set : map.tileSets) {\r
- TileSetLayout layout = new TileSetLayout(set, imageDirHandle);\r
+ TileSetLayout layout = new TileSetLayout(set, inputDirHandle);\r
firstgid = set.firstgid;\r
lastgid = firstgid + layout.numTiles - 1;\r
if (tileNum >= firstgid && tileNum <= lastgid) {\r
return null;\r
}\r
\r
+ /**\r
+ * Processes a \r
+ * @param args args[0]: the input directory containing tmx files and tile set images.\r
+ * args[1]: The output directory, should be empty before running.\r
+ */\r
public static void main (String[] args) {\r
- File tmxFile, baseDir, outputDir;\r
+ File tmxFile, inputDir, outputDir;\r
\r
Settings settings = new Settings();\r
settings.padding = 2;\r
settings.duplicatePadding = true;\r
+ settings.incremental = true;\r
+ settings.alias = true;\r
\r
// Create a new JoglApplication so that Gdx stuff works properly\r
new JoglApplication(new ApplicationListener() {\r
\r
TiledMapPacker packer = new TiledMapPacker();\r
\r
- if (args.length != 3) {\r
- System.out.println("Usage: TMXFILE BASEDIR OUTPUTDIR");\r
- return;\r
+ if (args.length != 2) {\r
+ System.out.println("Usage: INPUTDIR OUTPUTDIR");\r
+ System.exit(0);\r
}\r
\r
- tmxFile = new File(args[0]);\r
- baseDir = new File(args[1]);\r
- outputDir = new File(args[2]);\r
+ inputDir = new File(args[0]);\r
+ outputDir = new File(args[1]);\r
\r
- if (!baseDir.exists()) {\r
- throw new RuntimeException("Base directory does not exist");\r
- }\r
- if (!tmxFile.exists()) {\r
- throw new RuntimeException("TMX file does not exist");\r
+ if (!inputDir.exists()) {\r
+ throw new RuntimeException("Input directory does not exist");\r
}\r
\r
try {\r
- packer.processMap(tmxFile, baseDir, outputDir, settings);\r
+ packer.processMap(inputDir, outputDir, settings);\r
} catch (IOException e) {\r
throw new RuntimeException("Error processing map: " + e.getMessage());\r
}\r