• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2010 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 package com.jme3.animation;
34 
35 import com.jme3.math.FastMath;
36 import com.jme3.util.TempVars;
37 import java.util.BitSet;
38 
39 /**
40  * <code>AnimChannel</code> provides controls, such as play, pause,
41  * fast forward, etc, for an animation. The animation
42  * channel may influence the entire model or specific bones of the model's
43  * skeleton. A single model may have multiple animation channels influencing
44  * various parts of its body. For example, a character model may have an
45  * animation channel for its feet, and another for its torso, and
46  * the animations for each channel are controlled independently.
47  *
48  * @author Kirill Vainer
49  */
50 public final class AnimChannel {
51 
52     private static final float DEFAULT_BLEND_TIME = 0.15f;
53 
54     private AnimControl control;
55 
56     private BitSet affectedBones;
57 
58     private Animation animation;
59     private Animation blendFrom;
60     private float time;
61     private float speed;
62     private float timeBlendFrom;
63     private float speedBlendFrom;
64 
65     private LoopMode loopMode, loopModeBlendFrom;
66 
67     private float blendAmount = 1f;
68     private float blendRate   = 0;
69 
clampWrapTime(float t, float max, LoopMode loopMode)70     private static float clampWrapTime(float t, float max, LoopMode loopMode){
71         if (max == Float.POSITIVE_INFINITY)
72             return t;
73 
74         if (t < 0f){
75             //float tMod = -(-t % max);
76             switch (loopMode){
77                 case DontLoop:
78                     return 0;
79                 case Cycle:
80                     return t;
81                 case Loop:
82                     return max - t;
83             }
84         }else if (t > max){
85             switch (loopMode){
86                 case DontLoop:
87                     return max;
88                 case Cycle:
89                     return /*-max;*/-(2f * max - t);
90                 case Loop:
91                     return t - max;
92             }
93         }
94 
95         return t;
96     }
97 
AnimChannel(AnimControl control)98     AnimChannel(AnimControl control){
99         this.control = control;
100     }
101 
102     /**
103      * Returns the parent control of this AnimChannel.
104      *
105      * @return the parent control of this AnimChannel.
106      * @see AnimControl
107      */
getControl()108     public AnimControl getControl() {
109         return control;
110     }
111 
112     /**
113      * @return The name of the currently playing animation, or null if
114      * none is assigned.
115      *
116      * @see AnimChannel#setAnim(java.lang.String)
117      */
getAnimationName()118     public String getAnimationName() {
119         return animation != null ? animation.getName() : null;
120     }
121 
122     /**
123      * @return The loop mode currently set for the animation. The loop mode
124      * determines what will happen to the animation once it finishes
125      * playing.
126      *
127      * For more information, see the LoopMode enum class.
128      * @see LoopMode
129      * @see AnimChannel#setLoopMode(com.jme3.animation.LoopMode)
130      */
getLoopMode()131     public LoopMode getLoopMode() {
132         return loopMode;
133     }
134 
135     /**
136      * @param loopMode Set the loop mode for the channel. The loop mode
137      * determines what will happen to the animation once it finishes
138      * playing.
139      *
140      * For more information, see the LoopMode enum class.
141      * @see LoopMode
142      */
setLoopMode(LoopMode loopMode)143     public void setLoopMode(LoopMode loopMode) {
144         this.loopMode = loopMode;
145     }
146 
147     /**
148      * @return The speed that is assigned to the animation channel. The speed
149      * is a scale value starting from 0.0, at 1.0 the animation will play
150      * at its default speed.
151      *
152      * @see AnimChannel#setSpeed(float)
153      */
getSpeed()154     public float getSpeed() {
155         return speed;
156     }
157 
158     /**
159      * @param speed Set the speed of the animation channel. The speed
160      * is a scale value starting from 0.0, at 1.0 the animation will play
161      * at its default speed.
162      */
setSpeed(float speed)163     public void setSpeed(float speed) {
164         this.speed = speed;
165     }
166 
167     /**
168      * @return The time of the currently playing animation. The time
169      * starts at 0 and continues on until getAnimMaxTime().
170      *
171      * @see AnimChannel#setTime(float)
172      */
getTime()173     public float getTime() {
174         return time;
175     }
176 
177     /**
178      * @param time Set the time of the currently playing animation, the time
179      * is clamped from 0 to {@link #getAnimMaxTime()}.
180      */
setTime(float time)181     public void setTime(float time) {
182         this.time = FastMath.clamp(time, 0, getAnimMaxTime());
183     }
184 
185     /**
186      * @return The length of the currently playing animation, or zero
187      * if no animation is playing.
188      *
189      * @see AnimChannel#getTime()
190      */
getAnimMaxTime()191     public float getAnimMaxTime(){
192         return animation != null ? animation.getLength() : 0f;
193     }
194 
195     /**
196      * Set the current animation that is played by this AnimChannel.
197      * <p>
198      * This resets the time to zero, and optionally blends the animation
199      * over <code>blendTime</code> seconds with the currently playing animation.
200      * Notice that this method will reset the control's speed to 1.0.
201      *
202      * @param name The name of the animation to play
203      * @param blendTime The blend time over which to blend the new animation
204      * with the old one. If zero, then no blending will occur and the new
205      * animation will be applied instantly.
206      */
setAnim(String name, float blendTime)207     public void setAnim(String name, float blendTime){
208         if (name == null)
209             throw new NullPointerException();
210 
211         if (blendTime < 0f)
212             throw new IllegalArgumentException("blendTime cannot be less than zero");
213 
214         Animation anim = control.animationMap.get(name);
215         if (anim == null)
216             throw new IllegalArgumentException("Cannot find animation named: '"+name+"'");
217 
218         control.notifyAnimChange(this, name);
219 
220         if (animation != null && blendTime > 0f){
221             // activate blending
222             blendFrom = animation;
223             timeBlendFrom = time;
224             speedBlendFrom = speed;
225             loopModeBlendFrom = loopMode;
226             blendAmount = 0f;
227             blendRate   = 1f / blendTime;
228         }
229 
230         animation = anim;
231         time = 0;
232         speed = 1f;
233         loopMode = LoopMode.Loop;
234     }
235 
236     /**
237      * Set the current animation that is played by this AnimChannel.
238      * <p>
239      * See {@link #setAnim(java.lang.String, float)}.
240      * The blendTime argument by default is 150 milliseconds.
241      *
242      * @param name The name of the animation to play
243      */
setAnim(String name)244     public void setAnim(String name){
245         setAnim(name, DEFAULT_BLEND_TIME);
246     }
247 
248     /**
249      * Add all the bones of the model's skeleton to be
250      * influenced by this animation channel.
251      */
addAllBones()252     public void addAllBones() {
253         affectedBones = null;
254     }
255 
256     /**
257      * Add a single bone to be influenced by this animation channel.
258      */
addBone(String name)259     public void addBone(String name) {
260         addBone(control.getSkeleton().getBone(name));
261     }
262 
263     /**
264      * Add a single bone to be influenced by this animation channel.
265      */
addBone(Bone bone)266     public void addBone(Bone bone) {
267         int boneIndex = control.getSkeleton().getBoneIndex(bone);
268         if(affectedBones == null) {
269             affectedBones = new BitSet(control.getSkeleton().getBoneCount());
270         }
271         affectedBones.set(boneIndex);
272     }
273 
274     /**
275      * Add bones to be influenced by this animation channel starting from the
276      * given bone name and going toward the root bone.
277      */
addToRootBone(String name)278     public void addToRootBone(String name) {
279         addToRootBone(control.getSkeleton().getBone(name));
280     }
281 
282     /**
283      * Add bones to be influenced by this animation channel starting from the
284      * given bone and going toward the root bone.
285      */
addToRootBone(Bone bone)286     public void addToRootBone(Bone bone) {
287         addBone(bone);
288         while (bone.getParent() != null) {
289             bone = bone.getParent();
290             addBone(bone);
291         }
292     }
293 
294     /**
295      * Add bones to be influenced by this animation channel, starting
296      * from the given named bone and going toward its children.
297      */
addFromRootBone(String name)298     public void addFromRootBone(String name) {
299         addFromRootBone(control.getSkeleton().getBone(name));
300     }
301 
302     /**
303      * Add bones to be influenced by this animation channel, starting
304      * from the given bone and going toward its children.
305      */
addFromRootBone(Bone bone)306     public void addFromRootBone(Bone bone) {
307         addBone(bone);
308         if (bone.getChildren() == null)
309             return;
310         for (Bone childBone : bone.getChildren()) {
311             addBone(childBone);
312             addFromRootBone(childBone);
313         }
314     }
315 
getAffectedBones()316     BitSet getAffectedBones(){
317         return affectedBones;
318     }
319 
update(float tpf, TempVars vars)320     void update(float tpf, TempVars vars) {
321         if (animation == null)
322             return;
323 
324         if (blendFrom != null){
325             blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this, vars);
326             //blendFrom.setTime(timeBlendFrom, control.skeleton, 1f - blendAmount, affectedBones);
327             timeBlendFrom += tpf * speedBlendFrom;
328             timeBlendFrom = clampWrapTime(timeBlendFrom,
329                                           blendFrom.getLength(),
330                                           loopModeBlendFrom);
331             if (timeBlendFrom < 0){
332                 timeBlendFrom = -timeBlendFrom;
333                 speedBlendFrom = -speedBlendFrom;
334             }
335 
336             blendAmount += tpf * blendRate;
337             if (blendAmount > 1f){
338                 blendAmount = 1f;
339                 blendFrom = null;
340             }
341         }
342 
343         animation.setTime(time, blendAmount, control, this, vars);
344         //animation.setTime(time, control.skeleton, blendAmount, affectedBones);
345         time += tpf * speed;
346 
347         if (animation.getLength() > 0){
348             if (time >= animation.getLength()) {
349                 control.notifyAnimCycleDone(this, animation.getName());
350             } else if (time < 0) {
351                 control.notifyAnimCycleDone(this, animation.getName());
352             }
353         }
354 
355         time = clampWrapTime(time, animation.getLength(), loopMode);
356         if (time < 0){
357             time = -time;
358             speed = -speed;
359         }
360 
361 
362     }
363 }
364