package funkin.animate; import haxe.format.JsonParser; import openfl.Assets; import openfl.geom.Matrix3D; import openfl.geom.Matrix; import sys.io.File; /** * Generally designed / written in a way that can be easily taken out of FNF and used elsewhere * I don't think it even has ties to OpenFL? Could probably just use it for ANY haxe * project if needed, DOES NEED A LOT OF CLEANUP THOUGH! */ class ParseAnimate { // make list of frames needed to render (with ASI) // make GIANT list of all the frames ever and have them in order? public static var symbolMap:Map = new Map(); public static var actualSprites:Map = new Map(); private var _atlas:Map; private var _symbolData:Map; private var _defaultSymbolName:String; public function new(data:AnimJson, atlas:Spritemap) { // bitmap data could prob be instead // this code is mostly nabbed from https://github.com/miltoncandelero/OpenFLAnimateAtlas/blob/master/Source/animateatlas/displayobject/SpriteAnimationLibrary.hx parseAnimationData(data); parseAtlasData(atlas); } private function parseAnimationData(data:AnimJson):Void { _symbolData = new Map(); var symbols = data.SD.S; for (symbol in symbols) _symbolData[symbol.SN] = preprocessSymbolData(symbol); var defaultSymbol:Symbol = preprocessSymbolData(data.AN); _defaultSymbolName = defaultSymbol.SN; _symbolData.set(_defaultSymbolName, defaultSymbol); } // at little redundant, does exactly the same thing as genSpritemap() private function parseAtlasData(atlas:Spritemap):Void { _atlas = new Map(); if (atlas.ATLAS != null && atlas.ATLAS.SPRITES != null) { for (s in atlas.ATLAS.SPRITES) _atlas.set(s.SPRITE.name, s.SPRITE); } } /** * Not used, was used for testing stuff though! */ public static function init() { // Main.gids var folder:String = 'tightestBars'; // var spritemap:Spritemap = // var spritemap:Spritemap = genSpritemap('test/$folder/spritemap1.json'); actualSprites = genSpritemap('test/$folder/spritemap1.json'); var animation:AnimJson = cast CoolUtil.coolJSON(Assets.getText('src/$folder/Animation.json')); generateSymbolmap(animation.SD.S); trace("\n\nANIMATION SHIT\n"); var timelineLength:Int = 0; for (lyr in animation.AN.TL.L) timelineLength = Std.int(Math.max(lyr.FR.length, timelineLength)); var content:String = animation.AN.TL.L[0].LN; content += "TOTAL FRAMES NEEDED: " + timelineLength + "\n"; for (frm in 0...timelineLength) { trace('FRAME NUMBER ' + frm); try { parseTimeline(animation.AN.TL, 1, frm); content += 'Good write on frame: ' + frm + "\n"; } catch (e) { content += "BAD WRITE : " + frm + "\n"; content += "\t" + e + "\n"; trace(e); } // File.saveContent("output.txt", content); } parseTimeline(animation.AN.TL, 1, 0); trace(actualSprites); } /** * a MAP of SPRITES, not to be confused with Spritemap... lol */ public static function genSpritemap(json:String):Map { var sprShitty:Spritemap = cast CoolUtil.coolJSON(json); var sprMap:Map = new Map(); for (spr in sprShitty.ATLAS.SPRITES) sprMap.set(spr.SPRITE.name, spr.SPRITE); return sprMap; } // should change dis to all private? public static function generateSymbolmap(symbols:Array) { for (symbol in symbols) { // trace(symbol.SN + "has: " + symbol.TL.L.length + " LAYERS"); symbolMap.set(symbol.SN, symbol); // parseTimeline(symbol.TL); } } public static function preprocessSymbolData(anim:Symbol):Symbol { var timelineData:Timeline = anim.TL; var layerData:Array = timelineData.L; if (!timelineData.sortedForRender) { timelineData.sortedForRender = true; layerData.reverse(); } for (layerStuff in layerData) { var frames:Array = layerStuff.FR; for (frame in frames) { var elements:Array = frame.E; for (e in 0...elements.length) { var element:Element = elements[e]; if (element.ASI != null) { element = elements[e] = { SI: { SN: "ATLAS_SYMBOL_SPRITE", LP: "LP", TRP: {x: 0, y: 0}, M3D: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], FF: 0, ST: "G", ASI: element.ASI } } } } } } return anim; } public static var curLoopType:String; /** * Stuff for debug parsing */ public static var depthTypeBeat:String = ""; /** * Array of bullshit that will eventually be RENDERED by whoever wanna use it! */ public static var frameList:Array> = []; // for loop stuf /** * Similar to frameList, keeps track of shit according to framess? * That amount of arrays within arrays is fuckin dumb * but innermost array is basically just x and y value, cuz im dum */ public static var matrixHelp:Array>> = []; public static var trpHelpIDK:Array>> = []; public static var loopedFrameShit:Int = 0; public static var funnyMatrix:Matrix = new Matrix(); public static var matrixFlipper:Array = []; // clean up all the crazy ass arrays public static function resetFrameList() { // funnyMatrix.identity(); frameList = []; frameList.push([]); matrixHelp = []; matrixHelp.push([]); trpHelpIDK = []; trpHelpIDK.push([]); } public static var isFlipped:Bool = false; public static function parseTimeline(TL:Timeline, tabbed:Int = 0, ?frameInput:Int) { var strTab:String = ""; for (i in 0...tabbed) strTab += '\t'; for (layer in TL.L) { var frameArray:Array = []; var frameMap:Map = new Map(); for (frms in layer.FR) { for (i in 0...frms.DU) frameArray.push(frms.I); frameMap.set(frms.I, frms); } if (frameInput == null) frameInput = 0; var oldFrm:Int = frameInput; /* if (curLoopType == "SF") { trace(layer.LN); trace(frameArray); trace(frameInput); trace(curLoopType); }*/ if (curLoopType == "LP") frameInput = frameArray[frameInput % frameArray.length]; else if (curLoopType == "SF") { frameInput = frameArray[loopedFrameShit]; // see what happens when something has more than 2 layer? // single frame stuff isn't fully implemented } else frameInput = frameArray[frameInput]; // trace(frameMap.get(frameInput)); var frame:Frame = frameMap.get(frameInput); // get somethin sorted per element list, which would essentially be per symbol things properly sorted // seperate data types if symbol or atlassymbolinstance? would probably be maybe slightly less memory intensive? i dunno // goes thru each layer, and then each element // after it gets thru each element it adds to the layer frame stuff. // make somethin that works recursively, maybe thats the symbol dictionary type shit? for (element in frame.E) { if (Reflect.hasField(element, "ASI")) { matrixHelp[matrixHelp.length - 1].push(element.ASI.M3D); var m3D = element.ASI.M3D; var lilMatrix:Matrix = new Matrix(m3D[0], m3D[1], m3D[4], m3D[5], m3D[12], m3D[13]); matrixFlipper.push(lilMatrix); // matrixFlipper.reverse(); // funnyMatrix.identity(); // for (m in matrixFlipper) // funnyMatrix.concat(m); if (isFlipped) { trace("MORE FLIPPED SHIT"); trace("MORE FLIPPED SHIT"); trace("MORE FLIPPED SHIT"); trace(funnyMatrix); trace(matrixFlipper); } // trace(funnyMatrix); funnyMatrix.concat(lilMatrix); // trace(funnyMatrix); frameList[frameList.length - 1].push({ frameName: element.ASI.N, depthString: depthTypeBeat, matrixArray: matrixHelp[matrixHelp.length - 1], trpArray: trpHelpIDK[trpHelpIDK.length - 1], fullMatrix: funnyMatrix.clone() }); // flips the matrix once?? I cant remember exactly why it needs to be flipped // matrixHelp[matrixHelp.length - 1].reverse(); // trpHelpIDK = []; // push the matrix array after each symbol? funnyMatrix.identity(); matrixFlipper = []; depthTypeBeat = ""; curLoopType = ""; loopedFrameShit = 0; isFlipped = false; } else { var m3D = element.SI.M3D; var lilMatrix:Matrix = new Matrix(m3D[0], m3D[1], m3D[4], m3D[5], m3D[12], m3D[13]); if (lilMatrix.a == -1) { isFlipped = true; trace('IS THE NEGATIVE ONE'); } if (isFlipped) trace(lilMatrix); funnyMatrix.concat(lilMatrix); matrixFlipper.push(lilMatrix); // trace(funnyMatrix); matrixHelp[matrixHelp.length - 1].push(element.SI.M3D); trpHelpIDK[trpHelpIDK.length - 1].push([element.SI.TRP.x, element.SI.TRP.y]); // trpHelpIDK.push(); depthTypeBeat += "->" + element.SI.SN; curLoopType = element.SI.LP; var inputFrame:Int = element.SI.FF; // JANKY FIX, MAY NOT ACCOUNT FOR ALL SCENARIOS OF SINGLE FRAME ANIMATIONS!! if (curLoopType == "SF") { // trace("LOOP SHIT: " + inputFrame); loopedFrameShit = inputFrame; } // condense the animation code, so it automatically already fills up animation shit per symbol parseTimeline(symbolMap.get(element.SI.SN).TL, tabbed + 1, inputFrame); } // idk if this should go per layer or per element / object? matrixHelp.push([]); trpHelpIDK.push([]); } if (tabbed == 0) { frameList[frameList.length - 1].reverse(); frameList.push([]); // new layer essentially } } frameList.reverse(); } } typedef VALIDFRAME = { frameName:String, depthString:String, matrixArray:Array>, trpArray:Array>, fullMatrix:Matrix } typedef AnimJson = { AN:Animation, SD:SymbolDictionary, MD:MetaData } typedef Animation = { N:String, SN:String, TL:Timeline } typedef SymbolDictionary = { S:Array } typedef Symbol = { /**Symbol name*/ SN:String, TL:Timeline } typedef Timeline = { ?sortedForRender:Bool, L:Array } typedef Layer = { LN:String, FR:Array } typedef Frame = { E:Array, I:Int, DU:Int // maybe need to implement names if it has frame labels? } typedef Element = { SI:SymbolInstance, ?ASI:AlsoSymbolInstance // lmfao idk what ASI stands for lmfaoo, i dont think its "also" } typedef SymbolInstance = { SN:String, ASI:AlsoSymbolInstance, /**Symbol type, prob either G (graphic), or movie clip?*/ ST:String, /**First frame*/ FF:Int, /**Loop type, loop ping pong, etc.*/ LP:String, /**3D matrix*/ M3D:Array, TRP: { x:Float, y:Float } } typedef AlsoSymbolInstance = { N:String, M3D:Array } typedef MetaData = { /** * Framerate */ FRT:Int } // SPRITEMAP BULLSHIT typedef Spritemap = { ATLAS: { SPRITES:Array }, meta:Meta } typedef SpriteBullshit = { SPRITE:Sprite } typedef Sprite = { name:String, x:Int, y:Int, w:Int, h:Int, rotated:Bool } typedef Meta = { app:String, verstion:String, image:String, format:String, size: { w:Int, h:Float }, resolution:Float }