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

com.jme3.anim.AnimComposer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2009-2022 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.anim;

import com.jme3.anim.tween.Tween;
import com.jme3.anim.tween.Tweens;
import com.jme3.anim.tween.action.*;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;
import com.jme3.util.clone.Cloner;
import java.io.IOException;
import java.util.*;

/**
 * AnimComposer is a Spatial control that allows manipulation of
 * {@link Armature armature} (skeletal) animation.
 *
 * @author Nehon
 */
public class AnimComposer extends AbstractControl {
    /**
     * The name of the default layer.
     */
    public static final String DEFAULT_LAYER = "Default";
    private Map animClipMap = new HashMap<>();

    private Map actions = new HashMap<>();
    private float globalSpeed = 1f;
    private Map layers = new LinkedHashMap<>(4);

    /**
     * Instantiate a composer with a single layer, no actions, and no clips.
     */
    public AnimComposer() {
        layers.put(DEFAULT_LAYER, new AnimLayer(this, DEFAULT_LAYER, null));
    }

    /**
     * Tells if an animation is contained in the list of animations.
     *
     * @param name The name of the animation.
     * @return true, if the named animation is in the list of animations.
     */
    public boolean hasAnimClip(String name) {
        return animClipMap.containsKey(name);
    }

    /**
     * Retrieve an animation from the list of animations.
     *
     * @param name The name of the animation to retrieve.
     * @return The animation corresponding to the given name, or null, if no
     * such named animation exists.
     */
    public AnimClip getAnimClip(String name) {
        return animClipMap.get(name);
    }

    /**
     * Adds an animation to be available for playing to this
     * AnimControl.
     *
     * @param anim The animation to add.
     */
    public void addAnimClip(AnimClip anim) {
        animClipMap.put(anim.getName(), anim);
    }

    /**
     * Remove an animation so that it is no longer available for playing.
     *
     * @param anim The animation to remove.
     */
    public void removeAnimClip(AnimClip anim) {
        if (!animClipMap.containsKey(anim.getName())) {
            throw new IllegalArgumentException("Given animation does not exist "
                    + "in this AnimControl");
        }

        animClipMap.remove(anim.getName());
    }

    /**
     * Run an action on the default layer. By default action will loop.
     *
     * @param name The name of the action to run.
     * @return The action corresponding to the given name.
     */
    public Action setCurrentAction(String name) {
        return setCurrentAction(name, DEFAULT_LAYER);
    }

    /**
     * Run an action on specified layer. By default action will loop.
     *
     * @param actionName The name of the action to run.
     * @param layerName The layer on which action should run.
     * @return The action corresponding to the given name.
     */
    public Action setCurrentAction(String actionName, String layerName) {
        return setCurrentAction(actionName, layerName, true);
    }

    /**
     * Run an action on specified layer.
     *
     * @param actionName The name of the action to run.
     * @param layerName The layer on which action should run.
     * @param loop True if the action must loop.
     * @return The action corresponding to the given name.
     */
    public Action setCurrentAction(String actionName, String layerName, boolean loop) {
        AnimLayer l = getLayer(layerName);
        Action currentAction = action(actionName);
        l.setCurrentAction(actionName, currentAction, loop);

        return currentAction;
    }

    /**
     * Return the current action on the default layer.
     *
     * @return The action corresponding to the given name.
     */
    public Action getCurrentAction() {
        return getCurrentAction(DEFAULT_LAYER);
    }

    /**
     * Return current action on specified layer.
     *
     * @param layerName The layer on which action should run.
     * @return The action corresponding to the given name.
     */
    public Action getCurrentAction(String layerName) {
        AnimLayer l = getLayer(layerName);
        Action result = l.getCurrentAction();

        return result;
    }

    /**
     * Remove current action on default layer.
     */
    public void removeCurrentAction() {
        removeCurrentAction(DEFAULT_LAYER);
    }

    /**
     * Remove current action on specified layer.
     *
     * @param layerName The name of the layer we want to remove its action.
     */
    public void removeCurrentAction(String layerName) {
        AnimLayer l = getLayer(layerName);
        l.setCurrentAction(null);
    }

    /**
     * Returns current time of the default layer.
     *
     * @return The current time.
     */
    public double getTime() {
        return getTime(DEFAULT_LAYER);
    }

    /**
     * Returns current time of the specified layer.
     *
     * @param layerName The layer from which to get the time.
     * @return the time (in seconds)
     */
    public double getTime(String layerName) {
        AnimLayer l = getLayer(layerName);
        double result = l.getTime();

        return result;
    }

    /**
     * Sets current time on the default layer.
     *
     * @param time the desired time (in seconds)
     */
    public void setTime(double time) {
        setTime(DEFAULT_LAYER, time);
    }

    /**
     * Sets current time on the specified layer.
     *
     * @param layerName the name of the Layer to modify
     * @param time the desired time (in seconds)
     */
    public void setTime(String layerName, double time) {
        AnimLayer l = getLayer(layerName);
        if (l.getCurrentAction() == null) {
            throw new RuntimeException("There is no action running in layer " + layerName);
        }

        l.setTime(time);
    }

    /**
     *
     * @param name The name of the action to return.
     * @return The action registered with specified name. It will make a new action if there isn't any.
     * @see #makeAction(java.lang.String)
     */
    public Action action(String name) {
        Action action = actions.get(name);
        if (action == null) {
            action = makeAction(name);
            actions.put(name, action);
        }
        return action;
    }

    /**
     *
     * @param name The name of the action to return.
     * @return The action registered with specified name or null if nothing is registered.
     */
    public Action getAction(String name) {
        return actions.get(name);
    }

    /**
     * Register given action with specified name.
     *
     * @param name The name of the action.
     * @param action The action to add.
     */
    public void addAction(String name, Action action) {
        actions.put(name, action);
    }

    /**
     * Create a new ClipAction with specified clip name.
     *
     * @param name The name of the clip.
     * @return a new action
     * @throws IllegalArgumentException if clip with specified name not found.
     */
    public Action makeAction(String name) {
        Action action;
        AnimClip clip = animClipMap.get(name);
        if (clip == null) {
            throw new IllegalArgumentException("Cannot find clip named " + name);
        }
        action = new ClipAction(clip);
        return action;
    }

    /**
     * Tells if an action is contained in the list of actions.
     *
     * @param name The name of the action.
     * @return true, if the named action is in the list of actions.
     */
    public boolean hasAction(String name) {
        return actions.containsKey(name);
    }

    /**
     * Remove specified action.
     *
     * @param name The name of the action to remove.
     * @return The removed action.
     */
    public Action removeAction(String name) {
        return actions.remove(name);
    }

    /**
     * Add a layer to this composer.
     *
     * @param name the desired name for the new layer
     * @param mask the desired mask for the new layer (alias created)
     */
    public void makeLayer(String name, AnimationMask mask) {
        AnimLayer l = new AnimLayer(this, name, mask);
        layers.put(name, l);
    }

    /**
     * Remove specified layer. This will stop the current action on this layer.
     *
     * @param name The name of the layer to remove.
     */
    public void removeLayer(String name) {
        layers.remove(name);
    }

    /**
     * Creates an action that will interpolate over an entire sequence
     * of tweens in order.
     *
     * @param name a name for the new Action
     * @param tweens the desired sequence of tweens
     * @return a new instance
     */
    public BaseAction actionSequence(String name, Tween... tweens) {
        BaseAction action = new BaseAction(Tweens.sequence(tweens));
        actions.put(name, action);
        return action;
    }

    /**
     * Creates an action that blends the named clips using the given blend
     * space.
     *
     * @param name a name for the new Action
     * @param blendSpace how to blend the clips (not null, alias created)
     * @param clips the names of the clips to be used (not null)
     * @return a new instance
     */
    public BlendAction actionBlended(String name, BlendSpace blendSpace, String... clips) {
        BlendableAction[] acts = new BlendableAction[clips.length];
        for (int i = 0; i < acts.length; i++) {
            BlendableAction ba = (BlendableAction) makeAction(clips[i]);
            acts[i] = ba;
        }
        BlendAction action = new BlendAction(blendSpace, acts);
        actions.put(name, action);
        return action;
    }

    /**
     * Reset all layers to t=0 with no current action.
     */
    public void reset() {
        for (AnimLayer layer : layers.values()) {
            layer.setCurrentAction(null);
        }
    }

    /**
     * Returns an unmodifiable collection of all available animations. When an attempt
     * is made to modify the collection, an UnsupportedOperationException is thrown.
     *
     * @return the unmodifiable collection of animations
     */
    public Collection getAnimClips() {
        return Collections.unmodifiableCollection(animClipMap.values());
    }

    /**
     * Returns an unmodifiable set of all available animation names. When an
     * attempt is made to modify the set, an UnsupportedOperationException is
     * thrown.
     *
     * @return the unmodifiable set of animation names.
     */
    public Set getAnimClipsNames() {
        return Collections.unmodifiableSet(animClipMap.keySet());
    }

    /**
     * used internally
     *
     * @param tpf time per frame (in seconds)
     */
    @Override
    protected void controlUpdate(float tpf) {
        for (AnimLayer layer : layers.values()) {
            layer.update(tpf);
        }
    }

    /**
     * used internally
     *
     * @param rm the RenderManager rendering the controlled Spatial (not null)
     * @param vp the ViewPort being rendered (not null)
     */
    @Override
    protected void controlRender(RenderManager rm, ViewPort vp) {

    }

    /**
     * Determine the global speed applied to all layers.
     *
     * @return the speed factor (1=normal speed)
     */
    public float getGlobalSpeed() {
        return globalSpeed;
    }

    /**
     * Alter the global speed applied to all layers.
     *
     * @param globalSpeed the desired speed factor (1=normal speed, default=1)
     */
    public void setGlobalSpeed(float globalSpeed) {
        this.globalSpeed = globalSpeed;
    }

    /**
     * Provides access to the named layer.
     *
     * @param layerName the name of the layer to access
     * @return the pre-existing instance
     */
    public AnimLayer getLayer(String layerName) {
        AnimLayer result = layers.get(layerName);
        if (result == null) {
            throw new IllegalArgumentException("Unknown layer " + layerName);
        }
        return result;
    }

    /**
     * Access the manager of the named layer.
     *
     * @param layerName the name of the layer to access
     * @return the current manager (typically an AnimEvent) or null for none
     */
    public Object getLayerManager(String layerName) {
        AnimLayer layer = getLayer(layerName);
        Object result = layer.getManager();

        return result;
    }

    /**
     * Enumerates the names of all layers.
     *
     * @return an unmodifiable set of names
     */
    public Set getLayerNames() {
        Set result = Collections.unmodifiableSet(layers.keySet());
        return result;
    }

    /**
     * Assign a manager to the named layer.
     *
     * @param layerName the name of the layer to modify
     * @param manager the desired manager (typically an AnimEvent) or null for
     * none
     */
    public void setLayerManager(String layerName, Object manager) {
        AnimLayer layer = getLayer(layerName);
        layer.setManager(manager);
    }

    /**
     * Create a shallow clone for the JME cloner.
     *
     * @return a new instance
     */
    @Override
    public Object jmeClone() {
        try {
            AnimComposer clone = (AnimComposer) super.clone();
            return clone;
        } catch (CloneNotSupportedException ex) {
            throw new AssertionError();
        }
    }

    /**
     * Callback from {@link com.jme3.util.clone.Cloner} to convert this
     * shallow-cloned composer into a deep-cloned one, using the specified
     * Cloner and original to resolve copied fields.
     *
     * @param cloner the Cloner that's cloning this composer (not null)
     * @param original the instance from which this composer was shallow-cloned
     * (not null, unaffected)
     */
    @Override
    public void cloneFields(Cloner cloner, Object original) {
        super.cloneFields(cloner, original);
        Map clips = new HashMap<>();
        for (String key : animClipMap.keySet()) {
            clips.put(key, cloner.clone(animClipMap.get(key)));
        }
        Map act = new HashMap<>();
        for (String key : actions.keySet()) {
            act.put(key, cloner.clone(actions.get(key)));
        }
        actions = act;
        animClipMap = clips;

        Map newLayers = new LinkedHashMap<>();
        for (String key : layers.keySet()) {
            newLayers.put(key, cloner.clone(layers.get(key)));
        }

        layers = newLayers;

    }

    /**
     * De-serialize this composer from the specified importer, for example when
     * loading from a J3O file.
     *
     * @param im the importer to use (not null)
     * @throws IOException from the importer
     */
    @Override
    @SuppressWarnings("unchecked")
    public void read(JmeImporter im) throws IOException {
        super.read(im);
        InputCapsule ic = im.getCapsule(this);
        animClipMap = (Map) ic.readStringSavableMap("animClipMap", new HashMap());
        globalSpeed = ic.readFloat("globalSpeed", 1f);
    }

    /**
     * Serialize this composer to the specified exporter, for example when
     * saving to a J3O file.
     *
     * @param ex the exporter to use (not null)
     * @throws IOException from the exporter
     */
    @Override
    public void write(JmeExporter ex) throws IOException {
        super.write(ex);
        OutputCapsule oc = ex.getCapsule(this);
        oc.writeStringSavableMap(animClipMap, "animClipMap", new HashMap());
        oc.write(globalSpeed, "globalSpeed", 1f);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy