package CharacterExport { import flash.display.*; import djarts.core.*; import flash.geom.Rectangle; import flash.geom.Matrix; import flash.text.TextField; import djarts.display.CachedMovieClip; import flash.geom.Point; import flash.geom.ColorTransform; public class CharacterSequence { protected var sequence; public function get Sequence() { return sequence; } protected var length = -1; public function get Length() { return length; } protected var exporter:CharacterExporter; public function get Exporter():CharacterExporter { return exporter; } protected var graphic; protected var pieces:Array = []; protected var pieceSequences:Array = []; public function get PieceSequences():Array { return pieceSequences; } protected var formatType; protected var animationName:String; public function get AnimationName():String { return animationName; } public var AvatarName:String = ""; public function CharacterSequence(exporter:CharacterExporter, sequence, fullSequence, formatType) { this.exporter = exporter; this.sequence = sequence; this.formatType = formatType; graphic = exporter.GetNewSequence(sequence); if (formatType == CharacterExporter.NORMAL) { length = graphic.totalFrames; } if (formatType == CharacterExporter.OLDMONSTER) { length = 1; for (var i = 0; i < graphic.numChildren; i++) { var c = graphic.getChildAt(i); if (c is MovieClip && c.totalFrames > 1) { length = c.totalFrames; break; } } } length = fullSequence ? length : Math.min(length, 1); CharacterExporter.SafeGotoFrame2(graphic, 1); animationName = exporter.GetSequenceName(sequence); } public function ProducePieces() { for (var i = 0; i < length; i++) { ProducePiecesFromFrame(i); } /* // test by hiding all alternate usages of Pieces, to make sure we are tracking frame->frame correctly var usedPiece = {}; for (var i = 0; i < pieceSequences.length; i++) { var pieceIndex = this.pieces.indexOf(pieceSequences[i].GetPiece()); if (pieceIndex in usedPiece) { pieceSequences.splice(i, 1); i--; } else { usedPiece[pieceIndex] = true; } } */ //CombinePieces(); } protected function ProducePiecesFromFrame(frame) { if (formatType == CharacterExporter.OLDMONSTER) { for (var i = 0; i < graphic.numChildren; i++) { var child = graphic.getChildAt(i); if (child is MovieClip) { CharacterExporter.SafeGotoFrame2(child, frame + 1); } } } else { CharacterExporter.SafeGotoFrame2(graphic, frame + 1); } RecursePieces(graphic, frame + 1, 0, new Matrix()); } protected function RecursePieces(graphic:MovieClip, frame, depth, parentMatrix:Matrix) { _strace(depth+" "+graphic.name+" frame:"+frame); var mat:Matrix = graphic.transform.matrix.clone(); mat.concat(parentMatrix); var currentPieces:Array = []; var allChildren:Array = CharacterExporter.GetChildren(graphic); for (var i = 0; i < allChildren.length; i++) { if (!allChildren[i].visible) continue; if (allChildren[i] is MovieClip) { if (CharacterExporter.IsFullPiece(this.graphic, graphic, allChildren[i])) { currentPieces.push(allChildren[i]); } else { depth = GeneratePieceForClips(currentPieces, depth, frame, mat.clone()); currentPieces.splice(0); depth = RecursePieces(allChildren[i], frame, depth, mat.clone()); } } else { currentPieces.push(allChildren[i]); } } depth = GeneratePieceForClips(currentPieces, depth, frame, mat.clone()); return depth; } protected function GeneratePieceForClips(clips:Array, depth, frame, parentMatrix:Matrix) { if (clips.length > 0) { var l = clips.length; var bounds:Rectangle = null; var invertMatrix:Matrix = clips[0].transform.matrix.clone(); invertMatrix.invert(); var useMatrix:Array = []; for (var i = 0; i < clips.length; i++) { var m:Matrix; if (i == 0) { m = new Matrix(); } else { m = clips[i].transform.matrix.clone(); m.concat(invertMatrix); } var r:Rectangle = CacheManager.Instance().GetAccurateBounds(clips[i]); CharacterExporter.TransformRect(r, m); if (bounds == null) { bounds = r.clone(); } else { if (r.left < bounds.left) bounds.left = r.left; if (r.top < bounds.top) bounds.top = r.top; if (r.right > bounds.right) bounds.right = r.right; if (r.bottom > bounds.bottom) bounds.bottom = r.bottom; } useMatrix.push(m); } CharacterExporter.ScaleRect(bounds, exporter.Upscale); CharacterExporter.RoundRect(bounds); var bd:BitmapData = new BitmapData(Math.max(1, bounds.width), Math.max(1,bounds.height), true, 0x0); for (var i = 0; i < clips.length; i++) { var m:Matrix = useMatrix[i]; m.scale(exporter.Upscale, exporter.Upscale); m.translate(-bounds.left, -bounds.top); var ct:ColorTransform = clips[i] is Shape ? clips[i].parent.transform.colorTransform : clips[i].transform.colorTransform; bd.draw(clips[i], m, ct, null, null, true); } var center:Point = new Point(-bounds.left, -bounds.top); var piece:Piece = exporter.Library.GetPieceFromBitmapData(bd, clips[0].parent.name, center); if (piece) { var mat:Matrix = new Matrix(1/exporter.Upscale, 0, 0, 1/exporter.Upscale, center.x - piece.CenterPoint.x, center.y - piece.CenterPoint.y); mat.concat(clips[0].transform.matrix); mat.concat(parentMatrix); //mat.a /= exporter.Upscale; //mat.d /= exporter.Upscale; var pieceSequence:PieceSequence = GetPieceSequence(piece, frame, clips[0], mat, depth); var info:PieceFrameInfo = pieceSequence.GetFrame(frame); if (info) { info.Present = true; info.Transform = mat; info.Depth = depth; depth++; } } } return depth; } protected function GetPieceSequence(piece:Piece, frame, clip:DisplayObject, newTransform:Matrix, newDepth) { if (this.pieces.indexOf(piece) == -1) this.pieces.push(piece); var options:Array = []; for (var i in pieceSequences) { if (pieceSequences[i].GetPiece() == piece) { var frameInfo:PieceFrameInfo = pieceSequences[i].GetFrame(frame); if (frameInfo && !frameInfo.Present) { if (pieceSequences[i].CheckMatches(clip)) { return pieceSequences[i]; } else { options.push(pieceSequences[i]); } } } } if (options.length == 0) { var s:PieceSequence = new PieceSequence(piece, this.length); piece.AddUse(sequence); pieceSequences.push(s); return s; } var scores:Array = []; for (var i = 0; i < options.length; i++) { var score = 0; if (frame > 1) { var prevInfo:PieceFrameInfo = options[i].GetFrame(frame - 1); if (prevInfo && prevInfo.Present) { score = ComputeTransformScore(prevInfo.Transform, prevInfo.Depth, newTransform, newDepth); } } scores.push(score); } var sorted:Array = scores.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY); return options[sorted[0]]; } protected function ComputeTransformScore(transformA:Matrix, depthA, transformB:Matrix, depthB, posVal = 1, scaleVal = 15, rotVal = 20, depthVal = 10) { var deltaPos = Point.distance(new Point(transformA.tx, transformA.ty), new Point(transformB.tx, transformB.ty)); deltaPos /= exporter.Upscale; var partsA = CharacterExporter.Decompose(transformA); var partsB = CharacterExporter.Decompose(transformB); var rotA = partsA.rot; var rotB = partsB.rot; var deltaRot = CharacterExporter.GetAngleDiffAbs(rotA, rotB); var deltaScaleX = partsA.sx / partsB.sx; if (deltaScaleX < 1) deltaScaleX = 1 / deltaScaleX; var deltaScaleY = partsA.sy / partsB.sy; if (deltaScaleY < 1) deltaScaleY = 1 / deltaScaleY; var deltaScale = Math.max(deltaScaleX, deltaScaleY); var deltaDepth = Math.abs(depthA - depthB); return deltaPos * posVal + (deltaScale - 1) * scaleVal + deltaRot * rotVal + deltaDepth * depthVal; } public function DebugPieces(rowY) { var count:Array = []; for (var i in pieces) { count.push(0); } for (var i in pieceSequences) { var p:Piece = pieceSequences[i].GetPiece(); var index = pieces.indexOf(p); count[index]++; } var nextX = 0; for (var i = 0; i < pieces.length; i++) { var b:Bitmap = new Bitmap(pieces[i].FullData); b.scaleX = b.scaleY = 1; b.x = nextX; b.y = rowY; Globals.Instance().GetStage().addChild(b); nextX += b.width; var t:TextField = new TextField(); t.textColor = 0xFF0000; t.text = "x" + count[i]; Globals.Instance().GetStage().addChild(t); t.x = b.x; t.y = b.y + b.height; } } public static var ONLYCOMBINEADJACENT = true; protected function CombinePieces() { var sequenceByPresent = {}; for (var i in pieceSequences) { var str = ""; for (var j = 0; j < length; j++) { var info:PieceFrameInfo = pieceSequences[i].GetFrame(j + 1); str += (info && info.Present) ? "1" : "0"; } if (!(str in sequenceByPresent)) { sequenceByPresent[str] = []; } sequenceByPresent[str].push(pieceSequences[i]); } for (var presentStr in sequenceByPresent) { var all:Array = sequenceByPresent[presentStr]; var firstFrame = ("" + presentStr).indexOf("1") + 1; var depths:Array = []; for (var i = 0; i < all.length; i++) { depths.push(all[i].GetFrame(firstFrame).Depth); } var sorted:Array = depths.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY); var groups:Array = []; var lastGroup:Array = null; for (var i = 0; i < all.length; i++) { var index = sorted[i]; if (ONLYCOMBINEADJACENT) { if (lastGroup != null && SequenceMatches(lastGroup[0], all[index])) { lastGroup.push(all[index]); } else { lastGroup = []; groups.push(lastGroup); lastGroup.push(all[index]); } } else { var foundGroup = false; for (var j in groups) { if (SequenceMatches(groups[j][0], all[index])) { groups[j].push(all[index]); foundGroup = true; break; } } if (!foundGroup) { var g:Array = []; g.push(all[index]); groups.push(g); } } } } for (var i in groups) { // recovering the original pieces... // for now: render the bitmaps? (one will be transformed... but it would be transformed anyways, right?) var group:Array = groups[i]; if (group.length > 1) { var origSeq:PieceSequence = group[0]; var invert:Matrix; var firstPresent = null; for (var j = 0; j < length; j++) { var info:PieceFrameInfo = origSeq.GetFrame(j + 1); if (info && info.Present) { firstPresent = j + 1; invert = info.Transform.clone(); invert.invert(); break; } } var bounds:Rectangle = null; var transforms:Array = []; for (var j = 0; j < group.length; j++) { var p:Piece = group[j].GetPiece(); p.RemoveUse(sequence); var m:Matrix = new Matrix(); if (j > 0) { //m.translate(-group[j].CenterPoint.x, -group[j].CenterPoint.y); m.concat(invert); m.concat(group[j].GetFrame(firstPresent).Transform); //m.translate(origSeq.CenterPoint.x, origSeq.CenterPoint.y); } var r:Rectangle = p.FullData.rect.clone(); CharacterExporter.TransformRect(r, m); if (bounds == null) { bounds = r; } else { if (r.left < bounds.left) bounds.left = r.left; if (r.right > bounds.right) bounds.right = r.right; if (r.top < bounds.top) bounds.top = r.top; if (r.bottom > bounds.bottom) bounds.bottom = r.bottom; } transforms.push(m); } CharacterExporter.RoundRect(bounds); var bd:BitmapData = new BitmapData(bounds.width, bounds.height, true, 0x0); for (var j = 0; j < group.length; j++) { transforms[j].translate(-bounds.left, -bounds.top); bd.draw(group[j].GetPiece().FullData, transforms[j], null, null, null, true); } var p:Piece = this.exporter.Library.GetPieceFromBitmapData(bd, "CombinedPiece"); var seq:PieceSequence = new PieceSequence(p, this.length); p.AddUse(sequence); seq.CopyTransformsFrom(origSeq, group[group.length - 1].GetFrame(firstPresent).Depth); this.pieceSequences.push(seq); this.pieces.push(p); } } for (var i in groups) { if (groups[i].length > 1) { for (var j in groups[i]) { var index = this.pieceSequences.indexOf(groups[i][j]); if (index == -1) { throw new Error("huh?"); } else { pieceSequences.splice(index, 1); } } } } for (var i = 0; i < pieces.length; i++) { if (pieces[i].GetUses(sequence) == 0) { pieces.splice(i, 1); i--; } } } protected function SequenceMatches(a:PieceSequence, b:PieceSequence) { var relative:Matrix = null; for (var i = 0; i < length; i++) { var infoA:PieceFrameInfo = a.GetFrame(i + 1); var infoB:PieceFrameInfo = b.GetFrame(i + 1); if (infoA && infoB && infoA.Present && infoB.Present) { var m:Matrix = CharacterExporter.GetTransform(infoA.Transform, infoB.Transform); if (relative == null) { relative = m; } else { if (ComputeTransformScore(relative, 0, m, 0, 4, 15, 5, 0) > 4) return false; //if (!CharacterExporter.MatricesMatch(relative, m)) return false; } } } return true; } public function ProduceFrame(frame, excludeNames:Array = null):MovieClip { var depths:Array = []; for (var i = 0; i < pieceSequences.length; i++) { depths.push(pieceSequences[i].GetFrame(frame).Depth); } var sorts:Array = depths.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY); var m:MovieClip = new MovieClip(); for (var i = 0; i < sorts.length; i++) { var ps:PieceSequence = pieceSequences[sorts[i]]; var info:PieceFrameInfo = ps.GetFrame(frame); if (info.Present && (excludeNames == null || excludeNames.indexOf(ps.GetPiece().Name) == -1)) { var p:Piece = ps.GetPiece(); var b:Bitmap = new Bitmap(p.FullData); var mat:Matrix = new Matrix(); mat.translate(-p.CenterPoint.x, -p.CenterPoint.y); mat.concat(info.Transform); //mat.scale(1 / exporter.Upscale, 1 / exporter.Upscale); //b.smoothing = true; b.transform.matrix = mat; m.addChild(b); } } return m; } public function ProduceFrameBitmapData(frame, excludeNames:Array = null) { var r:Rectangle = null; for (var i in pieceSequences) { var ps:PieceSequence = pieceSequences[i]; var frameInfo:PieceFrameInfo = ps.GetFrame(frame); if (frameInfo && frameInfo.Present && (excludeNames == null || excludeNames.indexOf(ps.GetPiece().Name) == -1)) { var piece:Piece = ps.GetPiece(); var bounds:Rectangle = piece.FullData.rect; bounds.x -= piece.CenterPoint.x; bounds.y -= piece.CenterPoint.y; var mat:Matrix = frameInfo.Transform.clone(); //mat.scale(1 / exporter.Upscale, 1 / exporter.Upscale); CharacterExporter.TransformRect(bounds, mat); if (r == null) { r = bounds; } else { if (bounds.left < r.left) r.left = bounds.left; if (bounds.top < r.top) r.top = bounds.top; if (bounds.right > r.right) r.right = bounds.right; if (bounds.bottom > r.bottom) r.bottom = bounds.bottom; } } } if (r == null) { return {data:new BitmapData(1, 1, true, 0x0),center_point:new Point(0, 0)}; } CharacterExporter.RoundRect(r); var bd:BitmapData; try { bd = new BitmapData(Math.min(512, r.width), Math.min(512, r.height), true, 0x0); } catch (e){ _strace("failed to create bitmap data"); } var depths:Array = []; for (var i = 0; i < pieceSequences.length; i++) { depths.push(pieceSequences[i].GetFrame(frame).Depth); } var sorts:Array = depths.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY); for (var i = 0; i < sorts.length; i++) { var ps:PieceSequence = pieceSequences[sorts[i]]; var info:PieceFrameInfo = ps.GetFrame(frame); if (info && info.Present && (excludeNames == null || excludeNames.indexOf(ps.GetPiece().Name) == -1)) { var p:Piece = ps.GetPiece(); var mat:Matrix = new Matrix(); mat.translate(-ps.GetPiece().CenterPoint.x, -ps.GetPiece().CenterPoint.y); mat.concat(info.Transform); //mat.scale(1 / exporter.Upscale, 1 / exporter.Upscale); mat.translate(-r.left, -r.top); bd.draw(p.FullData, mat, null, null, null, true); } } return {data:bd,center_point:new Point(-r.left, -r.top)}; } public function GetDebugClip(excludeNames:Array = null):CachedMovieClip { var c:CachedMovieClip = new CachedMovieClip(); c.frames = []; for (var i = 0; i < this.length; i++) { var frame = this.ProduceFrameBitmapData(i + 1, excludeNames); c.frames.push(frame.data); c.framePositions.push(new Point(-frame.center_point.x, -frame.center_point.y)); } c.SetTotalFrames(this.length); return c; } } }