Draft of the new animation system

shader-nodes-enhancement
Nehon 7 years ago committed by Rémy Bouquet
parent 4904d0235e
commit c3cb4ef97f
  1. 2
      .gitignore
  2. 113
      jme3-core/src/main/java/com/jme3/anim/AnimClip.java
  3. 83
      jme3-core/src/main/java/com/jme3/anim/AnimComposer.java
  4. 49
      jme3-core/src/main/java/com/jme3/anim/Armature.java
  5. 42
      jme3-core/src/main/java/com/jme3/anim/Joint.java
  6. 113
      jme3-core/src/main/java/com/jme3/anim/JointTrack.java
  7. 26
      jme3-core/src/main/java/com/jme3/anim/SkinningControl.java
  8. 344
      jme3-core/src/main/java/com/jme3/anim/TransformTrack.java
  9. 13
      jme3-core/src/main/java/com/jme3/anim/interpolator/AnimInterpolator.java
  10. 151
      jme3-core/src/main/java/com/jme3/anim/interpolator/AnimInterpolators.java
  11. 121
      jme3-core/src/main/java/com/jme3/anim/interpolator/FrameInterpolator.java
  12. 110
      jme3-core/src/main/java/com/jme3/anim/tween/AbstractTween.java
  13. 71
      jme3-core/src/main/java/com/jme3/anim/tween/Tween.java
  14. 2
      jme3-core/src/main/java/com/jme3/animation/AnimChannel.java
  15. 2
      jme3-core/src/main/java/com/jme3/animation/AnimControl.java
  16. 1
      jme3-core/src/main/java/com/jme3/animation/AnimEventListener.java
  17. 3
      jme3-core/src/main/java/com/jme3/animation/Animation.java
  18. 8
      jme3-core/src/main/java/com/jme3/animation/AudioTrack.java
  19. 2
      jme3-core/src/main/java/com/jme3/animation/Bone.java
  20. 6
      jme3-core/src/main/java/com/jme3/animation/BoneTrack.java
  21. 1
      jme3-core/src/main/java/com/jme3/animation/ClonableTrack.java
  22. 15
      jme3-core/src/main/java/com/jme3/animation/EffectTrack.java
  23. 1
      jme3-core/src/main/java/com/jme3/animation/LoopMode.java
  24. 2
      jme3-core/src/main/java/com/jme3/animation/Pose.java
  25. 6
      jme3-core/src/main/java/com/jme3/animation/Skeleton.java
  26. 3
      jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java
  27. 6
      jme3-core/src/main/java/com/jme3/animation/SpatialTrack.java
  28. 1
      jme3-core/src/main/java/com/jme3/animation/Track.java
  29. 8
      jme3-core/src/main/java/com/jme3/animation/TrackInfo.java
  30. 13
      jme3-core/src/main/java/com/jme3/math/EaseFunction.java
  31. 163
      jme3-core/src/main/java/com/jme3/math/Easing.java
  32. 165
      jme3-core/src/main/java/com/jme3/math/MathUtils.java
  33. 3
      jme3-core/src/main/java/com/jme3/math/Transform.java
  34. 4
      jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureBone.java
  35. 8
      jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugAppState.java
  36. 8
      jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugger.java
  37. 4
      jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureInterJointsWire.java
  38. 15
      jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java
  39. 13
      jme3-examples/src/main/java/jme3test/model/TestGltfLoading2.java
  40. 76
      jme3-examples/src/main/java/jme3test/model/anim/EraseTimer.java
  41. 142
      jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java

2
.gitignore vendored

@ -2,7 +2,7 @@
**/.classpath
**/.settings
**/.project
**/out
**/out/
/.gradle/
/.nb-gradle/
/.idea/

@ -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) {
}
}

@ -1,8 +1,7 @@
package com.jme3.animation;
package com.jme3.anim;
import com.jme3.export.*;
import com.jme3.math.Matrix4f;
import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
@ -60,33 +59,6 @@ public class Armature implements JmeCloneable, Savable {
}
}
//
// /**
// * Special-purpose copy constructor.
// * <p>
// * Shallow copies bind pose data from the source skeleton, does not
// * copy any other data.
// *
// * @param source The source Skeleton to copy from
// */
// public Armature(Armature source) {
// Joint[] sourceList = source.jointList;
// jointList = new Joint[sourceList.length];
// for (int i = 0; i < sourceList.length; i++) {
// jointList[i] = new Joint(sourceList[i]);
// }
//
// rootJoints = new Bone[source.rootJoints.length];
// for (int i = 0; i < rootJoints.length; i++) {
// rootJoints[i] = recreateBoneStructure(source.rootJoints[i]);
// }
// createSkinningMatrices();
//
// for (int i = rootJoints.length - 1; i >= 0; i--) {
// rootJoints[i].update();
// }
// }
/**
* Update all joints sin this Amature.
*/
@ -176,8 +148,18 @@ public class Armature implements JmeCloneable, Savable {
//make sure all bones are updated
update();
//Save the current pose as bind pose
for (Joint rootJoint : rootJoints) {
rootJoint.setBindPose();
for (Joint joint : jointList) {
joint.setBindPose();
}
}
/**
* This methods sets this armature in its bind pose (aligned with the undeformed mesh)
* Note that this is only useful for debugging porpose.
*/
public void resetToBindPose() {
for (Joint joint : rootJoints) {
joint.resetToBindPose();
}
}
@ -187,11 +169,9 @@ public class Armature implements JmeCloneable, Savable {
* @return
*/
public Matrix4f[] computeSkinningMatrices() {
TempVars vars = TempVars.get();
for (int i = 0; i < jointList.length; i++) {
jointList[i].getOffsetTransform(skinningMatrixes[i], vars.quat1, vars.vect1, vars.vect2, vars.tempMat3);
jointList[i].getOffsetTransform(skinningMatrixes[i]);
}
vars.release();
return skinningMatrixes;
}
@ -247,5 +227,4 @@ public class Armature implements JmeCloneable, Savable {
output.write(rootJoints, "rootJoints", null);
output.write(jointList, "jointList", null);
}
}

@ -1,4 +1,4 @@
package com.jme3.animation;
package com.jme3.anim;
import com.jme3.export.*;
import com.jme3.material.MatParamOverride;
@ -23,13 +23,6 @@ public class Joint implements Savable, JmeCloneable {
private ArrayList<Joint> children = new ArrayList<>();
private Geometry targetGeometry;
public Joint() {
}
public Joint(String name) {
this.name = name;
}
/**
* The attachment node.
*/
@ -43,29 +36,37 @@ public class Joint implements Savable, JmeCloneable {
/**
* The base transform of the joint in local space.
* Those transform are the bone's initial value.
* Those transform are the joint's initial value.
*/
private Transform baseLocalTransform = new Transform();
/**
* The transform of the bone in model space. Relative to the origin of the model.
* The transform of the joint in model space. Relative to the origin of the model.
*/
private Transform modelTransform = new Transform();
/**
* The matrix used to transform affected vertices position into the bone model space.
* The matrix used to transform affected vertices position into the joint model space.
* Used for skinning.
*/
private Matrix4f inverseModelBindMatrix = new Matrix4f();
public Joint() {
}
public Joint(String name) {
this.name = name;
}
/**
* Updates world transforms for this bone and it's children.
*/
public final void update() {
this.updateModelTransforms();
for (int i = children.size() - 1; i >= 0; i--) {
children.get(i).update();
for (Joint child : children) {
child.update();
}
}
@ -128,7 +129,7 @@ public class Joint implements Savable, JmeCloneable {
*
* @param outTransform
*/
void getOffsetTransform(Matrix4f outTransform, Quaternion tmp1, Vector3f tmp2, Vector3f tmp3, Matrix3f tmp4) {
void getOffsetTransform(Matrix4f outTransform) {
modelTransform.toTransformMatrix(outTransform).mult(inverseModelBindMatrix, outTransform);
}
@ -136,6 +137,19 @@ public class Joint implements Savable, JmeCloneable {
//Note that the whole Armature must be updated before calling this method.
modelTransform.toTransformMatrix(inverseModelBindMatrix);
inverseModelBindMatrix.invertLocal();
baseLocalTransform.set(localTransform);
}
protected void resetToBindPose() {
localTransform.fromTransformMatrix(inverseModelBindMatrix.invert()); // local = modelBind
if (parent != null) {
localTransform.combineWithParent(parent.modelTransform.invert()); // local = local Bind
}
updateModelTransforms();
for (Joint child : children) {
child.resetToBindPose();
}
}
public Vector3f getLocalTranslation() {

@ -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);
}
}

@ -29,7 +29,7 @@
* 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.animation;
package com.jme3.anim;
import com.jme3.export.*;
import com.jme3.material.MatParamOverride;
@ -53,15 +53,17 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The Skeleton control deforms a model according to a armature, It handles the
* The Skinning control deforms a model according to an armature, It handles the
* computation of the deformation matrices and performs the transformations on
* the mesh
* <p>
* It can perform software skinning or Hardware skinning
*
* @author Rémy Bouquet Based on AnimControl by Kirill Vainer
* @author Rémy Bouquet Based on SkeletonControl by Kirill Vainer
*/
public class ArmatureControl extends AbstractControl implements Cloneable, JmeCloneable {
public class SkinningControl extends AbstractControl implements Cloneable, JmeCloneable {
private static final Logger logger = Logger.getLogger(ArmatureControl.class.getName());
private static final Logger logger = Logger.getLogger(SkinningControl.class.getName());
/**
* The armature of the model.
@ -71,7 +73,7 @@ public class ArmatureControl extends AbstractControl implements Cloneable, JmeCl
/**
* List of geometries affected by this control.
*/
private SafeArrayList<Geometry> targets = new SafeArrayList<Geometry>(Geometry.class);
private SafeArrayList<Geometry> targets = new SafeArrayList<>(Geometry.class);
/**
* Used to track when a mesh was updated. Meshes are only updated if they
@ -113,16 +115,16 @@ public class ArmatureControl extends AbstractControl implements Cloneable, JmeCl
/**
* Serialization only. Do not use.
*/
public ArmatureControl() {
public SkinningControl() {
}
/**
* Creates a armature control. The list of targets will be acquired
* automatically when the control is attached to a node.
*
* @param skeleton the armature
* @param armature the armature
*/
public ArmatureControl(Armature armature) {
public SkinningControl(Armature armature) {
if (armature == null) {
throw new IllegalArgumentException("armature cannot be null");
}
@ -283,7 +285,7 @@ public class ArmatureControl extends AbstractControl implements Cloneable, JmeCl
if (hwSkinningSupported) {
hwSkinningEnabled = true;
Logger.getLogger(ArmatureControl.class.getName()).log(Level.INFO, "Hardware skinning engaged for {0}", spatial);
Logger.getLogger(SkinningControl.class.getName()).log(Level.INFO, "Hardware skinning engaged for {0}", spatial);
} else {
switchToSoftware();
}
@ -308,6 +310,7 @@ public class ArmatureControl extends AbstractControl implements Cloneable, JmeCl
@Override
protected void controlUpdate(float tpf) {
wasMeshUpdated = false;
armature.update();
}
//only do this for software updates
@ -558,9 +561,8 @@ public class ArmatureControl extends AbstractControl implements Cloneable, JmeCl
* has additional indexes since tangent has 4 components instead of 3 for
* pos and norm
*
* @param maxWeightsPerVert maximum number of weights per vertex
* @param mesh the mesh
* @param offsetMatrices the offsetMaytrices to apply
* @param offsetMatrices the offsetMatrices to apply
* @param tb the tangent vertexBuffer
*/
private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexBuffer tb) {

@ -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);
}

@ -33,6 +33,7 @@ package com.jme3.animation;
import com.jme3.math.FastMath;
import com.jme3.util.TempVars;
import java.util.BitSet;
/**
@ -46,6 +47,7 @@ import java.util.BitSet;
*
* @author Kirill Vainer
*/
@Deprecated
public final class AnimChannel {
private static final float DEFAULT_BLEND_TIME = 0.15f;

@ -64,7 +64,9 @@ import java.util.Map;
* 1) Morph/Pose animation
*
* @author Kirill Vainer
* @deprecated use {@link com.jme3.anim.AnimComposer}
*/
@Deprecated
public final class AnimControl extends AbstractControl implements Cloneable, JmeCloneable {
/**

@ -37,6 +37,7 @@ package com.jme3.animation;
*
* @author Kirill Vainer
*/
@Deprecated
public interface AnimEventListener {
/**

@ -37,13 +37,16 @@ import com.jme3.util.SafeArrayList;
import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
/**
* The animation class updates the animation target with the tracks of a given type.
*
* @author Kirill Vainer, Marcin Roguski (Kaelthas)
* @deprecated use {@link com.jme3.anim.AnimClip}
*/
@Deprecated
public class Animation implements Savable, Cloneable, JmeCloneable {
/**

@ -32,15 +32,12 @@
package com.jme3.animation;
import com.jme3.audio.AudioNode;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.export.*;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -62,6 +59,7 @@ import java.util.logging.Logger;
*
* @author Nehon
*/
@Deprecated
public class AudioTrack implements ClonableTrack {
private static final Logger logger = Logger.getLogger(AudioTrack.class.getName());

@ -67,7 +67,9 @@ import java.util.ArrayList;
*
* @author Kirill Vainer
* @author Rémy Bouquet
* @deprecated use {@link com.jme3.anim.Joint}
*/
@Deprecated
public final class Bone implements Savable, JmeCloneable {
// Version #2: Changed naming of transforms as they were misleading

@ -35,8 +35,12 @@ import com.jme3.export.*;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.util.TempVars;
<<<<<<< HEAD
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
=======
>>>>>>> Draft of the new animation system
import java.io.IOException;
import java.util.BitSet;
@ -44,7 +48,9 @@ import java.util.BitSet;
* Contains a list of transforms and times for each keyframe.
*
* @author Kirill Vainer
* @deprecated use {@link com.jme3.anim.JointTrack}
*/
@Deprecated
public final class BoneTrack implements JmeCloneable, Track {
/**

@ -44,6 +44,7 @@ import com.jme3.util.clone.JmeCloneable;
*
* @author Nehon
*/
@Deprecated
public interface ClonableTrack extends Track, JmeCloneable {
/**

@ -32,10 +32,7 @@
package com.jme3.animation;
import com.jme3.effect.ParticleEmitter;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.export.*;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
@ -67,6 +64,7 @@ import java.util.logging.Logger;
*
* @author Nehon
*/
@Deprecated
public class EffectTrack implements ClonableTrack {
private static final Logger logger = Logger.getLogger(EffectTrack.class.getName());
@ -130,15 +128,17 @@ public class EffectTrack implements ClonableTrack {
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
};
}
//Anim listener that stops the Emmitter when the animation is finished or changed.
private class OnEndListener implements AnimEventListener {
@Override
public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
stop();
}
@Override
public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
}
}
@ -188,6 +188,7 @@ public class EffectTrack implements ClonableTrack {
* @see Track#setTime(float, float, com.jme3.animation.AnimControl,
* com.jme3.animation.AnimChannel, com.jme3.util.TempVars)
*/
@Override
public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) {
if (time >= length) {
@ -233,6 +234,7 @@ public class EffectTrack implements ClonableTrack {
*
* @return length of the track
*/
@Override
public float getLength() {
return length;
}
@ -325,6 +327,7 @@ public class EffectTrack implements ClonableTrack {
return null;
}
@Override
public void cleanUp() {
TrackInfo t = (TrackInfo) emitter.getUserData("TrackInfo");
t.getTracks().remove(this);
@ -413,6 +416,7 @@ public class EffectTrack implements ClonableTrack {
* @param ex exporter
* @throws IOException exception
*/
@Override
public void write(JmeExporter ex) throws IOException {
OutputCapsule out = ex.getCapsule(this);
//reset the particle emission rate on the emitter before saving.
@ -431,6 +435,7 @@ public class EffectTrack implements ClonableTrack {
* @param im importer
* @throws IOException Exception
*/
@Override
public void read(JmeImporter im) throws IOException {
InputCapsule in = im.getCapsule(this);
this.particlesPerSeconds = in.readFloat("particlesPerSeconds", 0);

@ -35,6 +35,7 @@ package com.jme3.animation;
* <code>LoopMode</code> determines how animations repeat, or if they
* do not repeat.
*/
@Deprecated
public enum LoopMode {
/**
* The animation will play repeatedly, when it reaches the end

@ -34,12 +34,14 @@ package com.jme3.animation;
import com.jme3.export.*;
import com.jme3.math.Vector3f;
import com.jme3.util.BufferUtils;
import java.io.IOException;
import java.nio.FloatBuffer;
/**
* A pose is a list of offsets that say where a mesh vertices should be for this pose.
*/
@Deprecated
public final class Pose implements Savable, Cloneable {
private String name;

@ -31,11 +31,13 @@
*/
package com.jme3.animation;
import com.jme3.anim.Armature;
import com.jme3.export.*;
import com.jme3.math.Matrix4f;
import com.jme3.util.TempVars;
import com.jme3.util.clone.JmeCloneable;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -46,7 +48,9 @@ import java.util.List;
* animated matrixes.
*
* @author Kirill Vainer
* @deprecated use {@link Armature}
*/
@Deprecated
public final class Skeleton implements Savable, JmeCloneable {
private Bone[] rootBones;

@ -31,6 +31,7 @@
*/
package com.jme3.animation;
import com.jme3.anim.SkinningControl;
import com.jme3.export.*;
import com.jme3.material.MatParamOverride;
import com.jme3.math.FastMath;
@ -57,7 +58,9 @@ import java.util.logging.Logger;
* the mesh
*
* @author Rémy Bouquet Based on AnimControl by Kirill Vainer
* @deprecated use {@link SkinningControl}
*/
@Deprecated
public class SkeletonControl extends AbstractControl implements Cloneable, JmeCloneable {
/**

@ -31,10 +31,7 @@
*/
package com.jme3.animation;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.export.*;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
@ -48,6 +45,7 @@ import java.io.IOException;
*
* @author Marcin Roguski (Kaelthas)
*/
@Deprecated
public class SpatialTrack implements JmeCloneable, Track {
/**

@ -34,6 +34,7 @@ package com.jme3.animation;
import com.jme3.export.Savable;
import com.jme3.util.TempVars;
@Deprecated
public interface Track extends Savable, Cloneable {
/**

@ -31,13 +31,10 @@
*/
package com.jme3.animation;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import com.jme3.export.*;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
import java.util.ArrayList;
@ -50,6 +47,7 @@ import java.util.ArrayList;
*
* @author Nehon
*/
@Deprecated
public class TrackInfo implements Savable, JmeCloneable {
ArrayList<Track> tracks = new ArrayList<Track>();

@ -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));
}
}

@ -181,7 +181,8 @@ public final class Transform implements Savable, Cloneable, java.io.Serializable
* @param delta An amount between 0 and 1 representing how far to interpolate from t1 to t2.
*/
public void interpolateTransforms(Transform t1, Transform t2, float delta) {
this.rot.slerp(t1.rot,t2.rot,delta);
t1.rot.nlerp(t2.rot, delta);
this.rot.set(t1.rot);
this.translation.interpolateLocal(t1.translation,t2.translation,delta);
this.scale.interpolateLocal(t1.scale,t2.scale,delta);
}

@ -32,8 +32,8 @@ package com.jme3.scene.debug.custom;
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import com.jme3.animation.Armature;
import com.jme3.animation.Joint;
import com.jme3.anim.Armature;
import com.jme3.anim.Joint;
import com.jme3.bounding.*;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;

@ -4,7 +4,7 @@
*/
package com.jme3.scene.debug.custom;
import com.jme3.animation.*;
import com.jme3.anim.*;
import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
@ -55,9 +55,9 @@ public class ArmatureDebugAppState extends AbstractAppState {
debugNode.updateGeometricState();
}
public ArmatureDebugger addArmature(ArmatureControl armatureControl, boolean guessJointsOrientation) {
Armature armature = armatureControl.getArmature();
Spatial forSpatial = armatureControl.getSpatial();
public ArmatureDebugger addArmature(SkinningControl skinningControl, boolean guessJointsOrientation) {
Armature armature = skinningControl.getArmature();
Spatial forSpatial = skinningControl.getSpatial();
return addArmature(armature, forSpatial, guessJointsOrientation);
}

@ -32,7 +32,9 @@ package com.jme3.scene.debug.custom;
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import com.jme3.animation.*;
import com.jme3.anim.Armature;
import com.jme3.anim.Joint;
import com.jme3.animation.Bone;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
@ -91,7 +93,7 @@ public class ArmatureDebugger extends BatchNode {
interJointWires = new ArmatureInterJointsWire(armature, bonesLength, guessJointsOrientation);
wires = new Geometry(name + "_interwires", interJointWires);
this.attachChild(wires);
// this.attachChild(wires);
}
protected void initialize(AssetManager assetManager) {
@ -152,7 +154,7 @@ public class ArmatureDebugger extends BatchNode {
super.updateLogicalState(tpf);
bones.updateGeometry();
if (interJointWires != null) {
interJointWires.updateGeometry();
// interJointWires.updateGeometry();
}
}

@ -33,8 +33,8 @@ package com.jme3.scene.debug.custom;
*/
import com.jme3.animation.Armature;
import com.jme3.animation.Joint;
import com.jme3.anim.Armature;
import com.jme3.anim.Joint;
import com.jme3.math.Vector3f;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;

@ -43,11 +43,12 @@ import com.jme3.renderer.Limits;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control;
import com.jme3.scene.debug.custom.SkeletonDebugAppState;
import java.util.ArrayList;
import java.util.List;
//import com.jme3.scene.debug.custom.SkeletonDebugAppState;
public class TestGltfLoading extends SimpleApplication {
Node autoRotate = new Node("autoRotate");
@ -74,8 +75,8 @@ public class TestGltfLoading extends SimpleApplication {
*/
public void simpleInitApp() {
SkeletonDebugAppState skeletonDebugAppState = new SkeletonDebugAppState();
getStateManager().attach(skeletonDebugAppState);
// SkeletonDebugAppState skeletonDebugAppState = new SkeletonDebugAppState();
// getStateManager().attach(skeletonDebugAppState);
String folder = System.getProperty("user.home");
assetManager.registerLocator(folder, FileLocator.class);
@ -112,7 +113,7 @@ public class TestGltfLoading extends SimpleApplication {
// loadModel("Models/gltf/box/box.gltf", Vector3f.ZERO, 1);
// loadModel("Models/gltf/duck/Duck.gltf", new Vector3f(0, -1, 0), 1);
// loadModel("Models/gltf/damagedHelmet/damagedHelmet.gltf", Vector3f.ZERO, 1);
// loadModel("Models/gltf/hornet/scene.gltf", new Vector3f(0, -0.5f, 0), 0.4f);
loadModel("Models/gltf/hornet/scene.gltf", new Vector3f(0, -0.5f, 0), 0.4f);
//// loadModel("Models/gltf/adamHead/adamHead.gltf", Vector3f.ZERO, 0.6f);
// loadModel("Models/gltf/busterDrone/busterDrone.gltf", new Vector3f(0, 0f, 0), 0.8f);
// loadModel("Models/gltf/animatedCube/AnimatedCube.gltf", Vector3f.ZERO, 0.5f);
@ -206,14 +207,14 @@ public class TestGltfLoading extends SimpleApplication {
return;
}
ctrl.setHardwareSkinningPreferred(false);
getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(ctrl, true);
//getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(ctrl, true);
// AnimControl aCtrl = findControl(s, AnimControl.class);
// //ctrl.getSpatial().removeControl(ctrl);
// if (aCtrl == null) {
// return;
// }
// if (aCtrl.getSkeleton() != null) {
// getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(aCtrl.getSkeleton(), aCtrl.getSpatial(), true);
// if (aCtrl.getArmature() != null) {
// getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(aCtrl.getArmature(), aCtrl.getSpatial(), true);
// }
}

@ -42,11 +42,12 @@ import com.jme3.renderer.Limits;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control;
import com.jme3.scene.debug.custom.SkeletonDebugAppState;
import com.jme3.scene.plugins.gltf.GltfModelKey;
import java.util.*;
//import com.jme3.scene.debug.custom.SkeletonDebugAppState;
public class TestGltfLoading2 extends SimpleApplication {
Node autoRotate = new Node("autoRotate");
@ -73,8 +74,8 @@ public class TestGltfLoading2 extends SimpleApplication {
*/
public void simpleInitApp() {
SkeletonDebugAppState skeletonDebugAppState = new SkeletonDebugAppState();
getStateManager().attach(skeletonDebugAppState);
// SkeletonDebugAppState skeletonDebugAppState = new SkeletonDebugAppState();
// getStateManager().attach(skeletonDebugAppState);
// cam.setLocation(new Vector3f(4.0339394f, 2.645184f, 6.4627485f));
// cam.setRotation(new Quaternion(-0.013950467f, 0.98604023f, -0.119502485f, -0.11510504f));
@ -226,7 +227,7 @@ public class TestGltfLoading2 extends SimpleApplication {
if (ctrl == null) {
return;
}
//System.err.println(ctrl.getSkeleton().toString());
//System.err.println(ctrl.getArmature().toString());
//ctrl.setHardwareSkinningPreferred(false);
// getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(ctrl, true);
// AnimControl aCtrl = findControl(s, AnimControl.class);
@ -234,8 +235,8 @@ public class TestGltfLoading2 extends SimpleApplication {
// if (aCtrl == null) {
// return;
// }
// if (aCtrl.getSkeleton() != null) {
// getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(aCtrl.getSkeleton(), aCtrl.getSpatial(), true);
// if (aCtrl.getArmature() != null) {
// getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(aCtrl.getArmature(), aCtrl.getSpatial(), true);
// }
}

@ -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();
}
}

@ -1,15 +1,22 @@
package jme3test.model.anim;
import com.jme3.animation.*;
import com.jme3.anim.*;
import com.jme3.app.ChaseCameraAppState;
import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.*;
import com.jme3.scene.*;
import com.jme3.scene.debug.custom.ArmatureDebugAppState;
import com.jme3.scene.shape.Cylinder;
import com.jme3.util.TangentBinormalGenerator;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
/**
* Created by Nehon on 18/12/2017.
*/
@ -25,6 +32,7 @@ public class TestArmature extends SimpleApplication {
@Override
public void simpleInitApp() {
setTimer(new EraseTimer());
renderManager.setSinglePassLightBatchSize(2);
//cam.setFrustumPerspective(90f, (float) cam.getWidth() / cam.getHeight(), 0.01f, 10f);
viewPort.setBackgroundColor(ColorRGBA.DarkGray);
@ -34,20 +42,61 @@ public class TestArmature extends SimpleApplication {
j2 = new Joint("Joint_2");
root.addChild(j1);
j1.addChild(j2);
j1.setLocalTranslation(new Vector3f(0, 0.5f, 0));
j1.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI * 0.3f, Vector3f.UNIT_Z));
j2.setLocalTranslation(new Vector3f(0, 0.2f, 0));
root.setLocalTranslation(new Vector3f(0, 0, 0.5f));
j1.setLocalTranslation(new Vector3f(0, 0.0f, -0.5f));
j2.setLocalTranslation(new Vector3f(0, 0.0f, -0.2f));
Joint[] joints = new Joint[]{root, j1, j2};
Armature armature = new Armature(joints);
final Armature armature = new Armature(joints);
armature.setBindPose();
ArmatureControl ac = new ArmatureControl(armature);
AnimClip clip = new AnimClip("anim");
float[] times = new float[]{0, 2, 4};
Quaternion[] rotations = new Quaternion[]{
new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X),
new Quaternion().fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_X),
new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X)
};
Vector3f[] translations = new Vector3f[]{
new Vector3f(0, 0.2f, 0),
new Vector3f(0, 1.0f, 0),
new Vector3f(0, 0.2f, 0),
};
Vector3f[] scales = new Vector3f[]{
new Vector3f(1, 1, 1),
new Vector3f(2, 2, 2),
new Vector3f(1, 1, 1),
};
Vector3f[] scales2 = new Vector3f[]{
new Vector3f(1, 1, 1),
new Vector3f(0.5f, 0.5f, 0.5f),
new Vector3f(1, 1, 1),
};
JointTrack track1 = new JointTrack(j1, times, null, rotations, null);
JointTrack track2 = new JointTrack(j2, times, null, rotations, null);
clip.addTrack(track1);
clip.addTrack(track2);
final AnimComposer composer = new AnimComposer();
composer.addAnimClip(clip);
SkinningControl ac = new SkinningControl(armature);
ac.setHardwareSkinningPreferred(false);
Node node = new Node("Test Armature");
rootNode.attachChild(node);
Geometry cylinder = new Geometry("cylinder", createMesh());
Material m = new Material(assetManager, "Common/MatDefs/Misc/fakeLighting.j3md");
m.setColor("Color", ColorRGBA.randomColor());
cylinder.setMaterial(m);
node.attachChild(cylinder);
node.addControl(composer);
node.addControl(ac);
composer.setCurrentAnimClip("anim");
ArmatureDebugAppState debugAppState = new ArmatureDebugAppState();
debugAppState.addArmature(ac, true);
stateManager.attach(debugAppState);
@ -71,6 +120,23 @@ public class TestArmature extends SimpleApplication {
chaseCam.setMinDistance(0.01f);
chaseCam.setZoomSpeed(0.01f);
chaseCam.setDefaultVerticalRotation(0.3f);
inputManager.addMapping("bind", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addListener(new ActionListener() {
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if (isPressed) {
play = false;
composer.reset();
armature.resetToBindPose();
} else {
play = true;
composer.setCurrentAnimClip("anim");
}
}
}, "bind");
}
@ -99,14 +165,70 @@ public class TestArmature extends SimpleApplication {
});
}
private Mesh createMesh() {
Cylinder c = new Cylinder(30, 16, 0.1f, 1, true);
ShortBuffer jointIndex = (ShortBuffer) VertexBuffer.createBuffer(VertexBuffer.Format.UnsignedShort, 4, c.getVertexCount());
jointIndex.rewind();
c.setMaxNumWeights(1);
FloatBuffer jointWeight = (FloatBuffer) VertexBuffer.createBuffer(VertexBuffer.Format.Float, 4, c.getVertexCount());
jointWeight.rewind();
VertexBuffer vb = c.getBuffer(VertexBuffer.Type.Position);
FloatBuffer fvb = (FloatBuffer) vb.getData();
fvb.rewind();
for (int i = 0; i < c.getVertexCount(); i++) {
fvb.get();
fvb.get();
float z = fvb.get();
int index = 0;
if (z > 0) {
index = 0;
} else if (z > -0.2) {
index = 1;
} else {
index = 2;
}
jointIndex.put((short) index).put((short) 0).put((short) 0).put((short) 0);
jointWeight.put(1f).put(0f).put(0f).put(0f);
}
c.setBuffer(VertexBuffer.Type.BoneIndex, 4, jointIndex);
c.setBuffer(VertexBuffer.Type.BoneWeight, 4, jointWeight);
c.updateCounts();
c.updateBound();
//the mesh has some skinning let's create needed buffers for HW skinning
//creating empty buffers for HW skinning
//the buffers will be setup if ever used.
VertexBuffer weightsHW = new VertexBuffer(VertexBuffer.Type.HWBoneWeight);
VertexBuffer indicesHW = new VertexBuffer(VertexBuffer.Type.HWBoneIndex);
//setting usage to cpuOnly so that the buffer is not send empty to the GPU
indicesHW.setUsage(VertexBuffer.Usage.CpuOnly);
weightsHW.setUsage(VertexBuffer.Usage.CpuOnly);
c.setBuffer(weightsHW);
c.setBuffer(indicesHW);
c.generateBindPose();
c.prepareForAnim(false);
return c;
}
float time = 0;
boolean play = true;
@Override
public void simpleUpdate(float tpf) {
time += tpf;
float rot = FastMath.sin(time);
j1.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI * rot, Vector3f.UNIT_Z));
j2.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI * rot, Vector3f.UNIT_Z));
// if (play == false) {
// return;
// }
// time += tpf;
// float rot = FastMath.sin(time);
// j1.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI * rot, Vector3f.UNIT_Z));
// j2.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI * rot, Vector3f.UNIT_Z));
}
}

Loading…
Cancel
Save