📄 keyframecontroller.java
字号:
/*
* 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 + -