/**************************************************************************
* Copyright 2013 by Trixt0r
* (https://github.com/Trixt0r, Heinrich Reich, e-mail: trixter16@web.de)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
package com.brashmonkey.spriter.player;
import com.brashmonkey.spriter.SpriterCalculator;
import com.brashmonkey.spriter.animation.SpriterKeyFrame;
import com.brashmonkey.spriter.draw.DrawInstruction;
import com.brashmonkey.spriter.objects.SpriterObject;
/**
* This class is made to interpolate between two running animations.
* The idea is, to give an instance of this class two AbstractSpriterPlayer objects which hold and animate the same spriter entity.
* This will interpolate the runtime transformations of the bones and objects with a weight between 0 and 1.
* You will be also able to interpolate SpriterPlayerInterpolators with each other, since it extends #SpriterAbstractPlayer.
* Note that this #SpriterAbstractPlayer needs 3 times more calculation effort than a normal #SpriterPlayer.
*
* @author Trixt0r
*/
public class SpriterPlayerInterpolator extends SpriterAbstractPlayer{
private SpriterAbstractPlayer first, second;
private float weight;
public boolean updatePlayers = true;
/**
* Returns an instance of this class, which will manage the interpolation between two #SpriterAbstractPlayer instances.
* @param first player to interpolate with the second one.
* @param second player to interpolate with the first one.
*/
public SpriterPlayerInterpolator(SpriterAbstractPlayer first, SpriterAbstractPlayer second){
super(first.loader, first.animations);
this.weight = 0.5f;
setPlayers(first, second);
this.generateData();
this.update(0, 0);
}
/**
* Note: Make sure, that both instances hold the same bone and object structure.
* Otherwise you will not get the interpolation you wish.
* @param first SpriterPlayer instance to interpolate.
* @param second SpriterPlayer instance to interpolate.
*/
public void setPlayers(SpriterAbstractPlayer first, SpriterAbstractPlayer second){
this.first = first;
this.second = second;
this.moddedBones = this.first.moddedBones;
this.moddedObjects = this.first.moddedObjects;
this.first.setRootParent(this.rootParent);
this.second.setRootParent(this.rootParent);
}
/**
* @param weight to set. 0 means the animation of the first player will get played back.
* 1 means the second player will get played back.
*/
public void setWeight(float weight){
this.weight = weight;
}
/**
* @return The current weight.
*/
public float getWeight(){
return this.weight;
}
/**
* @return The first player.
*/
public SpriterAbstractPlayer getFirst(){
return this.first;
}
/**
* @return The second player.
*/
public SpriterAbstractPlayer getSecond(){
return this.second;
}
@Override
protected void step(float xOffset, float yOffset){
int firstLastSpeed = first.frameSpeed, secondLastSpeed = second.frameSpeed;
//int speed = this.frameSpeed;
//if(this.interpolateSpeed) speed = (int)this.interpolate(first.frameSpeed, second.frameSpeed, 0, 1, this.weight);
//this.first.frameSpeed = speed;
//this.second.frameSpeed = speed;
this.moddedBones = (this.weight <= 0.5f) ? this.first.moddedBones: this.second.moddedBones;
this.moddedObjects = (this.weight <= 0.5f) ? this.first.moddedObjects: this.second.moddedObjects;
this.currenObjectsToDraw = Math.max(first.currenObjectsToDraw, second.currenObjectsToDraw);
this.currentBonesToAnimate = Math.max(first.currentBonesToAnimate, second.currentBonesToAnimate);
if(this.updatePlayers){
this.first.update(xOffset, yOffset);
this.second.update(xOffset, yOffset);
}
SpriterKeyFrame key1 = (first.transitionFixed) ? first.lastFrame: first.lastTempFrame;
SpriterKeyFrame key2 = (second.transitionFixed) ? second.lastFrame: second.lastTempFrame;
this.transformBones(key1, key2, xOffset, yOffset);
this.transformObjects(first.lastFrame, second.lastFrame, xOffset, yOffset);
this.first.frameSpeed = firstLastSpeed;
this.second.frameSpeed = secondLastSpeed;
}
@Override
protected void setInstructionRef(DrawInstruction dI, SpriterObject obj1, SpriterObject obj2){
dI.ref = (this.weight <= 0.5f || obj2 == null) ? obj1.getRef(): obj2.getRef();
dI.loader = (this.weight <= 0.5f || obj2 == null) ? obj1.getLoader(): obj2.getLoader();
dI.obj = (this.weight <= 0.5f || obj2 == null) ? obj1: obj2;
}
/**
* See {@link SpriterCalculator#calculateInterpolation(float, float, float, float, long)}
* Can be inherited, to handle other interpolation techniques. Standard is linear interpolation.
*/
@Override
protected float interpolate(float a, float b, float timeA, float timeB, float currentTime){
return this.interpolator.interpolate(a, b, 0, 1, this.weight);
}
/**
* See {@link SpriterCalculator#calculateInterpolation(float, float, float, float, long)}
* Can be inherited, to handle other interpolation techniques. Standard is linear interpolation.
*/
@Override
protected float interpolateAngle(float a, float b, float timeA, float timeB, float currentTime){
return this.interpolator.interpolateAngle(a, b, 0, 1, this.weight);
}
/**
* @return true if this player also interpolates the speed of both players. false if not.
*/
/*public boolean interpolatesSpeed(){
return this.interpolateSpeed;
}*/
/**
* @param inter indicates whether this player has to interpolate the speed of bother players or not.
* If it set to false, this player will set for both players the speed which this player has. See {@link #setFrameSpeed(int)}
*/
/*public void setInterpolateSpeed(boolean inter){
this.interpolateSpeed = inter;
}*/
}