package com.nilunder.bdx.components;
import java.util.*;
import javax.vecmath.*;
import com.badlogic.gdx.graphics.*;
import com.badlogic.gdx.graphics.g3d.*;
import com.badlogic.gdx.graphics.g3d.attributes.*;
import com.badlogic.gdx.math.*;
import com.nilunder.bdx.*;
import com.nilunder.bdx.utils.Timer;
public class SpriteAnim extends Component<GameObject> {
public static class Animation extends ArrayList<Vector2f>{
public String name;
public float fps;
public boolean looping;
public int playHead;
public int playDir;
public Animation(String name, float fps, boolean looping){
this.name = name;
this.fps = fps;
this.looping = looping;
playHead = 0;
playDir = 1;
}
public Vector2f nextFrame(){
if (onLastFrame()){
if (looping)
reset();
else
playHead -= playDir;
}
Vector2f frame = get(playHead);
playHead += playDir;
return frame;
}
public boolean onLastFrame(){
return playHead == size() || playHead == -1;
}
public void reset(){
if (playDir > 0)
playHead = 0;
else
playHead = size() - 1;
}
}
public float speed;
public HashMap<String, Animation> animations;
public Animation active;
private int prevFrame;
private Timer ticker;
private Matrix3 uvScale;
private boolean rowBased;
private Vector2f baseFrame;
private Vector2f frameDim;
public SpriteAnim(GameObject g, int frameWidth, int frameHeight, boolean rowBased, boolean uniqueModel){
super(g);
if (uniqueModel)
g.mesh(g.mesh().copy());
this.rowBased = rowBased;
animations = new HashMap<String, Animation>();
ticker = new Timer();
uvScale = new Matrix3();
uvScale.idt();
speed = 1;
state = play;
// initially displayed frame
HashMap<Model,Vector2f> modelToFrame = g.scene.modelToFrame;
baseFrame = modelToFrame.get(g.modelInstance.model);
if (baseFrame == null){
baseFrame = uvFrame();
modelToFrame.put(g.modelInstance.model, baseFrame);
}
// frameDim
TextureAttribute ta = (TextureAttribute)g.modelInstance.materials.get(0).get(TextureAttribute.Diffuse);
GLTexture t = ta.textureDescription.texture;
float u = 1f / t.getWidth();
float v = 1f / t.getHeight();
frameDim = new Vector2f(u * frameWidth, v * frameHeight);
}
public SpriteAnim(GameObject g, int frameWidth, int frameHeight){
this(g, frameWidth, frameHeight, true, true);
}
public void add(String name, int index, int[] frames){
add(name, index, frames, 12, true);
}
public void add(String name, int sequence, int[] frames, float fps, boolean looping){
Animation anim = new Animation(name, fps, looping);
for (int i : frames){
Vector2f f = new Vector2f(baseFrame);
if (rowBased){
f.x += i * frameDim.x;
f.y += sequence * frameDim.y;
}else{
f.x += sequence * frameDim.x;
f.y += i * frameDim.y;
}
anim.add(f);
}
animations.put(name, anim);
}
public ArrayList<String> animationNames(){
return new ArrayList<String>(animations.keySet());
}
public void uvScaleX(float s){
uvScale(s, uvScaleY());
}
public void uvScaleY(float s){
uvScale(uvScaleX(), s);
}
public float uvScaleX(){
return uvScale.val[Matrix3.M00];
}
public float uvScaleY(){
return uvScale.val[Matrix3.M11];
}
public void play(String name){
Animation next = animations.get(name);
if (active != next){
active = next;
active.playDir = speed * active.fps < 0 ? -1 : 1;
active.reset();
ticker.done(true); // immediate play
}
if (!active.looping && active.onLastFrame()){
active.playDir = speed * active.fps < 0 ? -1 : 1;
active.reset();
ticker.done(true);
}
}
public void showNextFrame(){
active.playDir = speed * active.fps < 0 ? -1 : 1;
uvFrame(active.nextFrame());
}
public void frame(int frame){
active.playHead = frame; // Set the frame, and
ticker.done(true); // Update the sprite immediately
}
public int frame(){
return active.playHead - active.playDir;
}
public boolean frameChanged(){
return prevFrame != frame();
}
private State play = new State(){
private float nz(float n){
return n <= 0 ? 0.000001f : n;
}
public void main(){
if (active == null)
return;
prevFrame = frame();
ticker.interval = 1f / nz(Math.abs(active.fps) * Math.abs(speed));
if (ticker.tick()){
showNextFrame();
}
}
};
private void uvFrame(Vector2f frame){
Matrix3 trans = new Matrix3();
Vector2f df = uvFrame();
trans.setToTranslation(frame.x - df.x, frame.y - df.y);
Mesh mesh = g.modelInstance.model.meshes.first();
mesh.transformUV(trans);
}
private Vector2f uvFrame(){
Mesh mesh = g.modelInstance.model.meshes.first();
int n = mesh.getNumVertices();
float[] verts = new float[n*Bdx.VERT_STRIDE];
mesh.getVertices(0, verts.length, verts);
Vector2f frame = new Vector2f(0, 0);
int uvStart = Bdx.VERT_STRIDE - 2;
for (int v = 0; v < n; ++v){
int i = v * Bdx.VERT_STRIDE;
frame.x += verts[i + uvStart];
frame.y += verts[i + uvStart + 1];
}
frame.x /= n;
frame.y /= n;
return frame;
}
private void scaleUV(Matrix3 scale){
Matrix3 trans = new Matrix3(); trans.idt();
Vector2f df = uvFrame();
trans.setToTranslation(df.x, df.y);
Matrix3 toOrigin = new Matrix3(trans);
toOrigin.inv();
trans.mul(scale);
trans.mul(toOrigin);
Mesh mesh = g.modelInstance.model.meshes.first();
mesh.transformUV(trans);
}
private void uvScale(float x, float y){
if (uvScaleX() == x && uvScaleY() == y)
return;
// back to unit scale
uvScale.inv();
scaleUV(uvScale);
// set new scale
uvScale.idt();
uvScale.scale(x, y);
scaleUV(uvScale);
}
}