diff --git a/.gitignore b/.gitignore index ce4b6840a..065ad2311 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ **/.classpath **/.settings **/.project -**/out +**/out/ /.gradle/ /.nb-gradle/ /.idea/ diff --git a/jme3-core/src/main/java/com/jme3/anim/AnimClip.java b/jme3-core/src/main/java/com/jme3/anim/AnimClip.java new file mode 100644 index 000000000..00eff2c05 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/AnimClip.java @@ -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 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 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); + } + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/anim/AnimComposer.java b/jme3-core/src/main/java/com/jme3/anim/AnimComposer.java new file mode 100644 index 000000000..6a74a0c2c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/AnimComposer.java @@ -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 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 + * AnimControl. + * + * @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) { + + } +} diff --git a/jme3-core/src/main/java/com/jme3/animation/Armature.java b/jme3-core/src/main/java/com/jme3/anim/Armature.java similarity index 82% rename from jme3-core/src/main/java/com/jme3/animation/Armature.java rename to jme3-core/src/main/java/com/jme3/anim/Armature.java index 9c09e63a0..982e08d36 100644 --- a/jme3-core/src/main/java/com/jme3/animation/Armature.java +++ b/jme3-core/src/main/java/com/jme3/anim/Armature.java @@ -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. -// *

-// * 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); } - } diff --git a/jme3-core/src/main/java/com/jme3/animation/Joint.java b/jme3-core/src/main/java/com/jme3/anim/Joint.java similarity index 92% rename from jme3-core/src/main/java/com/jme3/animation/Joint.java rename to jme3-core/src/main/java/com/jme3/anim/Joint.java index a5a35af7f..3797039fe 100644 --- a/jme3-core/src/main/java/com/jme3/animation/Joint.java +++ b/jme3-core/src/main/java/com/jme3/anim/Joint.java @@ -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 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() { diff --git a/jme3-core/src/main/java/com/jme3/anim/JointTrack.java b/jme3-core/src/main/java/com/jme3/anim/JointTrack.java new file mode 100644 index 000000000..1d2b4fd51 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/JointTrack.java @@ -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); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/animation/ArmatureControl.java b/jme3-core/src/main/java/com/jme3/anim/SkinningControl.java similarity index 97% rename from jme3-core/src/main/java/com/jme3/animation/ArmatureControl.java rename to jme3-core/src/main/java/com/jme3/anim/SkinningControl.java index 00f017f32..097840e81 100644 --- a/jme3-core/src/main/java/com/jme3/animation/ArmatureControl.java +++ b/jme3-core/src/main/java/com/jme3/anim/SkinningControl.java @@ -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 + *

+ * 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 targets = new SafeArrayList(Geometry.class); + private SafeArrayList 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,10 +561,9 @@ 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 tb the tangent vertexBuffer + * @param mesh the mesh + * @param offsetMatrices the offsetMatrices to apply + * @param tb the tangent vertexBuffer */ private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexBuffer tb) { int maxWeightsPerVert = mesh.getMaxNumWeights(); diff --git a/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java b/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java new file mode 100644 index 000000000..5a2c329d9 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java @@ -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); + } +} diff --git a/jme3-core/src/main/java/com/jme3/anim/interpolator/AnimInterpolator.java b/jme3-core/src/main/java/com/jme3/anim/interpolator/AnimInterpolator.java new file mode 100644 index 000000000..a924a9d28 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/interpolator/AnimInterpolator.java @@ -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 { + + public abstract T interpolate(float t, int currentIndex, TrackDataReader data, TrackTimeReader times, T store); + +} diff --git a/jme3-core/src/main/java/com/jme3/anim/interpolator/AnimInterpolators.java b/jme3-core/src/main/java/com/jme3/anim/interpolator/AnimInterpolators.java new file mode 100644 index 000000000..c51b73eba --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/interpolator/AnimInterpolators.java @@ -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 NLerp = new AnimInterpolator() { + private Quaternion next = new Quaternion(); + + @Override + public Quaternion interpolate(float t, int currentIndex, TrackDataReader data, TrackTimeReader times, Quaternion store) { + data.getEntryClamp(currentIndex, store); + data.getEntryClamp(currentIndex + 1, next); + store.nlerp(next, t); + return store; + } + }; + + public static final AnimInterpolator SLerp = new AnimInterpolator() { + private Quaternion next = new Quaternion(); + + @Override + public Quaternion interpolate(float t, int currentIndex, TrackDataReader 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 SQuad = new AnimInterpolator() { + 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 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 LinearVec3f = new AnimInterpolator() { + private Vector3f next = new Vector3f(); + + @Override + public Vector3f interpolate(float t, int currentIndex, TrackDataReader 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 { + 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 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 { + private EaseFunction ease; + + public TimeInterpolator(EaseFunction ease) { + this.ease = ease; + } + + @Override + public Float interpolate(float t, int currentIndex, TrackDataReader 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); + + +} diff --git a/jme3-core/src/main/java/com/jme3/anim/interpolator/FrameInterpolator.java b/jme3-core/src/main/java/com/jme3/anim/interpolator/FrameInterpolator.java new file mode 100644 index 000000000..60d40038a --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/interpolator/FrameInterpolator.java @@ -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 timeInterpolator; + private AnimInterpolator translationInterpolator = AnimInterpolators.LinearVec3f; + private AnimInterpolator rotationInterpolator = AnimInterpolators.NLerp; + private AnimInterpolator scaleInterpolator = AnimInterpolators.LinearVec3f; + + private TrackDataReader translationReader = new TrackDataReader<>(); + private TrackDataReader rotationReader = new TrackDataReader<>(); + private TrackDataReader 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 timeInterpolator) { + this.timeInterpolator = timeInterpolator; + } + + public void setTranslationInterpolator(AnimInterpolator translationInterpolator) { + this.translationInterpolator = translationInterpolator; + } + + public void setRotationInterpolator(AnimInterpolator rotationInterpolator) { + this.rotationInterpolator = rotationInterpolator; + } + + public void setScaleInterpolator(AnimInterpolator 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 { + + private CompactArray data; + + protected void setData(CompactArray 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; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/anim/tween/AbstractTween.java b/jme3-core/src/main/java/com/jme3/anim/tween/AbstractTween.java new file mode 100644 index 000000000..aa3407268 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/tween/AbstractTween.java @@ -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); + } +} + \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/anim/tween/Tween.java b/jme3-core/src/main/java/com/jme3/anim/tween/Tween.java new file mode 100644 index 000000000..450eb5f44 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/tween/Tween.java @@ -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); + +} + diff --git a/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java b/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java index 0cd24cab8..04c8c2f49 100644 --- a/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java +++ b/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java @@ -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; diff --git a/jme3-core/src/main/java/com/jme3/animation/AnimControl.java b/jme3-core/src/main/java/com/jme3/animation/AnimControl.java index 8718cde26..7386e1084 100644 --- a/jme3-core/src/main/java/com/jme3/animation/AnimControl.java +++ b/jme3-core/src/main/java/com/jme3/animation/AnimControl.java @@ -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 { /** diff --git a/jme3-core/src/main/java/com/jme3/animation/AnimEventListener.java b/jme3-core/src/main/java/com/jme3/animation/AnimEventListener.java index d9b48459a..c20f150da 100644 --- a/jme3-core/src/main/java/com/jme3/animation/AnimEventListener.java +++ b/jme3-core/src/main/java/com/jme3/animation/AnimEventListener.java @@ -37,6 +37,7 @@ package com.jme3.animation; * * @author Kirill Vainer */ +@Deprecated public interface AnimEventListener { /** diff --git a/jme3-core/src/main/java/com/jme3/animation/Animation.java b/jme3-core/src/main/java/com/jme3/animation/Animation.java index 92a5a38fc..834595238 100644 --- a/jme3-core/src/main/java/com/jme3/animation/Animation.java +++ b/jme3-core/src/main/java/com/jme3/animation/Animation.java @@ -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 { /** diff --git a/jme3-core/src/main/java/com/jme3/animation/AudioTrack.java b/jme3-core/src/main/java/com/jme3/animation/AudioTrack.java index f4c53e4f1..300287c1e 100644 --- a/jme3-core/src/main/java/com/jme3/animation/AudioTrack.java +++ b/jme3-core/src/main/java/com/jme3/animation/AudioTrack.java @@ -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()); diff --git a/jme3-core/src/main/java/com/jme3/animation/Bone.java b/jme3-core/src/main/java/com/jme3/animation/Bone.java index e055f2e46..6cd751e3c 100644 --- a/jme3-core/src/main/java/com/jme3/animation/Bone.java +++ b/jme3-core/src/main/java/com/jme3/animation/Bone.java @@ -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 diff --git a/jme3-core/src/main/java/com/jme3/animation/BoneTrack.java b/jme3-core/src/main/java/com/jme3/animation/BoneTrack.java index 1eca2e9dc..32f5e6b7b 100644 --- a/jme3-core/src/main/java/com/jme3/animation/BoneTrack.java +++ b/jme3-core/src/main/java/com/jme3/animation/BoneTrack.java @@ -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 { /** diff --git a/jme3-core/src/main/java/com/jme3/animation/ClonableTrack.java b/jme3-core/src/main/java/com/jme3/animation/ClonableTrack.java index 6dd94c806..b777203a5 100644 --- a/jme3-core/src/main/java/com/jme3/animation/ClonableTrack.java +++ b/jme3-core/src/main/java/com/jme3/animation/ClonableTrack.java @@ -44,6 +44,7 @@ import com.jme3.util.clone.JmeCloneable; * * @author Nehon */ +@Deprecated public interface ClonableTrack extends Track, JmeCloneable { /** diff --git a/jme3-core/src/main/java/com/jme3/animation/EffectTrack.java b/jme3-core/src/main/java/com/jme3/animation/EffectTrack.java index efc5b9fbb..a05b481c4 100644 --- a/jme3-core/src/main/java/com/jme3/animation/EffectTrack.java +++ b/jme3-core/src/main/java/com/jme3/animation/EffectTrack.java @@ -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); diff --git a/jme3-core/src/main/java/com/jme3/animation/LoopMode.java b/jme3-core/src/main/java/com/jme3/animation/LoopMode.java index ca7b1eb1c..16ca1deb0 100644 --- a/jme3-core/src/main/java/com/jme3/animation/LoopMode.java +++ b/jme3-core/src/main/java/com/jme3/animation/LoopMode.java @@ -35,6 +35,7 @@ package com.jme3.animation; * LoopMode determines how animations repeat, or if they * do not repeat. */ +@Deprecated public enum LoopMode { /** * The animation will play repeatedly, when it reaches the end diff --git a/jme3-core/src/main/java/com/jme3/animation/Pose.java b/jme3-core/src/main/java/com/jme3/animation/Pose.java index fc06d0d07..f5d0e305a 100644 --- a/jme3-core/src/main/java/com/jme3/animation/Pose.java +++ b/jme3-core/src/main/java/com/jme3/animation/Pose.java @@ -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; diff --git a/jme3-core/src/main/java/com/jme3/animation/Skeleton.java b/jme3-core/src/main/java/com/jme3/animation/Skeleton.java index bff876790..651bf06a3 100644 --- a/jme3-core/src/main/java/com/jme3/animation/Skeleton.java +++ b/jme3-core/src/main/java/com/jme3/animation/Skeleton.java @@ -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; diff --git a/jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java b/jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java index 1e9abea5b..06f6927ab 100644 --- a/jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java +++ b/jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java @@ -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 { /** diff --git a/jme3-core/src/main/java/com/jme3/animation/SpatialTrack.java b/jme3-core/src/main/java/com/jme3/animation/SpatialTrack.java index 5afd84350..fc992d2b0 100644 --- a/jme3-core/src/main/java/com/jme3/animation/SpatialTrack.java +++ b/jme3-core/src/main/java/com/jme3/animation/SpatialTrack.java @@ -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,8 +45,9 @@ import java.io.IOException; * * @author Marcin Roguski (Kaelthas) */ +@Deprecated public class SpatialTrack implements JmeCloneable, Track { - + /** * Translations of the track. */ diff --git a/jme3-core/src/main/java/com/jme3/animation/Track.java b/jme3-core/src/main/java/com/jme3/animation/Track.java index 4a6dcdef9..7777e8894 100644 --- a/jme3-core/src/main/java/com/jme3/animation/Track.java +++ b/jme3-core/src/main/java/com/jme3/animation/Track.java @@ -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 { /** diff --git a/jme3-core/src/main/java/com/jme3/animation/TrackInfo.java b/jme3-core/src/main/java/com/jme3/animation/TrackInfo.java index 7ee927cd3..93fd6a88e 100644 --- a/jme3-core/src/main/java/com/jme3/animation/TrackInfo.java +++ b/jme3-core/src/main/java/com/jme3/animation/TrackInfo.java @@ -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 tracks = new ArrayList(); diff --git a/jme3-core/src/main/java/com/jme3/math/EaseFunction.java b/jme3-core/src/main/java/com/jme3/math/EaseFunction.java new file mode 100644 index 000000000..c2f5383f2 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/math/EaseFunction.java @@ -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); +} diff --git a/jme3-core/src/main/java/com/jme3/math/Easing.java b/jme3-core/src/main/java/com/jme3/math/Easing.java new file mode 100644 index 000000000..e9969c91a --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/math/Easing.java @@ -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); + } + } + + +} diff --git a/jme3-core/src/main/java/com/jme3/math/MathUtils.java b/jme3-core/src/main/java/com/jme3/math/MathUtils.java new file mode 100644 index 000000000..a27c266ce --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/math/MathUtils.java @@ -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)); + } + + +} diff --git a/jme3-core/src/main/java/com/jme3/math/Transform.java b/jme3-core/src/main/java/com/jme3/math/Transform.java index f0a18bf91..6c8795a4a 100644 --- a/jme3-core/src/main/java/com/jme3/math/Transform.java +++ b/jme3-core/src/main/java/com/jme3/math/Transform.java @@ -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); } diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureBone.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureBone.java index b0b6e7e27..3e3149f59 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureBone.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureBone.java @@ -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; diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugAppState.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugAppState.java index 73d0a7b01..06661eff7 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugAppState.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugAppState.java @@ -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); } diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugger.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugger.java index 7899cabac..8d068c185 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugger.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugger.java @@ -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(); } } diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureInterJointsWire.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureInterJointsWire.java index fbd97c1e0..3c93e7dad 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureInterJointsWire.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureInterJointsWire.java @@ -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; diff --git a/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java b/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java index d3d0da624..1623977c1 100644 --- a/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java +++ b/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java @@ -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); // } } diff --git a/jme3-examples/src/main/java/jme3test/model/TestGltfLoading2.java b/jme3-examples/src/main/java/jme3test/model/TestGltfLoading2.java index 7988da4fa..d467fbc9b 100644 --- a/jme3-examples/src/main/java/jme3test/model/TestGltfLoading2.java +++ b/jme3-examples/src/main/java/jme3test/model/TestGltfLoading2.java @@ -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); // } } diff --git a/jme3-examples/src/main/java/jme3test/model/anim/EraseTimer.java b/jme3-examples/src/main/java/jme3test/model/anim/EraseTimer.java new file mode 100644 index 000000000..ba6189195 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/model/anim/EraseTimer.java @@ -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(); + } + + +} diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java b/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java index c4f23e882..0beb2d0ae 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java @@ -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)); } }