⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 keyframecontroller.java

📁 java 3d game jme 工程开发源代码
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/*
 * Copyright (c) 2003-2009 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.jmex.model.animation;

import java.io.IOException;
import java.io.Serializable;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.logging.Logger;

import com.jme.scene.Controller;
import com.jme.scene.TexCoords;
import com.jme.scene.TriMesh;
import com.jme.util.export.InputCapsule;
import com.jme.util.export.JMEExporter;
import com.jme.util.export.JMEImporter;
import com.jme.util.export.OutputCapsule;
import com.jme.util.export.Savable;
import com.jme.util.geom.BufferUtils;

/**
 * Started Date: Jun 12, 2004 <br>
 * <br>
 * 
 * Class can do linear interpolation of a TriMesh between units of time. Similar
 * to <code>VertexKeyframeController</code> but interpolates float units of
 * time instead of integer keyframes. <br>
 * <br>
 * Controller.setSpeed(float) sets a speed relative to the defined speed. For
 * example, the default is 1. A speed of 2 would run twice as fast and a speed
 * of .5 would run half as fast <br>
 * <br>
 * Controller.setMinTime(float) and Controller.setMaxTime(float) both define the
 * bounds that KeyframeController should follow. It is the programmer's
 * responsiblity to make sure that the MinTime and MaxTime are within the span
 * of the defined <code>setKeyframe</code><br>
 * <br>
 * Controller functions RepeatType and isActive are both defined in their
 * default way for KeyframeController <br>
 * <br>
 * When this controller is saved/loaded to XML format, it assumes that the mesh
 * it morphs is the TriMesh it belongs to, so it is recomended to only attach
 * this controller to the TriMesh it animates.
 * 
 * @author Jack Lindamood, kevglass (parts), hevee (blend time)
 * @version $Id: KeyframeController.java,v 1.16 2007/08/02 23:09:04 nca Exp $
 */
public class KeyframeController extends Controller {
    private static final Logger logger = Logger.getLogger(KeyframeController.class.getName());

    private static final long serialVersionUID = 1L;

    /**
     * An array of <code>PointInTime</code> s that defines the animation
     */
    transient public ArrayList<PointInTime> keyframes;

    /**
     * A special array used with SmoothTransform to store temporary smooth
     * transforms
     */
    transient private ArrayList<PointInTime> prevKeyframes;

    /**
     * The mesh that is actually morphed
     */
    private TriMesh morphMesh;

    /**
     * The current time in the animation
     */
    transient private float curTime;

    /**
     * The current frame of the animation
     */
    transient private int curFrame;

    /**
     * The frame of animation we're heading towards
     */
    transient private int nextFrame;
    
    /**
     * The PointInTime before <code>curTime</code>
     */
    transient private PointInTime before;

    /**
     * The PointInTime after <code>curTime</code>
     */
    transient private PointInTime after;

    /**
     * If true, the animation is moving forward, if false the animation is
     * moving backwards
     */
    transient private boolean movingForward;

    /**
     * Used with SmoothTransform to signal it is doing a smooth transform
     */
    transient private boolean isSmooth;

    /**
     * Used with SmoothTransform to hold the new beinging and ending time once
     * the transform is complete
     */
    transient private float tempNewBeginTime;

    transient private float tempNewEndTime;

    /** If true, the model's bounding volume will update every frame. */
    private boolean updatePerFrame;

    /**
     * Default constructor. Speed is 1, MinTime is 0 MaxTime is 0. Both MinTime
     * and MaxTime are automatically adjusted by setKeyframe if the setKeyframe
     * time is less than MinTime or greater than MaxTime. Default RepeatType is
     * RT_WRAP.
     */
    public KeyframeController() {
        this.setSpeed(1);
        keyframes = new ArrayList<PointInTime>();
        curFrame = 0;
        this.setRepeatType(Controller.RT_WRAP);
        movingForward = true;
        this.setMinTime(0);
        this.setMaxTime(0);
        updatePerFrame = true;
    }
    
    public float getCurrentTime() {
        return curTime;
    }
    
    public int getCurrentFrame() {
        return curFrame;
    }

    /**
     * Gets the current time in the animation
     */
    public float getCurTime(){return curTime;}
    
    /**
     * Sets the current time in the animation
     * @param time
     *            The time this Controller should continue at
     */
    public void setCurTime(float time){ curTime = time;}
    
    /**
     * Sets the Mesh that will be physically changed by this KeyframeController
     * 
     * @param morph
     *            The new mesh to morph
     */
    public void setMorphingMesh(TriMesh morph) {
        morphMesh = morph;
        keyframes.clear();
        keyframes.add(new PointInTime(0, null));
    }

    public void shallowSetMorphMesh(TriMesh morph) {
        morphMesh = morph;
    }

    /**
     * Tells the controller to change its morphMesh to <code>shape</code> at
     * <code>time</code> seconds. Time must be >=0 and shape must be non-null
     * and shape must have the same number of vertexes as the current shape. If
     * not, then nothing happens. It is also required that
     * <code>setMorphingMesh(TriMesh)</code> is called before
     * <code>setKeyframe</code>. It is assumed that shape.indices ==
     * morphMesh.indices, otherwise morphing may look funny
     * 
     * @param time
     *            The time for the change
     * @param shape
     *            The new shape at that time
     */
    public void setKeyframe(float time, TriMesh shape) {
        if (morphMesh == null || time < 0
                || shape.getVertexBuffer().capacity() != morphMesh.getVertexBuffer().capacity())
                return;
        for (int i = 0; i < keyframes.size(); i++) {
            PointInTime lookingTime = keyframes.get(i);
            if (lookingTime.time == time) {
                lookingTime.newShape = shape;
                return;
            }
            if (lookingTime.time > time) {
                keyframes.add(i, new PointInTime(time, shape));
                return;
            }
        }
        keyframes.add(new PointInTime(time, shape));
        if (time > this.getMaxTime()) this.setMaxTime(time);
        if (time < this.getMinTime()) this.setMinTime(time);
    }

    /**
     * This function will do a smooth translation between a keframe's current
     * look, to the look directly at newTimeToReach. It takes translationLen
     * time (in sec) to do that translation, and once translated will animate
     * like normal between newBeginTime and newEndTime <br>
     * <br>
     * This would be usefull for example when a figure stops running and tries
     * to raise an arm. Instead of "teleporting" to the raise-arm animation
     * begining, a smooth translation can occur.
     * 
     * @param newTimeToReach
     *            The time to reach.
     * @param translationLen
     *            How long it takes
     * @param newBeginTime
     *            The new cycle begining time
     * @param newEndTime
     *            The new cycle ending time.
     */
    public void setSmoothTranslation(float newTimeToReach,
            float translationLen, float newBeginTime, float newEndTime) {
        if (!isActive() || isSmooth) return;
        if (newBeginTime < 0
                || newBeginTime >  keyframes.get(keyframes.size() - 1).time) {
            logger.warning("Attempt to set invalid begintime:" + newBeginTime);
            return;
        }
        if (newEndTime < 0
                || newEndTime >  keyframes.get(keyframes.size() - 1).time) {
            logger.warning(
                    "Attempt to set invalid endtime:" + newEndTime);
            return;
        }
        TriMesh begin = null, end = null;
        if (prevKeyframes == null) {
            prevKeyframes = new ArrayList<PointInTime>();
            begin = new TriMesh();
            end = new TriMesh();
        } else {
            begin = prevKeyframes.get(0).newShape;
            end = prevKeyframes.get(1).newShape;
            prevKeyframes.clear();
        }

        getCurrent(begin);

        curTime = newTimeToReach;
        curFrame = 0;
        setMinTime(0);
        setMaxTime( keyframes.get(keyframes.size() - 1).time);
        update(0);
        getCurrent(end);

        swapKeyframeSets();
        curTime = 0;
        curFrame = 0;
        setMinTime(0);
        setMaxTime(translationLen);
        setKeyframe(0, begin);
        setKeyframe(translationLen, end);
        isSmooth = true;
        tempNewBeginTime = newBeginTime;
        tempNewEndTime = newEndTime;
    }

    /**
     * Swaps prevKeyframes and keyframes
     */
    private void swapKeyframeSets() {
        ArrayList<PointInTime> temp = keyframes;
        keyframes = prevKeyframes;
        prevKeyframes = temp;
    }

    /**
     * Sets the new animation boundaries for this controller. This will start at
     * newBeginTime and proceed in the direction of newEndTime (either forwards
     * or backwards). If both are the same, then the animation is set to their
     * time and turned off, otherwise the animation is turned on to start the
     * animation acording to the repeat type. If either BeginTime or EndTime are
     * invalid times (less than 0 or greater than the maximum set keyframe time)
     * then a warning is set and nothing happens. <br>
     * It is suggested that this function be called if new animation boundaries
     * need to be set, instead of setMinTime and setMaxTime directly.
     * 
     * @param newBeginTime
     *            The starting time
     * @param newEndTime
     *            The ending time
     */
    public void setNewAnimationTimes(float newBeginTime, float newEndTime) {
        if (isSmooth) return;
        if (newBeginTime < 0
                || newBeginTime >  keyframes
                        .get(keyframes.size() - 1).time) {
            logger.warning(
                    "Attempt to set invalid begintime:" + newBeginTime);
            return;
        }
        if (newEndTime < 0
                || newEndTime >  keyframes
                        .get(keyframes.size() - 1).time) {
            logger.warning(
                    "Attempt to set invalid endtime:" + newEndTime);
            return;
        }
        setMinTime(newBeginTime);
        setMaxTime(newEndTime);
        setActive(true);
        if (newBeginTime <= newEndTime) { // Moving forward
            movingForward = true;
            curTime = newBeginTime;
            if (newBeginTime == newEndTime) {
                update(0);
                setActive(false);
            }
        } else { // Moving backwards
            movingForward = false;
            curTime = newEndTime;
        }
    }

    /**
     * Saves whatever the current morphMesh looks like into the dataCopy
     * 
     * @param dataCopy
     *            The copy to save the current mesh into
     */
    private void getCurrent(TriMesh dataCopy) {
        if (morphMesh.getColorBuffer() != null) {
            FloatBuffer dcColors = dataCopy.getColorBuffer();
            if (dcColors != null)
                dcColors.clear();
            FloatBuffer mmColors = morphMesh.getColorBuffer();
            mmColors.clear();
            if (dcColors == null || dcColors.capacity() != mmColors.capacity()) {
                dcColors = BufferUtils.createFloatBuffer(mmColors.capacity());
                dcColors.clear();

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -