package Roguelike.desktop; import Roguelike.Global; import Roguelike.Sprite.TilingSprite; import Roguelike.Util.EnumBitflag; import Roguelike.Util.ImageUtils; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.tools.texturepacker.TexturePacker; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.ObjectSet; import com.badlogic.gdx.utils.XmlReader; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Created by Philip on 17-Jan-16. */ public class AtlasCreator { private TexturePacker packer; private ObjectSet<String> packedPaths = new ObjectSet<String>( ); public AtlasCreator() { buildTilingMasksArray(); TexturePacker.Settings settings = new TexturePacker.Settings(); settings.combineSubdirectories = true; settings.duplicatePadding = true; settings.maxWidth = 2048; settings.maxHeight = 2048; settings.paddingX = 4; settings.paddingY = 4; settings.useIndexes = false; settings.filterMin = Texture.TextureFilter.MipMapLinearLinear; settings.filterMag = Texture.TextureFilter.MipMapLinearLinear; packer = new TexturePacker( new File("Sprites"), settings ); findFilesRecursive( new File("").getAbsoluteFile() ); // pack fog processTilingSprite( "Masks/fog", "Masks/fog", false ); // pack default strike processSprite( "EffectSprites/Strike/Strike" ); // pack default stuff processSprite( "white" ); processSprite( "blank" ); processSprite( "Oryx/uf_split/uf_items/satchel" ); processSprite( "Oryx/uf_split/uf_items/crystal_sun" ); processSprite( "Oryx/uf_split/uf_items/crystal_blood" ); processSprite( "Oryx/uf_split/uf_items/chest_gold" ); processSprite( "Oryx/uf_split/uf_terrain/floor_set_grey_9" ); processSprite( "Oryx/Custom/terrain/door_wood_h_closed" ); processSprite( "Oryx/Custom/terrain/door_wood_h_open" ); processSprite( "Oryx/Custom/terrain/door_wood_v_closed" ); processSprite( "Oryx/Custom/terrain/door_wood_v_open" ); // pack GUI File guiDir = new File( "Sprites/GUI" ); File[] guiFiles = guiDir.listFiles(); for ( File file : guiFiles ) { if ( file.getPath().endsWith( ".png" ) ) { packer.addImage( file ); } } File outDir = new File( "Atlases" ); File[] contents = outDir.listFiles(); for ( File file : contents ) { if ( file.getPath().endsWith( ".png" ) ) { file.delete(); } else if ( file.getPath().endsWith( ".atlas" ) ) { file.delete(); } } packer.pack( outDir, "SpriteAtlas" ); } private void findFilesRecursive( File dir ) { File[] contents = dir.listFiles(); if ( contents == null ) { return; } for ( File file : contents ) { if ( file.isDirectory() ) { findFilesRecursive( file ); } else if ( file.getPath().endsWith( ".xml" ) ) { parseXml( file.getPath() ); } } } private void parseXml( String file ) { XmlReader reader = new XmlReader(); XmlReader.Element xml = null; try { xml = reader.parse( Gdx.files.internal( file ) ); } catch ( Exception e ) { return; } if (xml == null) { return; } Array<XmlReader.Element> spriteElements = new Array<XmlReader.Element>( ); spriteElements.addAll( xml.getChildrenByNameRecursively( "Sprite" ) ); spriteElements.addAll( xml.getChildrenByNameRecursively( "Icon" ) ); spriteElements.addAll( xml.getChildrenByNameRecursively( "UseSprite" ) ); spriteElements.addAll( xml.getChildrenByNameRecursively( "HitSprite" ) ); spriteElements.addAll( xml.getChildrenByNameRecursively( "MovementSprite" ) ); spriteElements.addAll( xml.getChildrenByNameRecursively( "ReplacementSprite" ) ); spriteElements.addAll( xml.getChildrenByNameRecursively( "AdditionalSprite" ) ); for ( XmlReader.Element el : spriteElements ) { boolean found = processSprite( el ); if ( !found ) { throw new RuntimeException( "Failed to find sprite for file: " + file ); } } Array<XmlReader.Element> tilingSpriteElements = xml.getChildrenByNameRecursively( "TilingSprite" ); for ( XmlReader.Element el : tilingSpriteElements ) { boolean succeed = processTilingSprite( el ); if ( !succeed ) { throw new RuntimeException( "Failed to process tiling sprite in file: " + file ); } } } private boolean processTilingSprite( XmlReader.Element spriteElement ) { XmlReader.Element topElement = spriteElement.getChildByName( "Top" ); if (topElement != null) { // Predefined sprite XmlReader.Element overhangElement = spriteElement.getChildByName( "Overhang" ); XmlReader.Element frontElement = spriteElement.getChildByName( "Front" ); boolean exists = tryPackSprite( topElement ); if ( !exists ) { return false; } exists = tryPackSprite( frontElement ); if ( !exists ) { return false; } if ( overhangElement != null ) { exists = tryPackSprite( overhangElement ); if ( !exists ) { return false; } } } else { // Auto masking sprites XmlReader.Element spriteDataElement = spriteElement.getChildByName( "Sprite" ); String texName = spriteDataElement.get( "Name" ); String maskName = spriteElement.get( "Mask" ); boolean additive = spriteElement.getBoolean( "Additive", false ); boolean succeed = processTilingSprite( texName, maskName, additive ); if ( !succeed ) { return false; } } return true; } private boolean processTilingSprite( String baseName, String maskBaseName, boolean additive ) { for ( Array<String> mask : tilingMasks ) { boolean succeed = maskSprite( baseName, maskBaseName, mask, additive ); if ( !succeed ) { return false; } } return true; } private boolean maskSprite( String baseName, String maskBaseName, Array<String> masks, boolean additive ) { // Build the mask suffix String mask = ""; for ( String m : masks) { mask += "_" + m; } String maskedName = baseName + "_" + maskBaseName + mask + "_" + additive; // File exists on disk, no need to mask if ( tryPackSprite( maskedName ) ) { System.out.println( "Added Tiling sprite: " + maskedName ); return true; } FileHandle baseHandle = Gdx.files.internal( "Sprites/" + baseName + ".png" ); if ( !baseHandle.exists() ) { System.err.println( "Failed to find sprite for: " + baseName ); return false; } Pixmap base = new Pixmap( baseHandle ); Pixmap merged = additive ? new Pixmap( base.getWidth(), base.getHeight(), Pixmap.Format.RGBA8888 ) : base; for (String maskSuffix : masks) { FileHandle maskHandle = Gdx.files.internal( "Sprites/" + maskBaseName + "_" + maskSuffix + ".png" ); if ( !maskHandle.exists() ) { maskHandle = Gdx.files.internal( "Sprites/" + maskBaseName + "_C.png" ); } if ( !maskHandle.exists() ) { maskHandle = Gdx.files.internal( "Sprites/" + maskBaseName + ".png" ); } if ( !maskHandle.exists() ) { System.err.println( "Failed to find mask for: " + maskBaseName + "_" + maskSuffix ); return false; } Pixmap maskPixmap = new Pixmap( maskHandle ); Pixmap currentPixmap = additive ? base : merged; Pixmap maskedTex = ImageUtils.multiplyPixmap( currentPixmap, maskPixmap ); if (additive) { Pixmap addedText = ImageUtils.addPixmap( merged, maskedTex ); merged.dispose(); maskedTex.dispose(); merged = addedText; } else { if (merged != base) { merged.dispose(); } merged = maskedTex; } } BufferedImage image = ImageUtils.pixmapToImage( merged ); merged.dispose(); String path = "Sprites/"+maskedName+".png"; packer.addImage( image, maskedName ); packedPaths.add( path ); System.out.println( "Added Tiling sprite: " + maskedName ); return true; } private boolean tryPackSprite( XmlReader.Element element ) { String name = element.get( "Name" ); boolean exists = tryPackSprite( name ); if ( !exists ) { System.err.println( "Could not find sprites with name: " + name ); return false; } else { System.out.println( "Added sprites for name: " + name ); return true; } } private boolean tryPackSprite( String name ) { String path = "Sprites/" + name + ".png"; if ( packedPaths.contains( path ) ) { return true; } FileHandle handle = Gdx.files.internal( path ); if ( handle.exists() ) { packer.addImage( handle.file() ); packedPaths.add( path ); return true; } else { return false; } } private boolean processSprite( XmlReader.Element spriteElement ) { String name = spriteElement.get( "Name", null ); if (name == null) { return true; } return processSprite( name ); } private boolean processSprite( String name ) { int foundCount = 0; // Try 0 indexed sprite int i = 0; while ( true ) { boolean exists = tryPackSprite( name + "_" + i ); if ( !exists ) { break; } else { foundCount++; } i++; } // Try 1 indexed sprite if ( foundCount == 0 ) { i = 1; while ( true ) { boolean exists = tryPackSprite( name + "_" + i ); if ( !exists ) { break; } else { foundCount++; } i++; } } // Try sprite without indexes if ( foundCount == 0 ) { boolean exists = tryPackSprite( name ); if ( exists ) { foundCount++; } } if ( foundCount == 0 ) { System.err.println( "Could not find sprites with name: " + name ); } else { System.out.println( "Added sprites for name: " + name ); } return foundCount > 0; } public static Array<Array<String>> tilingMasks = new Array<Array<String>>( ); public static void buildTilingMasksArray() { HashSet<Global.Direction> directions = new HashSet<Global.Direction>( ); for ( Global.Direction dir : Global.Direction.values() ) { directions.add( dir ); } Set<Set<Global.Direction>> powerSet = powerSet( directions ); HashSet<String> alreadyAdded = new HashSet<String>( ); for ( Set<Global.Direction> set : powerSet ) { EnumBitflag bitflag = new EnumBitflag( ); for ( Global.Direction dir : set ) { bitflag.setBit( dir ); } Array<String> masks = TilingSprite.getMasks( bitflag ); String mask = ""; for ( String m : masks) { mask += "_" + m; } if ( !alreadyAdded.contains( mask ) ) { tilingMasks.add( masks ); alreadyAdded.add( mask ); } } } public static <T> Set<Set<T>> powerSet(Set<T> originalSet) { Set<Set<T>> sets = new HashSet<Set<T>>(); if (originalSet.isEmpty()) { sets.add(new HashSet<T>()); return sets; } List<T> list = new ArrayList<T>( originalSet); T head = list.get(0); Set<T> rest = new HashSet<T>(list.subList(1, list.size())); for (Set<T> set : powerSet(rest)) { Set<T> newSet = new HashSet<T>(); newSet.add(head); newSet.addAll(set); sets.add(newSet); sets.add(set); } return sets; } }