package CharacterExport { import flash.utils.*; import GraphicExport.*; import flash.display.BitmapData; import flash.geom.*; import flash.display.Sprite; import djarts.core.*; import com.adobe.serialization.json.JSON; public class ExportBuilder { protected var characters:Array; protected var output:ByteArray = new ByteArray(); public function get Output():ByteArray { return output; } protected var sheets:Array = []; protected var sheetWidth, sheetHeight; protected var pieces:Array = []; protected var pieceNodes:Array = []; public var jsonPngSet:JSONPNGSet = new JSONPNGSet(); public function GetJSONPNGSet(){ return jsonPngSet; } public var PNGSheets:Array = []; public function ExportBuilder(characters:Array, sheetWidth = -1, sheetHeight = -1, library:PieceLibrary = null) { output.endian = Endian.LITTLE_ENDIAN; this.characters = characters; if (library) { pieces = pieces.concat(library.GetAllUsedPieces()); } else { for (var i = 0; i < characters.length; i++) { var char:CharacterExporter = characters[i]; var p:Array = char.GetAllPieces(); pieces = pieces.concat(p); } } if (sheetWidth == -1) { sheetWidth = sheetHeight = DetermineMinimumSheetSize(pieces); } this.sheetWidth = sheetWidth; this.sheetHeight = sheetHeight; sheets.push(new SpriteSheet(sheetWidth, sheetHeight)); var piecePad = 2; for (var i = 0; i < pieces.length; i++) { var piece:Piece = pieces[i]; var bd:BitmapData = piece.FullData; var usedNewSheet = false; for (var sheetIndex = 0; sheetIndex < sheets.length; sheetIndex++) { var sheet:SpriteSheet = sheets[sheetIndex]; if (!sheet.sheetFull) { var node:RectangleNode = sheet.GetAllocator().Insert(bd.width + piecePad * 2, bd.height + piecePad * 2); if (node) { pieceNodes.push(node); sheet.GetBitmap().copyPixels(bd, bd.rect, new Point(node.Rect.x + piecePad, node.Rect.y + piecePad)); break; } } if (sheetIndex == sheets.length - 1) { if (usedNewSheet) { throw new Error("This piece is probably too big!"); } else { usedNewSheet = true; var newSheet:SpriteSheet = new SpriteSheet(sheetWidth, sheetHeight); newSheet.SheetIndex = sheets.length; sheets.push(newSheet); } } } } output.writeUnsignedInt(sheetWidth); output.writeUnsignedInt(sheetHeight); output.writeUnsignedInt(sheets.length); for (var i = 0; i < sheets.length; i++) { var png:ByteArray = PNGExportHelper.GetExportPNG(sheets[i].GetBitmap()); output.writeUnsignedInt(png.length); output.writeBytes(png); PNGSheets.push(png); } output.writeUnsignedInt(characters.length); var jsonOutputObj = {}; jsonOutputObj.characters = []; var outputName = ""; for (var i = 0; i < characters.length; i++) { var char:CharacterExporter = characters[i]; var name = ""; if (char.BaseGraphicId != -1) { name = GameData.Instance().GetGraphicDefByID(char.BaseGraphicId).Name; } else { name = char.GraphicName; } output.writeUTF(name); WriteCharacterExport(output, char, this.pieces, this.pieceNodes, piecePad); var jsonCharacterData = WriteCharacterExportJSON(char, this.pieces, this.pieceNodes, piecePad); jsonCharacterData.name = name; outputName = name; jsonOutputObj.characters.push(jsonCharacterData); } output.compress(); jsonOutputObj.format = "skeletal"; jsonPngSet.SetData(outputName, jsonOutputObj, PNGSheets); } public static function WriteCharacterExport(output:ByteArray, char:CharacterExporter, pieces:Array, pieceNodes:Array, piecePad) { var sequences:Array = char.Sequences; output.writeUnsignedInt(sequences.length); for (var j = 0; j < sequences.length; j++) { var seq:CharacterSequence = sequences[j]; output.writeUnsignedInt(seq.Length); var ps:Array = seq.PieceSequences; output.writeUnsignedInt(ps.length); for (var k = 0; k < ps.length; k++) { var pieceSequence:PieceSequence = ps[k]; var piece:Piece = pieceSequence.GetPiece(); var pieceIndex = pieces.indexOf(piece); var node:RectangleNode = pieceNodes[pieceIndex]; output.writeUnsignedInt(node.Host.HostTexture.SheetIndex); output.writeUnsignedInt(node.Rect.left + piecePad); output.writeUnsignedInt(node.Rect.top + piecePad); output.writeUnsignedInt(node.Rect.width - piecePad * 2); output.writeUnsignedInt(node.Rect.height - piecePad * 2); output.writeInt(piece.CenterPoint.x); output.writeInt(piece.CenterPoint.y); for (var l = 0; l < seq.Length; l++) { var info:PieceFrameInfo = pieceSequence.GetFrame(l + 1); if (info && info.Present) { output.writeBoolean(true); output.writeInt(info.Depth); var decompose = CharacterExporter.Decompose(info.Transform); output.writeDouble(decompose.rot); output.writeDouble(decompose.sx); output.writeDouble(decompose.sy); output.writeDouble(info.Transform.tx); output.writeDouble(info.Transform.ty); } else { output.writeBoolean(false); } } } } } public static function WriteCharacterExportJSON(char:CharacterExporter, pieces:Array, pieceNodes:Array, piecePad) { var jsonData = {}; var sequences:Array = char.Sequences; jsonData.sequences = []; for (var j = 0; j < sequences.length; j++) { var seq:CharacterSequence = sequences[j]; //output.writeUnsignedInt(seq.Length); var ps:Array = seq.PieceSequences; var outputPieceSequences = []; for (var k = 0; k < ps.length; k++) { var outputPiece = {}; var pieceSequence:PieceSequence = ps[k]; var piece:Piece = pieceSequence.GetPiece(); var pieceIndex = pieces.indexOf(piece); var node:RectangleNode = pieceNodes[pieceIndex]; outputPiece.texture_index = node.Host.HostTexture.SheetIndex; outputPiece.x = node.Rect.left + piecePad; outputPiece.y = node.Rect.top + piecePad; outputPiece.w = node.Rect.width - piecePad * 2; outputPiece.h = node.Rect.height - piecePad * 2; outputPiece.cx = piece.CenterPoint.x; outputPiece.cy = piece.CenterPoint.y; outputPiece.n = piece.Name; var outputSeq = []; for (var l = 0; l < seq.Length; l++) { var outputFrameInfo = {}; var info:PieceFrameInfo = pieceSequence.GetFrame(l + 1); if (info && info.Present) { var decompose = CharacterExporter.Decompose(info.Transform); outputFrameInfo.used = true; outputFrameInfo.z = info.Depth; outputFrameInfo.rot = RoundedOutput(decompose.rot); outputFrameInfo.sx = RoundedOutput(decompose.sx); outputFrameInfo.sy = RoundedOutput(decompose.sy); outputFrameInfo.tx = RoundedOutput(info.Transform.tx); outputFrameInfo.ty = RoundedOutput(info.Transform.ty); } else { outputFrameInfo.used = false; } outputSeq.push(outputFrameInfo); } outputPiece.sequence = outputSeq; outputPieceSequences.push(outputPiece); } jsonData.sequences.push(outputPieceSequences); } return jsonData; } public static function RoundedOutput(value) { var str = ""+value; var split = str.split("."); if (split.length == 2) { var decPart = split[1]; if (decPart.length > 3){ decPart = decPart.substr(0,3); } str = split[0] +"."+decPart; } return str; } public static function DetermineMinimumSheetSize(pieces:Array) { var sizeFound = false; var power = 6; var sheetSize = Math.pow(2, power); while(!sizeFound) { var piecePad = 2; var sheet:SpriteSheet = new SpriteSheet(sheetSize, sheetSize); sizeFound = true; for (var i = 0; i < pieces.length; i++) { var piece = pieces[i]; var bd:BitmapData; if (piece is AnimationPiece) { bd = piece.Data; } if (piece is Piece) { bd = piece.FullData; } if (!sheet.sheetFull) { var node:RectangleNode = sheet.GetAllocator().Insert(bd.width + piecePad * 2, bd.height + piecePad * 2); if (node == null) { sizeFound = false; break; } } } //double the size and try again! if (sizeFound == false) { power++; sheetSize = Math.pow(2, power); //Max size 1024; if (sheetSize == 1024) break; } } return sheetSize; } } }