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;
}
}
}