parent
4904d0235e
commit
c3cb4ef97f
@ -0,0 +1,113 @@ |
|||||||
|
package com.jme3.anim; |
||||||
|
|
||||||
|
import com.jme3.anim.tween.Tween; |
||||||
|
import com.jme3.export.*; |
||||||
|
import com.jme3.util.SafeArrayList; |
||||||
|
import com.jme3.util.clone.Cloner; |
||||||
|
import com.jme3.util.clone.JmeCloneable; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Nehon on 20/12/2017. |
||||||
|
*/ |
||||||
|
public class AnimClip implements Tween, JmeCloneable, Savable { |
||||||
|
|
||||||
|
private String name; |
||||||
|
private double length; |
||||||
|
|
||||||
|
private SafeArrayList<Tween> tracks = new SafeArrayList<>(Tween.class); |
||||||
|
|
||||||
|
public AnimClip() { |
||||||
|
} |
||||||
|
|
||||||
|
public AnimClip(String name) { |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTracks(Tween[] tracks) { |
||||||
|
for (Tween track : tracks) { |
||||||
|
addTrack(track); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void addTrack(Tween track) { |
||||||
|
tracks.add(track); |
||||||
|
if (track.getLength() > length) { |
||||||
|
length = track.getLength(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void removeTrack(Tween track) { |
||||||
|
if (tracks.remove(track)) { |
||||||
|
length = 0; |
||||||
|
for (Tween t : tracks.getArray()) { |
||||||
|
if (t.getLength() > length) { |
||||||
|
length = t.getLength(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public double getLength() { |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean interpolate(double t) { |
||||||
|
// Sanity check the inputs
|
||||||
|
if (t < 0) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
for (Tween track : tracks.getArray()) { |
||||||
|
track.interpolate(t); |
||||||
|
} |
||||||
|
return t <= length; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object jmeClone() { |
||||||
|
try { |
||||||
|
return super.clone(); |
||||||
|
} catch (CloneNotSupportedException e) { |
||||||
|
throw new RuntimeException("Error cloning", e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void cloneFields(Cloner cloner, Object original) { |
||||||
|
SafeArrayList<Tween> newTracks = new SafeArrayList<>(Tween.class); |
||||||
|
for (Tween track : tracks) { |
||||||
|
newTracks.add(cloner.clone(track)); |
||||||
|
} |
||||||
|
this.tracks = newTracks; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(JmeExporter ex) throws IOException { |
||||||
|
OutputCapsule oc = ex.getCapsule(this); |
||||||
|
oc.write(name, "name", null); |
||||||
|
oc.write(tracks.getArray(), "tracks", null); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void read(JmeImporter im) throws IOException { |
||||||
|
InputCapsule ic = im.getCapsule(this); |
||||||
|
name = ic.readString("name", null); |
||||||
|
Savable[] arr = ic.readSavableArray("tracks", null); |
||||||
|
if (arr != null) { |
||||||
|
tracks = new SafeArrayList<>(Tween.class); |
||||||
|
for (Savable savable : arr) { |
||||||
|
tracks.add((Tween) savable); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
package com.jme3.anim; |
||||||
|
|
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.renderer.ViewPort; |
||||||
|
import com.jme3.scene.control.AbstractControl; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Nehon on 20/12/2017. |
||||||
|
*/ |
||||||
|
public class AnimComposer extends AbstractControl { |
||||||
|
|
||||||
|
private Map<String, AnimClip> animClipMap = new HashMap<>(); |
||||||
|
|
||||||
|
private AnimClip currentAnimClip; |
||||||
|
private float time; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 |
||||||
|
* <code>AnimControl</code>. |
||||||
|
* |
||||||
|
* @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()); |
||||||
|
} |
||||||
|
|
||||||
|
public void setCurrentAnimClip(String name) { |
||||||
|
currentAnimClip = animClipMap.get(name); |
||||||
|
time = 0; |
||||||
|
if (currentAnimClip == null) { |
||||||
|
throw new IllegalArgumentException("Unknown clip " + name); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void reset() { |
||||||
|
currentAnimClip = null; |
||||||
|
time = 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void controlUpdate(float tpf) { |
||||||
|
if (currentAnimClip != null) { |
||||||
|
time += tpf; |
||||||
|
boolean running = currentAnimClip.interpolate(time); |
||||||
|
if (!running) { |
||||||
|
time -= currentAnimClip.getLength(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void controlRender(RenderManager rm, ViewPort vp) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,113 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2012 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.export.*; |
||||||
|
import com.jme3.math.*; |
||||||
|
import com.jme3.util.clone.Cloner; |
||||||
|
import com.jme3.util.clone.JmeCloneable; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Contains a list of transforms and times for each keyframe. |
||||||
|
* |
||||||
|
* @author Rémy Bouquet |
||||||
|
*/ |
||||||
|
public final class JointTrack extends TransformTrack implements JmeCloneable, Savable { |
||||||
|
|
||||||
|
private Joint target; |
||||||
|
|
||||||
|
/** |
||||||
|
* Serialization-only. Do not use. |
||||||
|
*/ |
||||||
|
public JointTrack() { |
||||||
|
super(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a bone track for the given bone index |
||||||
|
* |
||||||
|
* @param target The Joint target of this track |
||||||
|
* @param times a float array with the time of each frame |
||||||
|
* @param translations the translation of the bone for each frame |
||||||
|
* @param rotations the rotation of the bone for each frame |
||||||
|
* @param scales the scale of the bone for each frame |
||||||
|
*/ |
||||||
|
public JointTrack(Joint target, float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) { |
||||||
|
super(times, translations, rotations, scales); |
||||||
|
this.target = target; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean interpolate(double t) { |
||||||
|
setDefaultTransform(target.getLocalTransform()); |
||||||
|
boolean running = super.interpolate(t); |
||||||
|
Transform transform = getInterpolatedTransform(); |
||||||
|
target.setLocalTransform(transform); |
||||||
|
return running; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTarget(Joint target) { |
||||||
|
this.target = target; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object jmeClone() { |
||||||
|
try { |
||||||
|
return super.clone(); |
||||||
|
} catch (CloneNotSupportedException e) { |
||||||
|
throw new RuntimeException("Error cloning", e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void cloneFields(Cloner cloner, Object original) { |
||||||
|
super.cloneFields(cloner, original); |
||||||
|
this.target = cloner.clone(target); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(JmeExporter ex) throws IOException { |
||||||
|
super.write(ex); |
||||||
|
OutputCapsule oc = ex.getCapsule(this); |
||||||
|
oc.write(target, "target", null); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void read(JmeImporter im) throws IOException { |
||||||
|
super.read(im); |
||||||
|
InputCapsule ic = im.getCapsule(this); |
||||||
|
target = (Joint) ic.readSavable("target", null); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,344 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2012 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.tween.Tween; |
||||||
|
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 com.jme3.util.clone.JmeCloneable; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Contains a list of transforms and times for each keyframe. |
||||||
|
* |
||||||
|
* @author Rémy Bouquet |
||||||
|
*/ |
||||||
|
public abstract class TransformTrack implements Tween, JmeCloneable, Savable { |
||||||
|
|
||||||
|
private double length; |
||||||
|
|
||||||
|
/** |
||||||
|
* Transforms and times for track. |
||||||
|
*/ |
||||||
|
private CompactVector3Array translations; |
||||||
|
private CompactQuaternionArray rotations; |
||||||
|
private CompactVector3Array scales; |
||||||
|
private Transform transform = new Transform(); |
||||||
|
private Transform defaultTransform = new Transform(); |
||||||
|
private FrameInterpolator interpolator = FrameInterpolator.DEFAULT; |
||||||
|
private float[] times; |
||||||
|
|
||||||
|
/** |
||||||
|
* Serialization-only. Do not use. |
||||||
|
*/ |
||||||
|
public TransformTrack() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a transform track for the given bone index |
||||||
|
* |
||||||
|
* @param times a float array with the time of each frame |
||||||
|
* @param translations the translation of the bone for each frame |
||||||
|
* @param rotations the rotation of the bone for each frame |
||||||
|
* @param scales the scale of the bone for each frame |
||||||
|
*/ |
||||||
|
public TransformTrack(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) { |
||||||
|
this.setKeyframes(times, translations, rotations, scales); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a bone track for the given bone index |
||||||
|
* |
||||||
|
* @param targetJointIndex the bone's index |
||||||
|
*/ |
||||||
|
public TransformTrack(int targetJointIndex) { |
||||||
|
this(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* return the array of rotations of this track |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public Quaternion[] getRotations() { |
||||||
|
return rotations.toObjectArray(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the array of scales for this track |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public Vector3f[] getScales() { |
||||||
|
return scales == null ? null : scales.toObjectArray(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the arrays of time for this track |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public float[] getTimes() { |
||||||
|
return times; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* returns the array of translations of this track |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public Vector3f[] getTranslations() { |
||||||
|
return translations.toObjectArray(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the keyframes times for this Joint track |
||||||
|
* |
||||||
|
* @param times the keyframes times |
||||||
|
*/ |
||||||
|
public void setTimes(float[] times) { |
||||||
|
if (times.length == 0) { |
||||||
|
throw new RuntimeException("TransformTrack with no keyframes!"); |
||||||
|
} |
||||||
|
this.times = times; |
||||||
|
length = times[times.length - 1] - times[0]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the translations for this joint track |
||||||
|
* |
||||||
|
* @param translations the translation of the bone for each frame |
||||||
|
*/ |
||||||
|
public void setKeyframesTranslation(Vector3f[] translations) { |
||||||
|
if (times == null) { |
||||||
|
throw new RuntimeException("TransformTrack doesn't have any time for key frames, please call setTimes first"); |
||||||
|
} |
||||||
|
if (translations.length == 0) { |
||||||
|
throw new RuntimeException("TransformTrack with no translation keyframes!"); |
||||||
|
} |
||||||
|
this.translations = new CompactVector3Array(); |
||||||
|
this.translations.add(translations); |
||||||
|
this.translations.freeze(); |
||||||
|
|
||||||
|
assert times != null && times.length == translations.length; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the scales for this joint track |
||||||
|
* |
||||||
|
* @param scales the scales of the bone for each frame |
||||||
|
*/ |
||||||
|
public void setKeyframesScale(Vector3f[] scales) { |
||||||
|
if (times == null) { |
||||||
|
throw new RuntimeException("TransformTrack doesn't have any time for key frames, please call setTimes first"); |
||||||
|
} |
||||||
|
if (scales.length == 0) { |
||||||
|
throw new RuntimeException("TransformTrack with no scale keyframes!"); |
||||||
|
} |
||||||
|
this.scales = new CompactVector3Array(); |
||||||
|
this.scales.add(scales); |
||||||
|
this.scales.freeze(); |
||||||
|
|
||||||
|
assert times != null && times.length == scales.length; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the rotations for this joint track |
||||||
|
* |
||||||
|
* @param rotations the rotations of the bone for each frame |
||||||
|
*/ |
||||||
|
public void setKeyframesRotation(Quaternion[] rotations) { |
||||||
|
if (times == null) { |
||||||
|
throw new RuntimeException("TransformTrack doesn't have any time for key frames, please call setTimes first"); |
||||||
|
} |
||||||
|
if (rotations.length == 0) { |
||||||
|
throw new RuntimeException("TransformTrack with no rotation keyframes!"); |
||||||
|
} |
||||||
|
this.rotations = new CompactQuaternionArray(); |
||||||
|
this.rotations.add(rotations); |
||||||
|
this.rotations.freeze(); |
||||||
|
|
||||||
|
assert times != null && times.length == rotations.length; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set the translations, rotations and scales for this bone track |
||||||
|
* |
||||||
|
* @param times a float array with the time of each frame |
||||||
|
* @param translations the translation of the bone for each frame |
||||||
|
* @param rotations the rotation of the bone for each frame |
||||||
|
* @param scales the scale of the bone for each frame |
||||||
|
*/ |
||||||
|
public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) { |
||||||
|
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 boolean interpolate(double t) { |
||||||
|
float time = (float) t; |
||||||
|
|
||||||
|
transform.set(defaultTransform); |
||||||
|
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 true; |
||||||
|
} |
||||||
|
|
||||||
|
int startFrame = 0; |
||||||
|
int endFrame = 1; |
||||||
|
float blend = 0; |
||||||
|
if (time >= times[lastFrame]) { |
||||||
|
startFrame = lastFrame; |
||||||
|
|
||||||
|
time = time - times[startFrame] + times[startFrame - 1]; |
||||||
|
blend = (time - times[startFrame - 1]) |
||||||
|
/ (times[startFrame] - times[startFrame - 1]); |
||||||
|
|
||||||
|
} 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]); |
||||||
|
} |
||||||
|
|
||||||
|
Transform interpolated = interpolator.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()); |
||||||
|
} |
||||||
|
|
||||||
|
return time < length; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public Transform getInterpolatedTransform() { |
||||||
|
return transform; |
||||||
|
} |
||||||
|
|
||||||
|
public void setDefaultTransform(Transform transforms) { |
||||||
|
defaultTransform.set(transforms); |
||||||
|
} |
||||||
|
|
||||||
|
public void setFrameInterpolator(FrameInterpolator interpolator) { |
||||||
|
this.interpolator = interpolator; |
||||||
|
} |
||||||
|
|
||||||
|
@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); |
||||||
|
} |
||||||
|
|
||||||
|
@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); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object jmeClone() { |
||||||
|
try { |
||||||
|
return super.clone(); |
||||||
|
} catch (CloneNotSupportedException e) { |
||||||
|
throw new RuntimeException("Error cloning", e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void cloneFields(Cloner cloner, Object original) { |
||||||
|
int tablesLength = times.length; |
||||||
|
|
||||||
|
times = this.times.clone(); |
||||||
|
Vector3f[] sourceTranslations = this.getTranslations(); |
||||||
|
Quaternion[] sourceRotations = this.getRotations(); |
||||||
|
Vector3f[] sourceScales = this.getScales(); |
||||||
|
|
||||||
|
Vector3f[] translations = new Vector3f[tablesLength]; |
||||||
|
Quaternion[] rotations = new Quaternion[tablesLength]; |
||||||
|
Vector3f[] scales = new Vector3f[tablesLength]; |
||||||
|
for (int i = 0; i < tablesLength; ++i) { |
||||||
|
translations[i] = sourceTranslations[i].clone(); |
||||||
|
rotations[i] = sourceRotations[i].clone(); |
||||||
|
scales[i] = sourceScales != null ? sourceScales[i].clone() : new Vector3f(1.0f, 1.0f, 1.0f); |
||||||
|
} |
||||||
|
|
||||||
|
setKeyframesTranslation(translations); |
||||||
|
setKeyframesScale(scales); |
||||||
|
setKeyframesRotation(rotations); |
||||||
|
setFrameInterpolator(this.interpolator); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
package com.jme3.anim.interpolator; |
||||||
|
|
||||||
|
import static com.jme3.anim.interpolator.FrameInterpolator.TrackDataReader; |
||||||
|
import static com.jme3.anim.interpolator.FrameInterpolator.TrackTimeReader; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by nehon on 15/04/17. |
||||||
|
*/ |
||||||
|
public abstract class AnimInterpolator<T> { |
||||||
|
|
||||||
|
public abstract T interpolate(float t, int currentIndex, TrackDataReader<T> data, TrackTimeReader times, T store); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,151 @@ |
|||||||
|
package com.jme3.anim.interpolator; |
||||||
|
|
||||||
|
import com.jme3.math.*; |
||||||
|
|
||||||
|
import static com.jme3.anim.interpolator.FrameInterpolator.TrackDataReader; |
||||||
|
import static com.jme3.anim.interpolator.FrameInterpolator.TrackTimeReader; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by nehon on 15/04/17. |
||||||
|
*/ |
||||||
|
public class AnimInterpolators { |
||||||
|
|
||||||
|
//Rotation interpolators
|
||||||
|
|
||||||
|
public static final AnimInterpolator<Quaternion> NLerp = new AnimInterpolator<Quaternion>() { |
||||||
|
private Quaternion next = new Quaternion(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public Quaternion interpolate(float t, int currentIndex, TrackDataReader<Quaternion> data, TrackTimeReader times, Quaternion store) { |
||||||
|
data.getEntryClamp(currentIndex, store); |
||||||
|
data.getEntryClamp(currentIndex + 1, next); |
||||||
|
store.nlerp(next, t); |
||||||
|
return store; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public static final AnimInterpolator<Quaternion> SLerp = new AnimInterpolator<Quaternion>() { |
||||||
|
private Quaternion next = new Quaternion(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public Quaternion interpolate(float t, int currentIndex, TrackDataReader<Quaternion> data, TrackTimeReader times, Quaternion store) { |
||||||
|
data.getEntryClamp(currentIndex, store); |
||||||
|
data.getEntryClamp(currentIndex + 1, next); |
||||||
|
//MathUtils.slerpNoInvert(store, next, t, store);
|
||||||
|
MathUtils.slerp(store, next, t, store); |
||||||
|
return store; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public static final AnimInterpolator<Quaternion> SQuad = new AnimInterpolator<Quaternion>() { |
||||||
|
private Quaternion a = new Quaternion(); |
||||||
|
private Quaternion b = new Quaternion(); |
||||||
|
|
||||||
|
private Quaternion q0 = new Quaternion(); |
||||||
|
private Quaternion q1 = new Quaternion(); |
||||||
|
private Quaternion q2 = new Quaternion(); |
||||||
|
private Quaternion q3 = new Quaternion(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public Quaternion interpolate(float t, int currentIndex, TrackDataReader<Quaternion> data, TrackTimeReader times, Quaternion store) { |
||||||
|
data.getEntryModSkip(currentIndex - 1, q0); |
||||||
|
data.getEntryModSkip(currentIndex, q1); |
||||||
|
data.getEntryModSkip(currentIndex + 1, q2); |
||||||
|
data.getEntryModSkip(currentIndex + 2, q3); |
||||||
|
MathUtils.squad(q0, q1, q2, q3, a, b, t, store); |
||||||
|
return store; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
//Position / Scale interpolators
|
||||||
|
|
||||||
|
public static final AnimInterpolator<Vector3f> LinearVec3f = new AnimInterpolator<Vector3f>() { |
||||||
|
private Vector3f next = new Vector3f(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public Vector3f interpolate(float t, int currentIndex, TrackDataReader<Vector3f> data, TrackTimeReader times, Vector3f store) { |
||||||
|
data.getEntryClamp(currentIndex, store); |
||||||
|
data.getEntryClamp(currentIndex + 1, next); |
||||||
|
store.interpolateLocal(next, t); |
||||||
|
return store; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* CatmullRom interpolation |
||||||
|
*/ |
||||||
|
public static final CatmullRomInterpolator CatmullRom = new CatmullRomInterpolator(); |
||||||
|
|
||||||
|
public static class CatmullRomInterpolator extends AnimInterpolator<Vector3f> { |
||||||
|
private Vector3f p0 = new Vector3f(); |
||||||
|
private Vector3f p1 = new Vector3f(); |
||||||
|
private Vector3f p2 = new Vector3f(); |
||||||
|
private Vector3f p3 = new Vector3f(); |
||||||
|
private float tension = 0.7f; |
||||||
|
|
||||||
|
public CatmullRomInterpolator(float tension) { |
||||||
|
this.tension = tension; |
||||||
|
} |
||||||
|
|
||||||
|
public CatmullRomInterpolator() { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Vector3f interpolate(float t, int currentIndex, TrackDataReader<Vector3f> data, TrackTimeReader times, Vector3f store) { |
||||||
|
data.getEntryModSkip(currentIndex - 1, p0); |
||||||
|
data.getEntryModSkip(currentIndex, p1); |
||||||
|
data.getEntryModSkip(currentIndex + 1, p2); |
||||||
|
data.getEntryModSkip(currentIndex + 2, p3); |
||||||
|
|
||||||
|
FastMath.interpolateCatmullRom(t, tension, p0, p1, p2, p3, store); |
||||||
|
return store; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//Time Interpolators
|
||||||
|
|
||||||
|
public static class TimeInterpolator extends AnimInterpolator<Float> { |
||||||
|
private EaseFunction ease; |
||||||
|
|
||||||
|
public TimeInterpolator(EaseFunction ease) { |
||||||
|
this.ease = ease; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Float interpolate(float t, int currentIndex, TrackDataReader<Float> data, TrackTimeReader times, Float store) { |
||||||
|
return ease.apply(t); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//in
|
||||||
|
public static final TimeInterpolator easeInQuad = new TimeInterpolator(Easing.inQuad); |
||||||
|
public static final TimeInterpolator easeInCubic = new TimeInterpolator(Easing.inCubic); |
||||||
|
public static final TimeInterpolator easeInQuart = new TimeInterpolator(Easing.inQuart); |
||||||
|
public static final TimeInterpolator easeInQuint = new TimeInterpolator(Easing.inQuint); |
||||||
|
public static final TimeInterpolator easeInBounce = new TimeInterpolator(Easing.inBounce); |
||||||
|
public static final TimeInterpolator easeInElastic = new TimeInterpolator(Easing.inElastic); |
||||||
|
|
||||||
|
//out
|
||||||
|
public static final TimeInterpolator easeOutQuad = new TimeInterpolator(Easing.outQuad); |
||||||
|
public static final TimeInterpolator easeOutCubic = new TimeInterpolator(Easing.outCubic); |
||||||
|
public static final TimeInterpolator easeOutQuart = new TimeInterpolator(Easing.outQuart); |
||||||
|
public static final TimeInterpolator easeOutQuint = new TimeInterpolator(Easing.outQuint); |
||||||
|
public static final TimeInterpolator easeOutBounce = new TimeInterpolator(Easing.outBounce); |
||||||
|
public static final TimeInterpolator easeOutElastic = new TimeInterpolator(Easing.outElastic); |
||||||
|
|
||||||
|
//inout
|
||||||
|
public static final TimeInterpolator easeInOutQuad = new TimeInterpolator(Easing.inOutQuad); |
||||||
|
public static final TimeInterpolator easeInOutCubic = new TimeInterpolator(Easing.inOutCubic); |
||||||
|
public static final TimeInterpolator easeInOutQuart = new TimeInterpolator(Easing.inOutQuart); |
||||||
|
public static final TimeInterpolator easeInOutQuint = new TimeInterpolator(Easing.inOutQuint); |
||||||
|
public static final TimeInterpolator easeInOutBounce = new TimeInterpolator(Easing.inOutBounce); |
||||||
|
public static final TimeInterpolator easeInOutElastic = new TimeInterpolator(Easing.inOutElastic); |
||||||
|
|
||||||
|
//extra
|
||||||
|
public static final TimeInterpolator smoothStep = new TimeInterpolator(Easing.smoothStep); |
||||||
|
public static final TimeInterpolator smootherStep = new TimeInterpolator(Easing.smootherStep); |
||||||
|
|
||||||
|
public static final TimeInterpolator constant = new TimeInterpolator(Easing.constant); |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,121 @@ |
|||||||
|
package com.jme3.anim.interpolator; |
||||||
|
|
||||||
|
import com.jme3.animation.*; |
||||||
|
import com.jme3.math.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by nehon on 15/04/17. |
||||||
|
*/ |
||||||
|
public class FrameInterpolator { |
||||||
|
|
||||||
|
public static final FrameInterpolator DEFAULT = new FrameInterpolator(); |
||||||
|
|
||||||
|
private AnimInterpolator<Float> timeInterpolator; |
||||||
|
private AnimInterpolator<Vector3f> translationInterpolator = AnimInterpolators.LinearVec3f; |
||||||
|
private AnimInterpolator<Quaternion> rotationInterpolator = AnimInterpolators.NLerp; |
||||||
|
private AnimInterpolator<Vector3f> scaleInterpolator = AnimInterpolators.LinearVec3f; |
||||||
|
|
||||||
|
private TrackDataReader<Vector3f> translationReader = new TrackDataReader<>(); |
||||||
|
private TrackDataReader<Quaternion> rotationReader = new TrackDataReader<>(); |
||||||
|
private TrackDataReader<Vector3f> scaleReader = new TrackDataReader<>(); |
||||||
|
private TrackTimeReader timesReader = new TrackTimeReader(); |
||||||
|
|
||||||
|
private Transform transforms = new Transform(); |
||||||
|
|
||||||
|
public Transform interpolate(float t, int currentIndex, CompactVector3Array translations, CompactQuaternionArray rotations, CompactVector3Array scales, float[] times){ |
||||||
|
timesReader.setData(times); |
||||||
|
if( timeInterpolator != null){ |
||||||
|
t = timeInterpolator.interpolate(t,currentIndex, null, timesReader, null ); |
||||||
|
} |
||||||
|
if(translations != null) { |
||||||
|
translationReader.setData(translations); |
||||||
|
translationInterpolator.interpolate(t, currentIndex, translationReader, timesReader, transforms.getTranslation()); |
||||||
|
} |
||||||
|
if(rotations != null) { |
||||||
|
rotationReader.setData(rotations); |
||||||
|
rotationInterpolator.interpolate(t, currentIndex, rotationReader, timesReader, transforms.getRotation()); |
||||||
|
} |
||||||
|
if(scales != null){ |
||||||
|
scaleReader.setData(scales); |
||||||
|
scaleInterpolator.interpolate(t, currentIndex, scaleReader, timesReader, transforms.getScale()); |
||||||
|
} |
||||||
|
return transforms; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTimeInterpolator(AnimInterpolator<Float> timeInterpolator) { |
||||||
|
this.timeInterpolator = timeInterpolator; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTranslationInterpolator(AnimInterpolator<Vector3f> translationInterpolator) { |
||||||
|
this.translationInterpolator = translationInterpolator; |
||||||
|
} |
||||||
|
|
||||||
|
public void setRotationInterpolator(AnimInterpolator<Quaternion> rotationInterpolator) { |
||||||
|
this.rotationInterpolator = rotationInterpolator; |
||||||
|
} |
||||||
|
|
||||||
|
public void setScaleInterpolator(AnimInterpolator<Vector3f> scaleInterpolator) { |
||||||
|
this.scaleInterpolator = scaleInterpolator; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static class TrackTimeReader { |
||||||
|
private float[] data; |
||||||
|
|
||||||
|
protected void setData(float[] data) { |
||||||
|
this.data = data; |
||||||
|
} |
||||||
|
|
||||||
|
public float getEntry(int index) { |
||||||
|
return data[mod(index, data.length)]; |
||||||
|
} |
||||||
|
|
||||||
|
public int getLength() { |
||||||
|
return data.length; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class TrackDataReader<T> { |
||||||
|
|
||||||
|
private CompactArray<T> data; |
||||||
|
|
||||||
|
protected void setData(CompactArray<T> data) { |
||||||
|
this.data = data; |
||||||
|
} |
||||||
|
|
||||||
|
public T getEntryMod(int index, T store) { |
||||||
|
return data.get(mod(index, data.getTotalObjectSize()), store); |
||||||
|
} |
||||||
|
|
||||||
|
public T getEntryClamp(int index, T store) { |
||||||
|
index = (int) FastMath.clamp(index, 0, data.getTotalObjectSize() - 1); |
||||||
|
return data.get(index, store); |
||||||
|
} |
||||||
|
|
||||||
|
public T getEntryModSkip(int index, T store) { |
||||||
|
int total = data.getTotalObjectSize(); |
||||||
|
if (index == -1) { |
||||||
|
index--; |
||||||
|
} else if (index >= total) { |
||||||
|
index++; |
||||||
|
} |
||||||
|
|
||||||
|
index = mod(index, total); |
||||||
|
|
||||||
|
|
||||||
|
return data.get(index, store); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Euclidean modulo (cycle on 0,n instead of -n,0; 0,n) |
||||||
|
* |
||||||
|
* @param val |
||||||
|
* @param n |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private static int mod(int val, int n) { |
||||||
|
return ((val % n) + n) % n; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,110 @@ |
|||||||
|
/* |
||||||
|
* $Id$ |
||||||
|
* |
||||||
|
* Copyright (c) 2015, Simsilica, LLC |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions |
||||||
|
* are met: |
||||||
|
* |
||||||
|
* 1. Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* 2. 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. |
||||||
|
* |
||||||
|
* 3. Neither the name of the copyright holder 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 HOLDER 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.tween; |
||||||
|
|
||||||
|
|
||||||
|
import com.jme3.export.*; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Base implementation of the Tween interface that provides |
||||||
|
* default implementations of the getLength() and interopolate() |
||||||
|
* methods that provide common tween clamping and bounds checking. |
||||||
|
* Subclasses need only override the doInterpolate() method and |
||||||
|
* the rest is handled for them. |
||||||
|
* |
||||||
|
* @author Paul Speed |
||||||
|
*/ |
||||||
|
public abstract class AbstractTween implements Tween { |
||||||
|
|
||||||
|
private double length; |
||||||
|
|
||||||
|
protected AbstractTween(double length) { |
||||||
|
this.length = length; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public double getLength() { |
||||||
|
return length; |
||||||
|
} |
||||||
|
|
||||||
|
public void setLength(double length) { |
||||||
|
this.length = length; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Default implementation clamps the time value, converts |
||||||
|
* it to 0 to 1.0 based on getLength(), and calls doInterpolate(). |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean interpolate(double t) { |
||||||
|
if (t < 0) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Scale t to be between 0 and 1 for our length
|
||||||
|
if (length == 0) { |
||||||
|
t = 1; |
||||||
|
} else { |
||||||
|
t = t / length; |
||||||
|
} |
||||||
|
|
||||||
|
boolean done = false; |
||||||
|
if (t >= 1.0) { |
||||||
|
t = 1.0; |
||||||
|
done = true; |
||||||
|
} |
||||||
|
doInterpolate(t); |
||||||
|
return !done; |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract void doInterpolate(double t); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void write(JmeExporter ex) throws IOException { |
||||||
|
OutputCapsule oc = ex.getCapsule(this); |
||||||
|
oc.write(length, "length", 0); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void read(JmeImporter im) throws IOException { |
||||||
|
InputCapsule ic = im.getCapsule(this); |
||||||
|
length = ic.readDouble("length", 0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,71 @@ |
|||||||
|
/* |
||||||
|
* $Id$ |
||||||
|
* |
||||||
|
* Copyright (c) 2015, Simsilica, LLC |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions |
||||||
|
* are met: |
||||||
|
* |
||||||
|
* 1. Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* 2. 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. |
||||||
|
* |
||||||
|
* 3. Neither the name of the copyright holder 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 HOLDER 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.tween; |
||||||
|
|
||||||
|
|
||||||
|
import com.jme3.export.Savable; |
||||||
|
|
||||||
|
/** |
||||||
|
* Represents some action that interpolates across input between 0 |
||||||
|
* and some length value. (For example, movement, rotation, fading.) |
||||||
|
* It's also possible to have zero length 'instant' tweens. |
||||||
|
* |
||||||
|
* @author Paul Speed |
||||||
|
*/ |
||||||
|
public interface Tween extends Savable, Cloneable { |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the length of the tween. If 't' represents time in |
||||||
|
* seconds then this is the notional time in seconds that the tween |
||||||
|
* will run. Note: all of the caveats are because tweens may be |
||||||
|
* externally scaled in such a way that 't' no longer represents |
||||||
|
* actual time. |
||||||
|
*/ |
||||||
|
public double getLength(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the implementation specific interpolation to the |
||||||
|
* specified 'tween' value as a value in the range from 0 to |
||||||
|
* getLength(). If the value is greater or equal to getLength() |
||||||
|
* then it is internally clamped and the method returns false. |
||||||
|
* If 't' is still in the tween's range then this method returns |
||||||
|
* true. |
||||||
|
*/ |
||||||
|
public boolean interpolate(double t); |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,13 @@ |
|||||||
|
package com.jme3.math; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Nehon on 26/03/2017. |
||||||
|
*/ |
||||||
|
public interface EaseFunction { |
||||||
|
|
||||||
|
/** |
||||||
|
* @param value a value from 0 to 1. Passing a value out of this range will have unexpected behavior. |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
float apply(float value); |
||||||
|
} |
@ -0,0 +1,163 @@ |
|||||||
|
package com.jme3.math; |
||||||
|
|
||||||
|
/** |
||||||
|
* Expose several Easing function from Robert Penner |
||||||
|
* Created by Nehon on 26/03/2017. |
||||||
|
*/ |
||||||
|
public class Easing { |
||||||
|
|
||||||
|
|
||||||
|
public static EaseFunction constant = new EaseFunction() { |
||||||
|
@Override |
||||||
|
public float apply(float value) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
}; |
||||||
|
/** |
||||||
|
* In |
||||||
|
*/ |
||||||
|
public static EaseFunction linear = new EaseFunction() { |
||||||
|
@Override |
||||||
|
public float apply(float value) { |
||||||
|
return value; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public static EaseFunction inQuad = new EaseFunction() { |
||||||
|
@Override |
||||||
|
public float apply(float value) { |
||||||
|
return value * value; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public static EaseFunction inCubic = new EaseFunction() { |
||||||
|
@Override |
||||||
|
public float apply(float value) { |
||||||
|
return value * value * value; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public static EaseFunction inQuart = new EaseFunction() { |
||||||
|
@Override |
||||||
|
public float apply(float value) { |
||||||
|
return value * value * value * value; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public static EaseFunction inQuint = new EaseFunction() { |
||||||
|
@Override |
||||||
|
public float apply(float value) { |
||||||
|
return value * value * value * value * value; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Out Elastic and bounce |
||||||
|
*/ |
||||||
|
public static EaseFunction outElastic = new EaseFunction() { |
||||||
|
@Override |
||||||
|
public float apply(float value) { |
||||||
|
return FastMath.pow(2f, -10f * value) * FastMath.sin((value - 0.3f / 4f) * (2f * FastMath.PI) / 0.3f) + 1f; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public static EaseFunction outBounce = new EaseFunction() { |
||||||
|
@Override |
||||||
|
public float apply(float value) { |
||||||
|
if (value < (1f / 2.75f)) { |
||||||
|
return (7.5625f * value * value); |
||||||
|
} else if (value < (2f / 2.75f)) { |
||||||
|
return (7.5625f * (value -= (1.5f / 2.75f)) * value + 0.75f); |
||||||
|
} else if (value < (2.5 / 2.75)) { |
||||||
|
return (7.5625f * (value -= (2.25f / 2.75f)) * value + 0.9375f); |
||||||
|
} else { |
||||||
|
return (7.5625f * (value -= (2.625f / 2.75f)) * value + 0.984375f); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* In Elastic and bounce |
||||||
|
*/ |
||||||
|
public static EaseFunction inElastic = new Invert(outElastic); |
||||||
|
public static EaseFunction inBounce = new Invert(outBounce); |
||||||
|
|
||||||
|
/** |
||||||
|
* Out |
||||||
|
*/ |
||||||
|
public static EaseFunction outQuad = new Invert(inQuad); |
||||||
|
public static EaseFunction outCubic = new Invert(inCubic); |
||||||
|
public static EaseFunction outQuart = new Invert(inQuart); |
||||||
|
public static EaseFunction outQuint = new Invert(inQuint); |
||||||
|
|
||||||
|
/** |
||||||
|
* inOut |
||||||
|
*/ |
||||||
|
public static EaseFunction inOutQuad = new InOut(inQuad, outQuad); |
||||||
|
public static EaseFunction inOutCubic = new InOut(inCubic, outCubic); |
||||||
|
public static EaseFunction inOutQuart = new InOut(inQuart, outQuart); |
||||||
|
public static EaseFunction inOutQuint = new InOut(inQuint, outQuint); |
||||||
|
public static EaseFunction inOutElastic = new InOut(inElastic, outElastic); |
||||||
|
public static EaseFunction inOutBounce = new InOut(inBounce, outBounce); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Extra functions |
||||||
|
*/ |
||||||
|
|
||||||
|
public static EaseFunction smoothStep = new EaseFunction() { |
||||||
|
@Override |
||||||
|
public float apply(float t) { |
||||||
|
return t * t * (3f - 2f * t); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
public static EaseFunction smootherStep = new EaseFunction() { |
||||||
|
@Override |
||||||
|
public float apply(float t) { |
||||||
|
return t * t * t * (t * (t * 6f - 15f) + 10f); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* An Ease function composed of 2 sb function for custom in and out easing |
||||||
|
*/ |
||||||
|
public static class InOut implements EaseFunction { |
||||||
|
|
||||||
|
private EaseFunction in; |
||||||
|
private EaseFunction out; |
||||||
|
|
||||||
|
public InOut(EaseFunction in, EaseFunction out) { |
||||||
|
this.in = in; |
||||||
|
this.out = out; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public float apply(float value) { |
||||||
|
if (value < 0.5) { |
||||||
|
value = value * 2; |
||||||
|
return inQuad.apply(value) / 2; |
||||||
|
} else { |
||||||
|
value = (value - 0.5f) * 2; |
||||||
|
return outQuad.apply(value) / 2 + 0.5f; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class Invert implements EaseFunction { |
||||||
|
|
||||||
|
private EaseFunction func; |
||||||
|
|
||||||
|
public Invert(EaseFunction func) { |
||||||
|
this.func = func; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public float apply(float value) { |
||||||
|
return 1f - func.apply(1f - value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,165 @@ |
|||||||
|
package com.jme3.math; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Nehon on 23/04/2017. |
||||||
|
*/ |
||||||
|
public class MathUtils { |
||||||
|
|
||||||
|
public static Quaternion log(Quaternion q, Quaternion store) { |
||||||
|
float a = FastMath.acos(q.w); |
||||||
|
float sina = FastMath.sin(a); |
||||||
|
|
||||||
|
store.w = 0; |
||||||
|
if (sina > 0) { |
||||||
|
store.x = a * q.x / sina; |
||||||
|
store.y = a * q.y / sina; |
||||||
|
store.z = a * q.z / sina; |
||||||
|
} else { |
||||||
|
store.x = 0; |
||||||
|
store.y = 0; |
||||||
|
store.z = 0; |
||||||
|
} |
||||||
|
return store; |
||||||
|
} |
||||||
|
|
||||||
|
public static Quaternion exp(Quaternion q, Quaternion store) { |
||||||
|
|
||||||
|
float len = FastMath.sqrt(q.x * q.x + q.y * q.y + q.z * q.z); |
||||||
|
float sinLen = FastMath.sin(len); |
||||||
|
float cosLen = FastMath.cos(len); |
||||||
|
|
||||||
|
store.w = cosLen; |
||||||
|
if (len > 0) { |
||||||
|
store.x = sinLen * q.x / len; |
||||||
|
store.y = sinLen * q.y / len; |
||||||
|
store.z = sinLen * q.z / len; |
||||||
|
} else { |
||||||
|
store.x = 0; |
||||||
|
store.y = 0; |
||||||
|
store.z = 0; |
||||||
|
} |
||||||
|
return store; |
||||||
|
} |
||||||
|
|
||||||
|
//! This version of slerp, used by squad, does not check for theta > 90.
|
||||||
|
public static Quaternion slerpNoInvert(Quaternion q1, Quaternion q2, float t, Quaternion store) { |
||||||
|
float dot = q1.dot(q2); |
||||||
|
|
||||||
|
if (dot > -0.95f && dot < 0.95f) { |
||||||
|
float angle = FastMath.acos(dot); |
||||||
|
float sin1 = FastMath.sin(angle * (1 - t)); |
||||||
|
float sin2 = FastMath.sin(angle * t); |
||||||
|
float sin3 = FastMath.sin(angle); |
||||||
|
store.x = (q1.x * sin1 + q2.x * sin2) / sin3; |
||||||
|
store.y = (q1.y * sin1 + q2.y * sin2) / sin3; |
||||||
|
store.z = (q1.z * sin1 + q2.z * sin2) / sin3; |
||||||
|
store.w = (q1.w * sin1 + q2.w * sin2) / sin3; |
||||||
|
System.err.println("real slerp"); |
||||||
|
} else { |
||||||
|
// if the angle is small, use linear interpolation
|
||||||
|
store.set(q1).nlerp(q2, t); |
||||||
|
System.err.println("nlerp"); |
||||||
|
} |
||||||
|
return store; |
||||||
|
} |
||||||
|
|
||||||
|
public static Quaternion slerp(Quaternion q1, Quaternion q2, float t, Quaternion store) { |
||||||
|
|
||||||
|
float dot = (q1.x * q2.x) + (q1.y * q2.y) + (q1.z * q2.z) |
||||||
|
+ (q1.w * q2.w); |
||||||
|
|
||||||
|
if (dot < 0.0f) { |
||||||
|
// Negate the second quaternion and the result of the dot product
|
||||||
|
q2.x = -q2.x; |
||||||
|
q2.y = -q2.y; |
||||||
|
q2.z = -q2.z; |
||||||
|
q2.w = -q2.w; |
||||||
|
dot = -dot; |
||||||
|
} |
||||||
|
|
||||||
|
// Set the first and second scale for the interpolation
|
||||||
|
float scale0 = 1 - t; |
||||||
|
float scale1 = t; |
||||||
|
|
||||||
|
// Check if the angle between the 2 quaternions was big enough to
|
||||||
|
// warrant such calculations
|
||||||
|
if (dot < 0.9f) {// Get the angle between the 2 quaternions,
|
||||||
|
// and then store the sin() of that angle
|
||||||
|
float theta = FastMath.acos(dot); |
||||||
|
float invSinTheta = 1f / FastMath.sin(theta); |
||||||
|
|
||||||
|
// Calculate the scale for q1 and q2, according to the angle and
|
||||||
|
// it's sine value
|
||||||
|
scale0 = FastMath.sin((1 - t) * theta) * invSinTheta; |
||||||
|
scale1 = FastMath.sin((t * theta)) * invSinTheta; |
||||||
|
|
||||||
|
// Calculate the x, y, z and w values for the quaternion by using a
|
||||||
|
// special
|
||||||
|
// form of linear interpolation for quaternions.
|
||||||
|
store.x = (scale0 * q1.x) + (scale1 * q2.x); |
||||||
|
store.y = (scale0 * q1.y) + (scale1 * q2.y); |
||||||
|
store.z = (scale0 * q1.z) + (scale1 * q2.z); |
||||||
|
store.w = (scale0 * q1.w) + (scale1 * q2.w); |
||||||
|
} else { |
||||||
|
store.x = (scale0 * q1.x) + (scale1 * q2.x); |
||||||
|
store.y = (scale0 * q1.y) + (scale1 * q2.y); |
||||||
|
store.z = (scale0 * q1.z) + (scale1 * q2.z); |
||||||
|
store.w = (scale0 * q1.w) + (scale1 * q2.w); |
||||||
|
store.normalizeLocal(); |
||||||
|
} |
||||||
|
// Return the interpolated quaternion
|
||||||
|
return store; |
||||||
|
} |
||||||
|
|
||||||
|
// //! Given 3 quaternions, qn-1,qn and qn+1, calculate a control point to be used in spline interpolation
|
||||||
|
// private static Quaternion spline(Quaternion qnm1, Quaternion qn, Quaternion qnp1, Quaternion store, Quaternion tmp) {
|
||||||
|
// store.set(-qn.x, -qn.y, -qn.z, qn.w);
|
||||||
|
// //store.set(qn).inverseLocal();
|
||||||
|
// tmp.set(store);
|
||||||
|
//
|
||||||
|
// log(store.multLocal(qnm1), store);
|
||||||
|
// log(tmp.multLocal(qnp1), tmp);
|
||||||
|
// store.addLocal(tmp).multLocal(1f / -4f);
|
||||||
|
// exp(store, tmp);
|
||||||
|
// store.set(tmp).multLocal(qn);
|
||||||
|
//
|
||||||
|
// return store.normalizeLocal();
|
||||||
|
// //return qn * (((qni * qnm1).log() + (qni * qnp1).log()) / -4).exp();
|
||||||
|
// }
|
||||||
|
|
||||||
|
//! Given 3 quaternions, qn-1,qn and qn+1, calculate a control point to be used in spline interpolation
|
||||||
|
private static Quaternion spline(Quaternion qnm1, Quaternion qn, Quaternion qnp1, Quaternion store, Quaternion tmp) { |
||||||
|
Quaternion invQn = new Quaternion(-qn.x, -qn.y, -qn.z, qn.w); |
||||||
|
|
||||||
|
|
||||||
|
log(invQn.mult(qnp1), tmp); |
||||||
|
log(invQn.mult(qnm1), store); |
||||||
|
store.addLocal(tmp).multLocal(-1f / 4f); |
||||||
|
exp(store, tmp); |
||||||
|
store.set(qn).multLocal(tmp); |
||||||
|
|
||||||
|
return store.normalizeLocal(); |
||||||
|
//return qn * (((qni * qnm1).log() + (qni * qnp1).log()) / -4).exp();
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
//! spherical cubic interpolation
|
||||||
|
public static Quaternion squad(Quaternion q0, Quaternion q1, Quaternion q2, Quaternion q3, Quaternion a, Quaternion b, float t, Quaternion store) { |
||||||
|
|
||||||
|
spline(q0, q1, q2, a, store); |
||||||
|
spline(q1, q2, q3, b, store); |
||||||
|
|
||||||
|
slerp(a, b, t, store); |
||||||
|
slerp(q1, q2, t, a); |
||||||
|
return slerp(a, store, 2 * t * (1 - t), b); |
||||||
|
//slerpNoInvert(a, b, t, store);
|
||||||
|
//slerpNoInvert(q1, q2, t, a);
|
||||||
|
//return slerpNoInvert(a, store, 2 * t * (1 - t), b);
|
||||||
|
|
||||||
|
// quaternion c = slerpNoInvert(q1, q2, t),
|
||||||
|
// d = slerpNoInvert(a, b, t);
|
||||||
|
// return slerpNoInvert(c, d, 2 * t * (1 - t));
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
/* |
||||||
|
* To change this template, choose Tools | Templates |
||||||
|
* and open the template in the editor. |
||||||
|
*/ |
||||||
|
package jme3test.model.anim; |
||||||
|
|
||||||
|
import com.jme3.system.Timer; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Nehon |
||||||
|
*/ |
||||||
|
public class EraseTimer extends Timer { |
||||||
|
|
||||||
|
|
||||||
|
//private static final long TIMER_RESOLUTION = 1000L;
|
||||||
|
//private static final float INVERSE_TIMER_RESOLUTION = 1f/1000L;
|
||||||
|
private static final long TIMER_RESOLUTION = 1000000000L; |
||||||
|
private static final float INVERSE_TIMER_RESOLUTION = 1f / 1000000000L; |
||||||
|
|
||||||
|
private long startTime; |
||||||
|
private long previousTime; |
||||||
|
private float tpf; |
||||||
|
private float fps; |
||||||
|
|
||||||
|
public EraseTimer() { |
||||||
|
//startTime = System.currentTimeMillis();
|
||||||
|
startTime = System.nanoTime(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the time in seconds. The timer starts |
||||||
|
* at 0.0 seconds. |
||||||
|
* |
||||||
|
* @return the current time in seconds |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public float getTimeInSeconds() { |
||||||
|
return getTime() * INVERSE_TIMER_RESOLUTION; |
||||||
|
} |
||||||
|
|
||||||
|
public long getTime() { |
||||||
|
//return System.currentTimeMillis() - startTime;
|
||||||
|
return System.nanoTime() - startTime; |
||||||
|
} |
||||||
|
|
||||||
|
public long getResolution() { |
||||||
|
return TIMER_RESOLUTION; |
||||||
|
} |
||||||
|
|
||||||
|
public float getFrameRate() { |
||||||
|
return fps; |
||||||
|
} |
||||||
|
|
||||||
|
public float getTimePerFrame() { |
||||||
|
return tpf; |
||||||
|
} |
||||||
|
|
||||||
|
public void update() { |
||||||
|
tpf = (getTime() - previousTime) * (1.0f / TIMER_RESOLUTION); |
||||||
|
if (tpf >= 0.2) { |
||||||
|
//the frame lasted more than 200ms we erase its time to 16ms.
|
||||||
|
tpf = 0.016666f; |
||||||
|
} else { |
||||||
|
fps = 1.0f / tpf; |
||||||
|
} |
||||||
|
previousTime = getTime(); |
||||||
|
} |
||||||
|
|
||||||
|
public void reset() { |
||||||
|
//startTime = System.currentTimeMillis();
|
||||||
|
startTime = System.nanoTime(); |
||||||
|
previousTime = getTime(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue