spatialtransformer.java

来自「java 3d game jme 工程开发源代码」· Java 代码 · 共 900 行 · 第 1/3 页

JAVA
900
字号
/*
 * 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.jme.animation;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.logging.Logger;

import com.jme.math.Quaternion;
import com.jme.math.TransformQuaternion;
import com.jme.math.Vector3f;
import com.jme.scene.Controller;
import com.jme.scene.Spatial;
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;

/**
 * Started Date: Jul 9, 2004 <br>
 * <br>
 * 
 * This class animates spatials by interpolating between various
 * transformations. The user defines objects to be transformed and what
 * rotation/translation/scale to give each object at various points in time. The
 * user must call interpolateMissing() before using the controller in order to
 * interpolate unspecified translation/rotation/scale.
 * 
 * 
 * @author Jack Lindamood
 * @author Philip Wainwright (bugfixes)
 */
public class SpatialTransformer extends Controller {
    private static final Logger logger = Logger.getLogger(SpatialTransformer.class.getName());

    private static final long serialVersionUID = 1L;

    /** Number of objects this transformer changes. */
    private int numObjects;

    /** Refrences to the objects that will be changed. */
    public Spatial[] toChange;

    /** Used internally by update specifying how to change each object. */
    private TransformQuaternion[] pivots;

    /**
     * parentIndexes[i] states that toChange[i]'s parent is
     * toChange[parentIndex[i]].
     */
    public int[] parentIndexes;

    /** Interpolated array of keyframe states */
    public ArrayList<PointInTime> keyframes;

    /** Current time in the animation */
    private float curTime;

    /** Time previous to curTime */
    private transient PointInTime beginPointTime;

    /** Time after curTime */
    private transient PointInTime endPointTime;

    /** Used internally in update to flag that a pivot has been updated */
    private boolean[] haveChanged;

    private float delta;

    private final static Vector3f unSyncbeginPos = new Vector3f();

    private final static Vector3f unSyncendPos = new Vector3f();

    private final static Quaternion unSyncbeginRot = new Quaternion();

    private final static Quaternion unSyncendRot = new Quaternion();

    public SpatialTransformer() {}
    
    /**
     * Constructs a new SpatialTransformer that will operate on
     * <code>numObjects</code> Spatials
     * 
     * @param numObjects
     *            The number of spatials to change
     */
    public SpatialTransformer(int numObjects) {
        this.numObjects = numObjects;
        toChange = new Spatial[numObjects];
        pivots = new TransformQuaternion[numObjects];
        parentIndexes = new int[numObjects];
        haveChanged = new boolean[numObjects];
        Arrays.fill(parentIndexes, -1);
        for (int i = 0; i < numObjects; i++)
            pivots[i] = new TransformQuaternion();
        keyframes = new ArrayList<PointInTime>();
    }

    public void update(float time) {
        curTime += time * getSpeed();
        setBeginAndEnd();
        Arrays.fill(haveChanged, false);
        delta = endPointTime.time - beginPointTime.time;
        if (delta != 0f) delta = (curTime - beginPointTime.time) / delta;
        for (int i = 0; i < numObjects; i++) {
            updatePivot(i);
            pivots[i].applyToSpatial(toChange[i]);
        }
    }

    /**
     * Called by update, and itself recursivly. Will, when completed, change
     * toChange[objIndex] by pivots[objIndex]
     * 
     * @param objIndex
     *            The index to update.
     */
    private void updatePivot(int objIndex) {
        if (haveChanged[objIndex])
            return;

        haveChanged[objIndex] = true;
        int parentIndex = parentIndexes[objIndex];
        if (parentIndex != -1) {
            updatePivot(parentIndex);
        }
        pivots[objIndex].interpolateTransforms(beginPointTime.look[objIndex],
                endPointTime.look[objIndex], delta);
        if (parentIndex != -1)
                pivots[objIndex].combineWithParent(pivots[parentIndex]);
    }

    /**
     * overridden by SpatialTransformer to always set a time inside the first
     * and the last keyframe's time in the animation.
     * @author Kai Rabien (hevee)
     */
    public void setMinTime(float minTime) {
        if(keyframes != null &&  keyframes.size() > 0){
            float firstFrame = keyframes.get(0).time;
            float lastFrame = keyframes.get(keyframes.size() - 1).time;
            if(minTime < firstFrame) minTime = firstFrame;
            if(minTime > lastFrame) minTime = lastFrame;
        }

        curTime = minTime;
        super.setMinTime(minTime);
    }

    /**
     * overridden by SpatialTransformer to always set a time inside the first
     * and the last keyframe's time in the animation
     * @author Kai Rabien (hevee)
     */
    public void setMaxTime(float maxTime) {
        if(keyframes != null &&  keyframes.size() > 0){
            float firstFrame = keyframes.get(0).time;
            float lastFrame = keyframes.get(keyframes.size() - 1).time;
            if(maxTime < firstFrame) maxTime = firstFrame;
            if(maxTime > lastFrame) maxTime = lastFrame;
        }
        super.setMaxTime(maxTime);
    }

    /**
     * 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 (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
            curTime = newBeginTime;
            if (newBeginTime == newEndTime) {
                update(0);
                setActive(false);
            }
        } else { // Moving backwards
            curTime = newEndTime;
        }
    }

    /**
     * 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;}

    /**
     * Called in update for calculating the correct beginPointTime and
     * endPointTime, and changing curTime if neccessary.
     * @author Kai Rabien (hevee)
     */
    private void setBeginAndEnd() {
        float minTime = getMinTime();
        float maxTime = getMaxTime();

        if(getSpeed() > 0){
            if(curTime >= maxTime){
                if(getRepeatType() == RT_WRAP){
                    int[] is = findIndicesBeforeAfter(minTime);
                    int beginIndex = is[0];
                    int endIndex = is[1];
                    beginPointTime = keyframes.get(beginIndex);
                    endPointTime = keyframes.get(endIndex);
                    float overshoot = curTime - maxTime;
                    curTime = minTime + overshoot;
                } else if (getRepeatType() == RT_CLAMP){
                    int[] is = findIndicesBeforeAfter(maxTime);
                    int beginIndex = is[1];
                    beginPointTime = keyframes.get(beginIndex);
                    endPointTime = beginPointTime;
                    curTime = maxTime;
                } else if(getRepeatType() == RT_CYCLE){
                    int[] is = findIndicesBeforeAfter(maxTime);
                    int beginIndex = is[0];
                    int endIndex = is[1];
                    beginPointTime = keyframes.get(beginIndex);
                    endPointTime = keyframes.get(endIndex);
                    float overshoot = curTime - maxTime;
                    curTime = maxTime - overshoot;
                    setSpeed(- getSpeed());
                }
            } else if(curTime <= minTime){
                int[] is = findIndicesBeforeAfter(minTime);
                int beginIndex = is[0];
                int endIndex = is[1];
                beginPointTime = keyframes.get(beginIndex);
                endPointTime = keyframes.get(endIndex);
                curTime = minTime;
            } else{//curTime is inside minTime and maxTime
                int[] is = findIndicesBeforeAfter(curTime);
                int beginIndex = is[0];
                int endIndex = is[1];
                beginPointTime = keyframes.get(beginIndex);
                endPointTime = keyframes.get(endIndex);

⌨️ 快捷键说明

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