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

com.jme3.animation.AnimChannel Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2009-2012 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme3.animation;

import com.jme3.math.FastMath;
import com.jme3.util.TempVars;
import java.util.BitSet;

/**
 * AnimChannel provides controls, such as play, pause,
 * fast forward, etc, for an animation. The animation
 * channel may influence the entire model or specific bones of the model's
 * skeleton. A single model may have multiple animation channels influencing
 * various parts of its body. For example, a character model may have an
 * animation channel for its feet, and another for its torso, and
 * the animations for each channel are controlled independently.
 * 
 * @author Kirill Vainer
 */
public final class AnimChannel {

    private static final float DEFAULT_BLEND_TIME = 0.15f;
    
    private AnimControl control;

    private BitSet affectedBones;

    private Animation animation;
    private Animation blendFrom;
    private float time;
    private float speed;
    private float timeBlendFrom;
    private float blendTime;
    private float speedBlendFrom;
    private boolean notified=false;

    private LoopMode loopMode, loopModeBlendFrom;
    
    private float blendAmount = 1f;
    private float blendRate   = 0;
    
    AnimChannel(AnimControl control){
        this.control = control;
    }

    /**
     * Returns the parent control of this AnimChannel.
     * 
     * @return the parent control of this AnimChannel.
     * @see AnimControl
     */
    public AnimControl getControl() {
        return control;
    }
    
    /**
     * @return The name of the currently playing animation, or null if
     * none is assigned.
     *
     * @see AnimChannel#setAnim(java.lang.String) 
     */
    public String getAnimationName() {
        return animation != null ? animation.getName() : null;
    }

    /**
     * @return The loop mode currently set for the animation. The loop mode
     * determines what will happen to the animation once it finishes
     * playing.
     * 
     * For more information, see the LoopMode enum class.
     * @see LoopMode
     * @see AnimChannel#setLoopMode(com.jme3.animation.LoopMode)
     */
    public LoopMode getLoopMode() {
        return loopMode;
    }

    /**
     * @param loopMode Set the loop mode for the channel. The loop mode
     * determines what will happen to the animation once it finishes
     * playing.
     *
     * For more information, see the LoopMode enum class.
     * @see LoopMode
     */
    public void setLoopMode(LoopMode loopMode) {
        this.loopMode = loopMode;
    }

    /**
     * @return The speed that is assigned to the animation channel. The speed
     * is a scale value starting from 0.0, at 1.0 the animation will play
     * at its default speed.
     *
     * @see AnimChannel#setSpeed(float)
     */
    public float getSpeed() {
        return speed;
    }

    /**
     * @param speed Set the speed of the animation channel. The speed
     * is a scale value starting from 0.0, at 1.0 the animation will play
     * at its default speed.
     */
    public void setSpeed(float speed) {
        this.speed = speed;
        if(blendTime>0){
            this.speedBlendFrom = speed;
            blendTime = Math.min(blendTime, animation.getLength() / speed);  
            blendRate = 1/ blendTime;
        }
    }

    /**
     * @return The time of the currently playing animation. The time
     * starts at 0 and continues on until getAnimMaxTime().
     *
     * @see AnimChannel#setTime(float)
     */
    public float getTime() {
        return time;
    }

    /**
     * @param time Set the time of the currently playing animation, the time
     * is clamped from 0 to {@link #getAnimMaxTime()}. 
     */
    public void setTime(float time) {
        this.time = FastMath.clamp(time, 0, getAnimMaxTime());
    }

    /**
     * @return The length of the currently playing animation, or zero
     * if no animation is playing.
     *
     * @see AnimChannel#getTime()
     */
    public float getAnimMaxTime(){
        return animation != null ? animation.getLength() : 0f;
    }

    /**
     * Set the current animation that is played by this AnimChannel.
     * 

* This resets the time to zero, and optionally blends the animation * over blendTime seconds with the currently playing animation. * Notice that this method will reset the control's speed to 1.0. * * @param name The name of the animation to play * @param blendTime The blend time over which to blend the new animation * with the old one. If zero, then no blending will occur and the new * animation will be applied instantly. */ public void setAnim(String name, float blendTime){ if (name == null) throw new IllegalArgumentException("name cannot be null"); if (blendTime < 0f) throw new IllegalArgumentException("blendTime cannot be less than zero"); Animation anim = control.animationMap.get(name); if (anim == null) throw new IllegalArgumentException("Cannot find animation named: '"+name+"'"); control.notifyAnimChange(this, name); if (animation != null && blendTime > 0f){ this.blendTime = blendTime; // activate blending blendTime = Math.min(blendTime, anim.getLength() / speed); blendFrom = animation; timeBlendFrom = time; speedBlendFrom = speed; loopModeBlendFrom = loopMode; blendAmount = 0f; blendRate = 1f / blendTime; }else{ blendFrom = null; } animation = anim; time = 0; speed = 1f; loopMode = LoopMode.Loop; notified = false; } /** * Set the current animation that is played by this AnimChannel. *

* See {@link #setAnim(java.lang.String, float)}. * The blendTime argument by default is 150 milliseconds. * * @param name The name of the animation to play */ public void setAnim(String name){ setAnim(name, DEFAULT_BLEND_TIME); } /** * Add all the bones of the model's skeleton to be * influenced by this animation channel. */ public void addAllBones() { affectedBones = null; } /** * Add a single bone to be influenced by this animation channel. */ public void addBone(String name) { addBone(control.getSkeleton().getBone(name)); } /** * Add a single bone to be influenced by this animation channel. */ public void addBone(Bone bone) { int boneIndex = control.getSkeleton().getBoneIndex(bone); if(affectedBones == null) { affectedBones = new BitSet(control.getSkeleton().getBoneCount()); } affectedBones.set(boneIndex); } /** * Add bones to be influenced by this animation channel starting from the * given bone name and going toward the root bone. */ public void addToRootBone(String name) { addToRootBone(control.getSkeleton().getBone(name)); } /** * Add bones to be influenced by this animation channel starting from the * given bone and going toward the root bone. */ public void addToRootBone(Bone bone) { addBone(bone); while (bone.getParent() != null) { bone = bone.getParent(); addBone(bone); } } /** * Add bones to be influenced by this animation channel, starting * from the given named bone and going toward its children. */ public void addFromRootBone(String name) { addFromRootBone(control.getSkeleton().getBone(name)); } /** * Add bones to be influenced by this animation channel, starting * from the given bone and going toward its children. */ public void addFromRootBone(Bone bone) { addBone(bone); if (bone.getChildren() == null) return; for (Bone childBone : bone.getChildren()) { addBone(childBone); addFromRootBone(childBone); } } BitSet getAffectedBones(){ return affectedBones; } public void reset(boolean rewind){ if(rewind){ setTime(0); if(control.getSkeleton()!=null){ control.getSkeleton().resetAndUpdate(); }else{ TempVars vars = TempVars.get(); update(0, vars); vars.release(); } } animation = null; notified = false; } void update(float tpf, TempVars vars) { if (animation == null) return; if (blendFrom != null && blendAmount != 1.0f){ // The blendFrom anim is set, the actual animation // playing will be set // blendFrom.setTime(timeBlendFrom, 1f, control, this, vars); blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this, vars); timeBlendFrom += tpf * speedBlendFrom; timeBlendFrom = AnimationUtils.clampWrapTime(timeBlendFrom, blendFrom.getLength(), loopModeBlendFrom); if (timeBlendFrom < 0){ timeBlendFrom = -timeBlendFrom; speedBlendFrom = -speedBlendFrom; } blendAmount += tpf * blendRate; if (blendAmount > 1f){ blendAmount = 1f; blendFrom = null; } } animation.setTime(time, blendAmount, control, this, vars); time += tpf * speed; if (animation.getLength() > 0){ if (!notified && (time >= animation.getLength() || time < 0)) { if (loopMode == LoopMode.DontLoop) { // Note that this flag has to be set before calling the notify // since the notify may start a new animation and then unset // the flag. notified = true; } control.notifyAnimCycleDone(this, animation.getName()); } } time = AnimationUtils.clampWrapTime(time, animation.getLength(), loopMode); if (time < 0){ // Negative time indicates that speed should be inverted // (for cycle loop mode only) time = -time; speed = -speed; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy