package com.nilunder.bdx.components;
import java.util.*;
import javax.vecmath.*;
import com.badlogic.gdx.utils.JsonValue;
import com.bulletphysics.linearmath.MatrixUtil;
import com.nilunder.bdx.*;
public class FAnim extends Component<GameObject>{
private static class KeyFrame{
public Vector2f handle_left;
public Vector2f position;
public Vector2f handle_right;
public KeyFrame(JsonValue frame){
JsonValue p = frame.child;
handle_left = new Vector2f(p.asFloatArray());
position = new Vector2f(p.next.asFloatArray());
handle_right = new Vector2f(p.next.next.asFloatArray());
}
}
private static class Channel extends ArrayList<KeyFrame>{
public Channel(JsonValue channel){
for (JsonValue frame : channel){
this.add(new KeyFrame(frame));
}
}
}
private static class Action extends HashMap<Integer, Channel>{
public Action(JsonValue action){
for (JsonValue channel : action){
put(Integer.parseInt(channel.name), new Channel(channel));
}
}
}
private static HashMap<String, Action> actions;
public static void loadActions(JsonValue actions){
FAnim.actions = new HashMap<String, Action>();
for (JsonValue act : actions){
FAnim.actions.put(act.name, new Action(act));
}
}
public int loop;
public boolean bounce;
private Action action;
private float frame;
private float firstFrame;
private float lastFrame;
private float increment;
private float[] offsetLocRotScale;
private boolean additive;
public FAnim(GameObject g){
super(g);
fps(24);
additive(true);
}
public void fps(float fps){
increment = fps / 60.f;
}
public void action(String name){
bounce = false;
action = actions.get(name);
firstFrame = firstFrame();
lastFrame = lastFrame();
frame = firstFrame;
loop = 0;
}
private State play = new State(){
public int wall;
public void main(){
showFrame(frame);
if (wall != 0){
if (bounce)
direction(-wall);
if (loop == 0){
stop();
}else{
--loop;
if (!bounce)
frame = wall == 1 ? firstFrame : lastFrame;
}
wall = 0;
}
wall = advance();
}
};
private void direction(int dir){
increment = Math.abs(increment) * dir;
}
private int advance(){
frame += increment;
int wall = 0;
if (frame > lastFrame){
frame = lastFrame;
wall = 1;
}else if (frame < firstFrame){
frame = firstFrame;
wall = -1;
}
return wall;
}
public FAnim play(){
state = play;
offsetLocRotScale = offsetLocRotScale();
return this;
}
public FAnim loop(){
return loop(Integer.MAX_VALUE);
}
public FAnim loop(int n){
loop = n;
return this;
}
public FAnim bounce(){
bounce = true;
return this;
}
public void stop(){
frame = firstFrame;
increment = Math.abs(increment);
state = null;
}
public void pause(){
state = null;
}
public void frame(float frame){
this.frame = frame;
showFrame(frame);
}
public float frame(){
return frame;
}
public boolean isPlaying() {
return state != null;
}
public void additive(boolean add){
additive = add;
}
public boolean additive() {
return additive;
}
private float firstFrame(){
Channel ch = action.entrySet().iterator().next().getValue();
return ch.get(0).position.x;
}
private float lastFrame(){
Channel ch = action.entrySet().iterator().next().getValue();
return ch.get(ch.size() - 1).position.x;
}
private void showFrame(float frame){
float[] lrs = locRotScale();
for (Map.Entry<Integer,Channel> e : action.entrySet()){
int i = e.getKey();
if (additive)
lrs[i] = channelValue(e.getValue()) + offsetLocRotScale[i];
else
lrs[i] = channelValue(e.getValue());
}
// Location
g.position(lrs[0], lrs[1], lrs[2]);
// Rotation
Matrix3f ori = new Matrix3f();
MatrixUtil.setEulerZYX(ori, lrs[3], lrs[4], lrs[5]);
g.orientation(ori);
// Scale
g.scale(lrs[6], lrs[7], lrs[8]);
}
private float channelValue(Channel channel){
Iterator<KeyFrame> ahead = channel.iterator();
KeyFrame fkf = ahead.next();
if (frame < fkf.position.x)
return fkf.position.y;
for (KeyFrame kf : channel){
KeyFrame kf_;
if (ahead.hasNext())
kf_ = ahead.next();
else
return kf.position.y;
if (frame >= kf.position.x && frame < kf_.position.x){
Vector2f range = kf_.position.minus(kf.position);
float t = (frame - kf.position.x) / range.x;
return bezier(kf.position, kf.handle_right, t, kf_.handle_left, kf_.position);
}
}
return firstFrame;
}
private float bezier(Vector2f p0, Vector2f p1, float t, Vector2f p2, Vector2f p3){
float u = 1.f - t;
float tt = t*t;
float uu = u*u;
float uuu = uu * u;
float ttt = tt * t;
Vector2f p = p0.mul(uuu); //first term
p.add(p1.mul(3.f * uu * t)); //second term
p.add(p2.mul(3.f * u * tt)); //third term
p.add(p3.mul(ttt)); //fourth term
return p.y;
}
private float[] locRotScale(){
Vector3f p = g.position();
Vector3f r = g.orientation().euler();
Vector3f s = g.scale();
float[] lrs = new float[]{
p.x, p.y, p.z,
r.x, r.y, r.z,
s.x, s.y, s.z
};
return lrs;
}
private float[] offsetLocRotScale(){
float[] lrs = locRotScale();
float[] offset = new float[lrs.length];
for (Map.Entry<Integer,Channel> e : action.entrySet()){
int i = e.getKey();
offset[i] = lrs[i] - channelValue(e.getValue());
}
return offset;
}
}