-
Notifications
You must be signed in to change notification settings - Fork 1
/
BlendableAnimation.java
207 lines (194 loc) · 10.7 KB
/
BlendableAnimation.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
package com.scrappers.dbtraining.mainScreens.prefaceScreen.renderer.animationWrapper;
import com.jme3.anim.AnimClip;
import com.jme3.anim.AnimComposer;
import com.jme3.anim.AnimTrack;
import com.jme3.anim.ArmatureMask;
import com.jme3.anim.TransformTrack;
import com.jme3.anim.tween.Tweens;
import com.jme3.anim.tween.action.Action;
import com.jme3.anim.tween.action.BaseAction;
import com.jme3.anim.tween.action.BlendAction;
import com.jme3.anim.tween.action.BlendSpace;
import com.jme3.anim.tween.action.BlendableAction;
import com.jme3.anim.tween.action.ClipAction;
import com.jme3.anim.tween.action.LinearBlendSpace;
import com.jme3.anim.util.HasLocalTransform;
import com.jme3.app.Application;
import com.jme3.app.state.BaseAppState;
import com.jme3.cinematic.Cinematic;
import com.jme3.cinematic.events.AnimEvent;
import com.jme3.cinematic.events.AnimationEvent;
import com.jme3.cinematic.events.CinematicEvent;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import com.scrappers.dbtraining.mainScreens.prefaceScreen.renderer.animationWrapper.builders.LayerBuilder;
/**
* <b>A class demonstrating the usage of BlendableActions #{@link BlendableAction} & BlendActions #{@link BlendAction}</b>
* <ul>
* <li>---Attach the AnimClips#{@link AnimClip#setTracks(AnimTrack[])} instances (holding your TransformTracks#{@link TransformTrack}) to the AnimComposer#{@link AnimComposer#addAnimClip(AnimClip)}---</li>
* <li>---Feed the AnimClips#{@link AnimClip}(Concrete class of the abstract BlendableAction) into a Single BlendAction#{@link BlendAction#BlendAction(BlendSpace, BlendableAction...)}---</li>
* <li>---Add that Single BlendAction#{@link BlendAction} to the AnimComposer#{@link AnimComposer} using {@link AnimComposer#addAction(String, Action)}---</li>
* <li>---Run that BlendAction using the name as id using {@link AnimComposer#setCurrentAction(String)} in the default layer---</li>
* </ul>
* @author pavl_g
*/
public class BlendableAnimation extends BaseAppState implements BlendSpace {
private final Spatial dataBaseStack;
//create a global AnimComposer Control instance
private AnimComposer animComposer;
private LinearBlendSpace linearBlendSpace;
private ClipAction capRotationClip;
private BlendAction blendAction;
private BaseAction baseAction;
private float count = 0;
public BlendableAnimation(final String id, final Spatial dataBaseStack){
super(id);
this.dataBaseStack=dataBaseStack;
}
@Override
protected void initialize(Application app) {
//disabling the animation at the Start-up
setEnabled(false);
animComposer = dataBaseStack.getControl(AnimComposer.class);
//let's simulate a bottle glass fall on a floor..LoL
//1) collect the objects , you want to animate
//2)create a TransformTrack
final TransformTrack capRotation=new TransformTrack();
final TransformTrack bottleTraction=new TransformTrack();
//3)initialize settings for TransformTracks
//set a target object that implements HasLocalTransform(eg: Spatial , node , emitters , AudioNode , etc) for your track
capRotation.setTarget(dataBaseStack);
capRotation.setTimes(new float[]{2,4,8,16});
//specify the totalAngleOfRotation(theta) then divide that by number of keyFrames , to get the angle for each frame
capRotation.setKeyframesRotation(new Quaternion[]{
new Quaternion().fromAngleAxis(0f,Vector3f.UNIT_Y),
dataBaseStack.getLocalRotation().fromAngleAxis((float)Math.toRadians(120), Vector3f.UNIT_Y),
dataBaseStack.getLocalRotation().fromAngleAxis((float)Math.toRadians(120), Vector3f.UNIT_Y),
dataBaseStack.getLocalRotation().fromAngleAxis((float)Math.toRadians(120), Vector3f.UNIT_Y)
});
//set a target object that implements HasLocalTransform(eg: Spatial , node , emitters , AudioNode , etc) for your track
bottleTraction.setTarget(dataBaseStack);
bottleTraction.setTimes(new float[]{2,4,8,16});
bottleTraction.setKeyframesRotation(new Quaternion[]{
new Quaternion().fromAngleAxis(0f,Vector3f.UNIT_X),
dataBaseStack.getLocalRotation().fromAngleAxis((float)Math.toRadians(30), Vector3f.UNIT_X),
dataBaseStack.getLocalRotation().fromAngleAxis((float)Math.toRadians(30), Vector3f.UNIT_X),
dataBaseStack.getLocalRotation().fromAngleAxis((float)Math.toRadians(30), Vector3f.UNIT_X)
});
//4)create AnimClips instances & instantiate them to use the TransformTracks
final AnimClip capRotationAnimClip=new AnimClip("CapRotation");
final AnimClip bottleTractionAnimClip=new AnimClip("BottleTraction");
//5)set the tracks to the AnimClip
capRotationAnimClip.setTracks(new TransformTrack[]{capRotation});
bottleTractionAnimClip.setTracks(new TransformTrack[]{bottleTraction});
//6)add the AnimClips to the AnimComposer , the adds part
animComposer.addAnimClip(capRotationAnimClip);
animComposer.addAnimClip(bottleTractionAnimClip);
//8)Create ClipAction instances for the AnimClips (BlendableActions)
capRotationClip=new ClipAction(capRotationAnimClip);
ClipAction bottleTractionClip=new ClipAction(bottleTractionAnimClip);
bottleTractionClip.setTransitionLength(10f);
bottleTractionClip.setLength(10f);
capRotationClip.setLength(10f);
capRotationClip.setTransitionLength(10f);
//9)feed the BlendableActions to a single BlendAction
float minValueOfBlendSlider = 6;
float maxValueOfBlendSlider = 12;
linearBlendSpace = new LinearBlendSpace(minValueOfBlendSlider, maxValueOfBlendSlider);
linearBlendSpace.setValue(FastMath.interpolateLinear(0.5f, 6, 12));
blendAction=new BlendAction(linearBlendSpace, capRotationClip, bottleTractionClip);
baseAction=new BaseAction(Tweens.sequence(capRotationClip, blendAction));
baseAction.setLength(10f);
baseAction.setSpeed(2f);
//10)add that BlendAction to the AnimComposer using addAction(...)
animComposer.addAction("SimulateBottleFall", blendAction);
animComposer.makeLayer(LayerBuilder.LAYER_BLENDABLE_ANIM, new ArmatureMask());
}
@Override
protected void cleanup(Application app) {
}
@Override
protected void onEnable() {
if(animComposer != null){
//bind the AnimComposer to the appState status.
animComposer.setEnabled(true);
//11)run this BlendAction in the default layer
animComposer.setCurrentAction("SimulateBottleFall", LayerBuilder.LAYER_BLENDABLE_ANIM);
}
}
@Override
protected void onDisable() {
if(animComposer != null){
animComposer.removeCurrentAction(LayerBuilder.LAYER_BLENDABLE_ANIM);
}
}
@Override
public void update(float tpf) {
count += tpf;
if(count > blendAction.getLength()){
linearBlendSpace.setValue(FastMath.interpolateLinear(1, 6, 12));
}
}
/**
* additional method for additional settings to set during the instantiation of #{@link BlendAction} instance
* this method gets called inside the Constructor of #{@link BlendAction#BlendAction(BlendSpace, BlendableAction...)} when the BlendAction instance gets instantiated\.
* @param action your blendAction instance.
*/
@Override
public void setBlendAction(BlendAction action) {
/*sets the length of transitions between keyFrames-delay time between keyFrames*/
action.setSpeed(5f);
}
/**
* Returns the value of the weight of the transformations interpolation(ie delta or scaleFactor).
* #{@link BlendableAction#interpolate(double)} , #{@link BlendableAction#getWeight()} ,#{BlendAction#blendWeight}.
* #{@link BlendAction#collectTransform(HasLocalTransform, Transform, float, BlendableAction)} :
* basically collects the transforms of that model & outputs the result of interpolation between them using the scaleDeltaFactor TransitionWeight or blendWeight.
* @return the value of the weight of the transformations interpolation(ie delta or scaleFactor)
* @apiNote Notice :
* <ol>
* <li>
* If you set the delta(weight) of the interpolation to 1 :
* that means the next Transform would be equal to the next keyFrame that you have specified using #{@link TransformTrack#setKeyframes(float[], Vector3f[], Quaternion[], Vector3f[])}
* </li>
* <li>
* If you set the delta(weight) of the interpolation to values less than 1 :
* that means the next Transform would be less than the next keyFrame that you have specified using #{@link TransformTrack#setKeyframes(float[], Vector3f[], Quaternion[], Vector3f[])}
* by (1-getWeight()) value , due to the linear interpolation.
* </li>
* <li>
* If you set the delta(weight) of the interpolation to values higher than 1:
* that means the next Transform (of the 2nd active Action only) would be more greater than your local next keyFrame by (getWeight()*getLength()) times ,
* because #{@link Transform#interpolateTransforms(Transform, Transform, float)} isn't protected against values bigger than 1 , so values bigger than 1 would extrapolate it
* that means even if you didn't setScaleKeyFrames() , the BlendAction would interpolate between the tr1.getScales() & tr2.getScales() by a delta of 2 ,
* that would lead to scaling the object by 2 times it's initial value.
* </li>
* <li>
* If you set the delta(weight) of the interpolation to values Zero:
* that means the next Transform would be equal to the previous while iterating over your TransformTrack keyFrames(would lead to a very smooth motion) ,
* #{@link Transform#interpolateTransforms(Transform, Transform, float)}.
* </li>
* </ol>
*
* <b>NOTICE2(WIP) : i have also noticed that the 2nd , 3rd , 4th , .... blendable actions for the single blendAction run only if the getWeight() is > 1 , in the 2nd loop
* of the keyFrames
* --->because this #{@link BlendAction#setCollectTransformDelegate(BlendableAction)} gets settled to null after the first run of the #{@link BlendAction#doInterpolate(double)} &
* the secondary blendActions are dependant upon #{@link BlendAction#collect(HasLocalTransform, Transform)} to provide their interpolation which basically
* interpolates only if(getWeight() > 1) or less than 1.
*</b>
*/
@Override
public float getWeight() {
return 1;
}
/**
* this doesn't actually get called or used by anything in the universe
* @param value DON'T USE
*/
@Deprecated
@Override
public void setValue(float value) {}
}