Funkin/source/funkin/animate/ParseAnimate.hx
2022-03-18 12:28:25 -04:00

514 lines
11 KiB
Haxe

package funkin.animate;
import haxe.format.JsonParser;
import openfl.Assets;
import openfl.geom.Matrix3D;
import openfl.geom.Matrix;
/**
* 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<String, Symbol> = new Map();
public static var actualSprites:Map<String, Sprite> = new Map();
private var _atlas:Map<String, Sprite>;
private var _symbolData:Map<String, Symbol>;
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<String, Sprite>();
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<String, Sprite>
{
var sprShitty:Spritemap = cast CoolUtil.coolJSON(json);
var sprMap:Map<String, Sprite> = 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<Symbol>)
{
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<Layer> = timelineData.L;
if (!timelineData.sortedForRender)
{
timelineData.sortedForRender = true;
layerData.reverse();
}
for (layerStuff in layerData)
{
var frames:Array<Frame> = layerStuff.FR;
for (frame in frames)
{
var elements:Array<Element> = 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<Array<VALIDFRAME>> = [];
// 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<Array<Array<Float>>> = [];
public static var trpHelpIDK:Array<Array<Array<Float>>> = [];
public static var loopedFrameShit:Int = 0;
public static var funnyMatrix:Matrix = new Matrix();
public static var matrixFlipper:Array<Matrix> = [];
// 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<Int> = [];
var frameMap:Map<Int, Frame> = 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<Array<Float>>,
trpArray:Array<Array<Float>>,
fullMatrix:Matrix
}
typedef AnimJson =
{
AN:Animation,
SD:SymbolDictionary,
MD:MetaData
}
typedef Animation =
{
N:String,
SN:String,
TL:Timeline
}
typedef SymbolDictionary =
{
S:Array<Symbol>
}
typedef Symbol =
{
/**Symbol name*/
SN:String,
TL:Timeline
}
typedef Timeline =
{
?sortedForRender:Bool,
L:Array<Layer>
}
typedef Layer =
{
LN:String,
FR:Array<Frame>
}
typedef Frame =
{
E:Array<Element>,
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<Float>,
TRP:
{
x:Float, y:Float
}
}
typedef AlsoSymbolInstance =
{
N:String,
M3D:Array<Float>
}
typedef MetaData =
{
/**
* Framerate
*/
FRT:Int
}
// SPRITEMAP BULLSHIT
typedef Spritemap =
{
ATLAS:
{
SPRITES:Array<SpriteBullshit>
},
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
}