Home | History | Annotate | Download | only in animation
      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 
     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 
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    252     public void addAllBones() {
    253         affectedBones = null;
    254     }
    255 
    256     /**
    257      * Add a single bone to be influenced by this animation channel.
    258      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    316     BitSet getAffectedBones(){
    317         return affectedBones;
    318     }
    319 
    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