All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.brashmonkey.spriter.Animation Maven / Gradle / Ivy

Go to download

overlap2d-runtime-libgdx provides functionality to load, manipulate and render scenes generated by Overlap2D.

There is a newer version: 0.1.1
Show newest version
package com.brashmonkey.spriter;

import java.util.HashMap;

import com.brashmonkey.spriter.Mainline.Key;
import com.brashmonkey.spriter.Mainline.Key.BoneRef;
import com.brashmonkey.spriter.Mainline.Key.ObjectRef;
import com.brashmonkey.spriter.Timeline.Key.Bone;
import com.brashmonkey.spriter.Timeline.Key.Object;
/**
 * Represents an animation of a Spriter SCML file.
 * An animation holds {@link Timeline}s and a {@link Mainline} to animate objects.
 * Furthermore it holds an {@link #id}, a {@link #length}, a {@link #name} and whether it is {@link #looping} or not.
 * @author Trixt0r
 *
 */
public class Animation {

    public final Mainline mainline;
    private final Timeline[] timelines;
    private int timelinePointer = 0;
    private final HashMap nameToTimeline;
    public final int id, length;
    public final String name;
    public final boolean looping;
	Key currentKey;
	Timeline.Key[] tweenedKeys, unmappedTweenedKeys;
	private boolean prepared;
    
    public Animation(Mainline mainline, int id, String name, int length, boolean looping, int timelines){
    	this.mainline = mainline;
    	this.id = id;
    	this.name = name;
    	this.length = length;
    	this.looping = looping;
    	this.timelines = new Timeline[timelines];
    	this.prepared = false;
    	this.nameToTimeline = new HashMap();
    	//this.currentKey = mainline.getKey(0);
    }
    
    /**
     * Returns a {@link Timeline} with the given index.
     * @param index the index of the timeline
     * @return the timeline with the given index
     * @throws IndexOutOfBoundsException if the index is out of range
     */
    public Timeline getTimeline(int index){
    	return this.timelines[index];
    }
    
    /**
     * Returns a {@link Timeline} with the given name.
     * @param name the name of the time line
     * @return the time line with the given name or null if no time line exists with the given name.
     */
    public Timeline getTimeline(String name){
    	return this.nameToTimeline.get(name);
    }
    
    void addTimeline(Timeline timeline){
    	this.timelines[timelinePointer++] = timeline;
    	this.nameToTimeline.put(timeline.name, timeline);
    }
    
    /**
     * Returns the number of time lines this animation holds.
     * @return the number of time lines
     */
    public int timelines(){
    	return timelines.length;
    }
    
    public String toString(){
    	String toReturn = getClass().getSimpleName()+"|[id: "+id+", "+name+", duration: "+length+", is looping: "+looping;
    	toReturn +="Mainline:\n";
    	toReturn += mainline;
    	toReturn += "Timelines\n";
    	for(Timeline timeline: this.timelines)
    		toReturn += timeline;
    	toReturn+="]";
    	return toReturn;
    }
    
    /**
     * Updates the bone and object structure with the given time to the given root bone.
     * @param time The time which has to be between 0 and {@link #length} to work properly.
     * @param root The root bone which is not allowed to be null. The whole animation runs relative to the root bone.
     */
    public void update(int time, Bone root){
    	if(!this.prepared) throw new SpriterException("This animation is not ready yet to animate itself. Please call prepare()!");
    	if(root == null) throw new SpriterException("The root can not be null! Set a root bone to apply this animation relative to the root bone.");
    	this.currentKey = mainline.getKeyBeforeTime(time);
    	
    	for(Timeline.Key timelineKey: this.unmappedTweenedKeys)
			timelineKey.active = false;
		for(BoneRef ref: currentKey.boneRefs)
			this.update(ref, root, time);
		for(ObjectRef ref: currentKey.objectRefs)
			this.update(ref, root, time);
    }
    
    protected void update(BoneRef ref, Bone root, int time){
    	boolean isObject = ref instanceof ObjectRef;
		//Get the timelines, the refs pointing to
		Timeline timeline = getTimeline(ref.timeline);
		Timeline.Key key = timeline.getKey(ref.key);
		Timeline.Key nextKey = timeline.getKey((ref.key+1)%timeline.keys.length);
		int currentTime = key.time;
		int nextTime = nextKey.time;
		if(nextTime < currentTime){
			if(!looping) nextKey = key;
			else nextTime = length;
		}
		//Normalize the time
		float t = (float)(time - currentTime)/(float)(nextTime - currentTime);
		if(Float.isNaN(t) || Float.isInfinite(t)) t = 1f;
		if(currentKey.time > currentTime){
			float tMid = (float)(currentKey.time - currentTime)/(float)(nextTime - currentTime);
			if(Float.isNaN(tMid) || Float.isInfinite(tMid)) tMid = 0f;
			t = (float)(time - currentKey.time)/(float)(nextTime - currentKey.time);
			if(Float.isNaN(t) || Float.isInfinite(t)) t = 1f;
			t = currentKey.curve.tween(tMid, 1f, t);
		}
		else 
			t = currentKey.curve.tween(0f, 1f, t);
		//Tween bone/object
		Bone bone1 = key.object();
		Bone bone2 = nextKey.object();
		Bone tweenTarget = this.tweenedKeys[ref.timeline].object();
		if(isObject) this.tweenObject((Object)bone1, (Object)bone2, (Object)tweenTarget, t, key.curve, key.spin);
		else this.tweenBone(bone1, bone2, tweenTarget, t, key.curve, key.spin);
		this.unmappedTweenedKeys[ref.timeline].active = true;
		this.unmapTimelineObject(ref.timeline, isObject,(ref.parent != null) ?
				this.unmappedTweenedKeys[ref.parent.timeline].object(): root);
    }
    
    void unmapTimelineObject(int timeline, boolean isObject, Bone root){
		Bone tweenTarget = this.tweenedKeys[timeline].object();
		Bone mapTarget = this.unmappedTweenedKeys[timeline].object();
		if(isObject) ((Object)mapTarget).set((Object)tweenTarget);
		else mapTarget.set(tweenTarget);
		mapTarget.unmap(root);
    }
	
	protected void tweenBone(Bone bone1, Bone bone2, Bone target, float t, Curve curve, int spin){
		target.angle = curve.tweenAngle(bone1.angle, bone2.angle, t, spin);
		curve.tweenPoint(bone1.position, bone2.position, t, target.position);
		curve.tweenPoint(bone1.scale, bone2.scale, t, target.scale);
		curve.tweenPoint(bone1.pivot, bone2.pivot, t, target.pivot);
	}
	
	protected void tweenObject(Object object1, Object object2, Object target, float t, Curve curve, int spin){
		this.tweenBone(object1, object2, target, t, curve, spin);
		target.alpha = curve.tweenAngle(object1.alpha, object2.alpha, t);
		target.ref.set(object1.ref);
	}
	
	Timeline getSimilarTimeline(Timeline t){
    	Timeline found = getTimeline(t.name);
    	if(found == null && t.id < this.timelines()) found = this.getTimeline(t.id);
    	return found;
	}
	
	/*Timeline getSimilarTimeline(BoneRef ref, Collection coveredTimelines){
		if(ref.parent == null) return null;
    	for(BoneRef boneRef: this.currentKey.objectRefs){
    		Timeline t = this.getTimeline(boneRef.timeline);
    		if(boneRef.parent != null && boneRef.parent.id == ref.parent.id && !coveredTimelines.contains(t))
    			return t;
    	}
    	return null;
	}
	
	Timeline getSimilarTimeline(ObjectRef ref, Collection coveredTimelines){
		if(ref.parent == null) return null;
    	for(ObjectRef objRef: this.currentKey.objectRefs){
    		Timeline t = this.getTimeline(objRef.timeline);
    		if(objRef.parent != null && objRef.parent.id == ref.parent.id && !coveredTimelines.contains(t))
    			return t;
    	}
    	return null;
	}*/
	
	/**
	 * Prepares this animation to set this animation in any time state.
	 * This method has to be called before {@link #update(int, Bone)}.
	 */
	public void prepare(){
		if(this.prepared) return;
		this.tweenedKeys = new Timeline.Key[timelines.length];
		this.unmappedTweenedKeys = new Timeline.Key[timelines.length];
		
		for(int i = 0; i < this.tweenedKeys.length; i++){
			this.tweenedKeys[i] = new Timeline.Key(i);
			this.unmappedTweenedKeys[i] = new Timeline.Key(i);
			this.tweenedKeys[i].setObject(new Timeline.Key.Object(new Point(0,0)));
			this.unmappedTweenedKeys[i].setObject(new Timeline.Key.Object(new Point(0,0)));
		}
		if(mainline.keys.length > 0) currentKey = mainline.getKey(0);
		this.prepared = true;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy