Zwischenstand commited
This commit is contained in:
389
com/jme3/anim/TransformTrack.java
Normal file
389
com/jme3/anim/TransformTrack.java
Normal file
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2023 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.interpolator.FrameInterpolator;
|
||||
import com.jme3.anim.util.HasLocalTransform;
|
||||
import com.jme3.animation.CompactQuaternionArray;
|
||||
import com.jme3.animation.CompactVector3Array;
|
||||
import com.jme3.export.*;
|
||||
import com.jme3.math.*;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An AnimTrack that transforms a Joint or Spatial
|
||||
* using a list of transforms and times for keyframes.
|
||||
*
|
||||
* @author Rémy Bouquet
|
||||
*/
|
||||
public class TransformTrack implements AnimTrack<Transform> {
|
||||
|
||||
private double length;
|
||||
/**
|
||||
* The interpolator to use, or null to always use the default interpolator
|
||||
* of the current thread.
|
||||
*/
|
||||
private FrameInterpolator interpolator = null;
|
||||
private HasLocalTransform target;
|
||||
|
||||
/**
|
||||
* Transforms and times for keyframes.
|
||||
*/
|
||||
private CompactVector3Array translations;
|
||||
private CompactQuaternionArray rotations;
|
||||
private CompactVector3Array scales;
|
||||
private float[] times;
|
||||
|
||||
/**
|
||||
* Serialization-only. Do not use.
|
||||
*/
|
||||
protected TransformTrack() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a transform track for the given target.
|
||||
*
|
||||
* @param target the target Joint or Spatial of the new track
|
||||
* @param times the time for each keyframe, or null for none
|
||||
* @param translations the translation of the target for each keyframe
|
||||
* (same length as times) or null for no translation
|
||||
* @param rotations the rotation of the target for each keyframe
|
||||
* (same length as times) or null for no rotation
|
||||
* @param scales the scale of the target for each keyframe
|
||||
* (same length as times) or null for no scaling
|
||||
*/
|
||||
public TransformTrack(HasLocalTransform target, float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) {
|
||||
this.target = target;
|
||||
this.setKeyframes(times, translations, rotations, scales);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the rotations.
|
||||
*
|
||||
* @return a new array, or null if no rotations
|
||||
*/
|
||||
public Quaternion[] getRotations() {
|
||||
return rotations == null ? null : rotations.toObjectArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the scales.
|
||||
*
|
||||
* @return a new array, or null if no scales
|
||||
*/
|
||||
public Vector3f[] getScales() {
|
||||
return scales == null ? null : scales.toObjectArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives access to the keyframe times.
|
||||
*
|
||||
* @return the pre-existing array
|
||||
*/
|
||||
public float[] getTimes() {
|
||||
return times;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the translations.
|
||||
*
|
||||
* @return a new array, or null if no translations
|
||||
*/
|
||||
public Vector3f[] getTranslations() {
|
||||
return translations == null ? null : translations.toObjectArray();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the keyframe times.
|
||||
*
|
||||
* @param times the desired keyframe times (alias created, not null, not empty)
|
||||
*/
|
||||
public void setTimes(float[] times) {
|
||||
if (times == null || times.length == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"No keyframe times were provided.");
|
||||
}
|
||||
this.times = times;
|
||||
length = times[times.length - 1] - times[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the translations.
|
||||
*
|
||||
* @param translations the desired translation of the target for each
|
||||
* keyframe (not null, same length as "times")
|
||||
*/
|
||||
public void setKeyframesTranslation(Vector3f[] translations) {
|
||||
if (times == null) {
|
||||
throw new IllegalStateException(
|
||||
"TransformTrack lacks keyframe times. "
|
||||
+ "Please invoke setTimes() first.");
|
||||
}
|
||||
if (translations == null || translations.length == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"No translations were provided.");
|
||||
}
|
||||
this.translations = new CompactVector3Array();
|
||||
this.translations.add(translations);
|
||||
this.translations.freeze();
|
||||
|
||||
assert times != null && times.length == translations.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scales.
|
||||
*
|
||||
* @param scales the desired scale of the target for each keyframe (not
|
||||
* null, same length as "times")
|
||||
*/
|
||||
public void setKeyframesScale(Vector3f[] scales) {
|
||||
if (times == null) {
|
||||
throw new IllegalStateException(
|
||||
"TransformTrack lacks keyframe times. "
|
||||
+ "Please invoke setTimes() first.");
|
||||
}
|
||||
if (scales == null || scales.length == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"No scale vectors were provided.");
|
||||
}
|
||||
this.scales = new CompactVector3Array();
|
||||
this.scales.add(scales);
|
||||
this.scales.freeze();
|
||||
|
||||
assert times != null && times.length == scales.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rotations.
|
||||
*
|
||||
* @param rotations the desired rotation of the target for each keyframe
|
||||
* (not null, same length as "times")
|
||||
*/
|
||||
public void setKeyframesRotation(Quaternion[] rotations) {
|
||||
if (times == null) {
|
||||
throw new IllegalStateException(
|
||||
"TransformTrack lacks keyframe times. "
|
||||
+ "Please invoke setTimes() first.");
|
||||
}
|
||||
if (rotations == null || rotations.length == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"No rotations were provided.");
|
||||
}
|
||||
this.rotations = new CompactQuaternionArray();
|
||||
this.rotations.add(rotations);
|
||||
this.rotations.freeze();
|
||||
|
||||
assert times != null && times.length == rotations.length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the translations, rotations, and/or scales.
|
||||
*
|
||||
* @param times the desired time for each keyframe,
|
||||
* or null to leave the times unchanged
|
||||
* @param translations the desired translation of the target for each
|
||||
* keyframe (same length as times)
|
||||
* or null to leave the translations unchanged
|
||||
* @param rotations the desired rotation of the target for each keyframe
|
||||
* (same length as times)
|
||||
* or null to leave the rotations unchanged
|
||||
* @param scales the desired scale of the target for each keyframe
|
||||
* (same length as times)
|
||||
* or null to leave the scales unchanged
|
||||
*/
|
||||
public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) {
|
||||
if (times != null) {
|
||||
setTimes(times);
|
||||
}
|
||||
if (translations != null) {
|
||||
setKeyframesTranslation(translations);
|
||||
}
|
||||
if (rotations != null) {
|
||||
setKeyframesRotation(rotations);
|
||||
}
|
||||
if (scales != null) {
|
||||
setKeyframesScale(scales);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDataAtTime(double t, Transform transform) {
|
||||
float time = (float) t;
|
||||
|
||||
int lastFrame = times.length - 1;
|
||||
if (time < 0 || lastFrame == 0) {
|
||||
if (translations != null) {
|
||||
translations.get(0, transform.getTranslation());
|
||||
}
|
||||
if (rotations != null) {
|
||||
rotations.get(0, transform.getRotation());
|
||||
}
|
||||
if (scales != null) {
|
||||
scales.get(0, transform.getScale());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int startFrame = 0;
|
||||
int endFrame = 1;
|
||||
float blend = 0;
|
||||
if (time >= times[lastFrame]) {
|
||||
// extrapolate beyond the final keyframe of the animation
|
||||
startFrame = lastFrame;
|
||||
|
||||
float inferredInterval = times[lastFrame] - times[lastFrame - 1];
|
||||
if (inferredInterval > 0f) {
|
||||
blend = (time - times[startFrame]) / inferredInterval;
|
||||
}
|
||||
|
||||
} else {
|
||||
// use lastFrame so we never overflow the array
|
||||
int i;
|
||||
for (i = 0; i < lastFrame && times[i] < time; i++) {
|
||||
startFrame = i;
|
||||
endFrame = i + 1;
|
||||
}
|
||||
blend = (time - times[startFrame])
|
||||
/ (times[endFrame] - times[startFrame]);
|
||||
}
|
||||
|
||||
FrameInterpolator fi = (interpolator == null)
|
||||
? FrameInterpolator.getThreadDefault() : interpolator;
|
||||
Transform interpolated = fi.interpolate(
|
||||
blend, startFrame, translations, rotations, scales, times);
|
||||
|
||||
if (translations != null) {
|
||||
transform.setTranslation(interpolated.getTranslation());
|
||||
}
|
||||
if (rotations != null) {
|
||||
transform.setRotation(interpolated.getRotation());
|
||||
}
|
||||
if (scales != null) {
|
||||
transform.setScale(interpolated.getScale());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the FrameInterpolator.
|
||||
*
|
||||
* @return the pre-existing instance or null
|
||||
*/
|
||||
public FrameInterpolator getFrameInterpolator() {
|
||||
return interpolator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the frame interpolator.
|
||||
*
|
||||
* @param interpolator the interpolator to use (alias created)
|
||||
*/
|
||||
public void setFrameInterpolator(FrameInterpolator interpolator) {
|
||||
this.interpolator = interpolator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives access to the target, which might be a Joint or a Spatial.
|
||||
*
|
||||
* @return the pre-existing instance
|
||||
*/
|
||||
public HasLocalTransform getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the target, which might be a Joint or a Spatial.
|
||||
*
|
||||
* @param target the target to use (alias created)
|
||||
*/
|
||||
public void setTarget(HasLocalTransform target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this track to the specified exporter, for example when
|
||||
* saving to a J3O file.
|
||||
*
|
||||
* @param ex the exporter to write to (not null)
|
||||
* @throws IOException from the exporter
|
||||
*/
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.write(translations, "translations", null);
|
||||
oc.write(rotations, "rotations", null);
|
||||
oc.write(times, "times", null);
|
||||
oc.write(scales, "scales", null);
|
||||
oc.write(target, "target", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* De-serializes this track from the specified importer, for example when
|
||||
* loading from a J3O file.
|
||||
*
|
||||
* @param im the importer to read from (not null)
|
||||
* @throws IOException from the importer
|
||||
*/
|
||||
@Override
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
InputCapsule ic = im.getCapsule(this);
|
||||
translations = (CompactVector3Array) ic.readSavable("translations", null);
|
||||
rotations = (CompactQuaternionArray) ic.readSavable("rotations", null);
|
||||
times = ic.readFloatArray("times", null);
|
||||
scales = (CompactVector3Array) ic.readSavable("scales", null);
|
||||
target = (HasLocalTransform) ic.readSavable("target", null);
|
||||
setTimes(times);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransformTrack jmeClone() {
|
||||
try {
|
||||
TransformTrack clone = (TransformTrack) super.clone();
|
||||
return clone;
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cloneFields(Cloner cloner, Object original) {
|
||||
this.target = cloner.clone(target);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user