From 74f9a648d16bed6cba64a22bf8285588908e222c Mon Sep 17 00:00:00 2001 From: Nehon Date: Sat, 23 Dec 2017 18:36:12 +0100 Subject: [PATCH] Adds support for different joint model transform accumulation strategy --- .../src/main/java/com/jme3/anim/Armature.java | 35 ++++++++- .../src/main/java/com/jme3/anim/Joint.java | 43 +++++----- .../jme3/anim/MatrixJointModelTransform.java | 78 +++++++++++++++++++ .../anim/SeparateJointModelTransform.java | 74 ++++++++++++++++++ .../jme3/anim/util/JointModelTransform.java | 22 ++++++ .../jme3test/model/anim/TestArmature.java | 2 + 6 files changed, 229 insertions(+), 25 deletions(-) create mode 100644 jme3-core/src/main/java/com/jme3/anim/MatrixJointModelTransform.java create mode 100644 jme3-core/src/main/java/com/jme3/anim/SeparateJointModelTransform.java create mode 100644 jme3-core/src/main/java/com/jme3/anim/util/JointModelTransform.java diff --git a/jme3-core/src/main/java/com/jme3/anim/Armature.java b/jme3-core/src/main/java/com/jme3/anim/Armature.java index 982e08d36..4e86b8fee 100644 --- a/jme3-core/src/main/java/com/jme3/anim/Armature.java +++ b/jme3-core/src/main/java/com/jme3/anim/Armature.java @@ -1,5 +1,6 @@ package com.jme3.anim; +import com.jme3.anim.util.JointModelTransform; import com.jme3.export.*; import com.jme3.math.Matrix4f; import com.jme3.util.clone.Cloner; @@ -22,7 +23,7 @@ public class Armature implements JmeCloneable, Savable { * will cause it to go to the animated position. */ private transient Matrix4f[] skinningMatrixes; - + private Class modelTransformClass = MatrixJointModelTransform.class; /** * Serialization only @@ -44,9 +45,14 @@ public class Armature implements JmeCloneable, Savable { List rootJointList = new ArrayList<>(); for (int i = jointList.length - 1; i >= 0; i--) { - Joint b = jointList[i]; - if (b.getParent() == null) { - rootJointList.add(b); + Joint joint = jointList[i]; + try { + joint.setJointModelTransform(modelTransformClass.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + throw new IllegalArgumentException(e); + } + if (joint.getParent() == null) { + rootJointList.add(joint); } } rootJoints = rootJointList.toArray(new Joint[rootJointList.size()]); @@ -75,6 +81,27 @@ public class Armature implements JmeCloneable, Savable { } } + /** + * Sets the JointModelTransform implementation + * Default is {@link MatrixJointModelTransform} + * + * @param modelTransformClass + * @see {@link JointModelTransform},{@link MatrixJointModelTransform},{@link SeparateJointModelTransform}, + */ + public void setModelTransformClass(Class modelTransformClass) { + this.modelTransformClass = modelTransformClass; + if (jointList == null) { + return; + } + for (Joint joint : jointList) { + try { + joint.setJointModelTransform(modelTransformClass.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + throw new IllegalArgumentException(e); + } + } + } + /** * returns the array of all root joints of this Armatire * diff --git a/jme3-core/src/main/java/com/jme3/anim/Joint.java b/jme3-core/src/main/java/com/jme3/anim/Joint.java index c2c92693b..c060ae86d 100644 --- a/jme3-core/src/main/java/com/jme3/anim/Joint.java +++ b/jme3-core/src/main/java/com/jme3/anim/Joint.java @@ -1,5 +1,6 @@ package com.jme3.anim; +import com.jme3.anim.util.JointModelTransform; import com.jme3.export.*; import com.jme3.material.MatParamOverride; import com.jme3.math.*; @@ -41,9 +42,9 @@ public class Joint implements Savable, JmeCloneable { /** * The transform of the joint in model space. Relative to the origin of the model. + * this is either a MatrixJointModelTransform or a SeparateJointModelTransform */ - private Transform modelTransform = new Transform(); - private Matrix4f modelTransformMatrix = new Matrix4f(); + private JointModelTransform jointModelTransform; /** * The matrix used to transform affected vertices position into the joint model space. @@ -78,12 +79,7 @@ public class Joint implements Savable, JmeCloneable { * model transform with this bones' local transform. */ public final void updateModelTransforms() { - localTransform.toTransformMatrix(modelTransformMatrix); - if (parent != null) { - parent.modelTransformMatrix.mult(modelTransformMatrix, modelTransformMatrix); - } - modelTransform.fromTransformMatrix(modelTransformMatrix); - + jointModelTransform.updateModelTransform(localTransform, parent); updateAttachNode(); } @@ -102,11 +98,11 @@ public class Joint implements Savable, JmeCloneable { * The animated meshes are in the same coordinate system as the * attachments node: no further transforms are needed. */ - attachedNode.setLocalTransform(modelTransform); + attachedNode.setLocalTransform(getModelTransform()); } else { Spatial loopSpatial = targetGeometry; - Transform combined = modelTransform.clone(); + Transform combined = getModelTransform().clone(); /* * Climb the scene graph applying local transforms until the * attachments node's parent is reached. @@ -131,23 +127,18 @@ public class Joint implements Savable, JmeCloneable { * @param outTransform */ void getOffsetTransform(Matrix4f outTransform) { - outTransform.set(modelTransformMatrix).mult(inverseModelBindMatrix, outTransform); + jointModelTransform.getOffsetTransform(outTransform, inverseModelBindMatrix); } protected void setBindPose() { //Note that the whole Armature must be updated before calling this method. - inverseModelBindMatrix.set(modelTransformMatrix); + getModelTransform().toTransformMatrix(inverseModelBindMatrix); inverseModelBindMatrix.invertLocal(); baseLocalTransform.set(localTransform); } protected void resetToBindPose() { - //just using modelTransformMatrix as a temp matrix here - modelTransformMatrix.set(inverseModelBindMatrix).invertLocal(); // model transform = model bind - if (parent != null) { - parent.modelTransformMatrix.invert().mult(modelTransformMatrix, modelTransformMatrix); - } - localTransform.fromTransformMatrix(modelTransformMatrix); + jointModelTransform.applyBindPose(localTransform, inverseModelBindMatrix, parent); updateModelTransforms(); for (Joint child : children) { @@ -155,6 +146,14 @@ public class Joint implements Savable, JmeCloneable { } } + protected JointModelTransform getJointModelTransform() { + return jointModelTransform; + } + + protected void setJointModelTransform(JointModelTransform jointModelTransform) { + this.jointModelTransform = jointModelTransform; + } + public Vector3f getLocalTranslation() { return localTransform.getTranslation(); } @@ -246,7 +245,7 @@ public class Joint implements Savable, JmeCloneable { } public Transform getModelTransform() { - return modelTransform; + return jointModelTransform.getModelTransform(); } public Matrix4f getInverseModelBindMatrix() { @@ -270,8 +269,8 @@ public class Joint implements Savable, JmeCloneable { this.targetGeometry = cloner.clone(targetGeometry); this.baseLocalTransform = cloner.clone(baseLocalTransform); - this.localTransform = cloner.clone(baseLocalTransform); - this.modelTransform = cloner.clone(baseLocalTransform); + this.localTransform = cloner.clone(localTransform); + this.jointModelTransform = cloner.clone(jointModelTransform); this.inverseModelBindMatrix = cloner.clone(inverseModelBindMatrix); } @@ -287,6 +286,7 @@ public class Joint implements Savable, JmeCloneable { baseLocalTransform = (Transform) input.readSavable("baseLocalTransforms", baseLocalTransform); localTransform.set(baseLocalTransform); inverseModelBindMatrix = (Matrix4f) input.readSavable("inverseModelBindMatrix", inverseModelBindMatrix); + jointModelTransform = (JointModelTransform) input.readSavable("jointModelTransform", null); ArrayList childList = input.readSavableArrayList("children", null); for (int i = childList.size() - 1; i >= 0; i--) { @@ -304,6 +304,7 @@ public class Joint implements Savable, JmeCloneable { output.write(baseLocalTransform, "baseLocalTransform", new Transform()); output.write(inverseModelBindMatrix, "inverseModelBindMatrix", new Matrix4f()); output.writeSavableArrayList(children, "children", null); + output.write(jointModelTransform, "jointModelTransform", null); } } diff --git a/jme3-core/src/main/java/com/jme3/anim/MatrixJointModelTransform.java b/jme3-core/src/main/java/com/jme3/anim/MatrixJointModelTransform.java new file mode 100644 index 000000000..9c141b5ff --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/MatrixJointModelTransform.java @@ -0,0 +1,78 @@ +package com.jme3.anim; + +import com.jme3.anim.util.JointModelTransform; +import com.jme3.export.*; +import com.jme3.math.Matrix4f; +import com.jme3.math.Transform; +import com.jme3.util.clone.Cloner; + +import java.io.IOException; + +/** + * This JointModelTransform implementation accumulate joints transforms in a Matrix4f to properly + * support non uniform scaling in an armature hierarchy + */ +public class MatrixJointModelTransform implements JointModelTransform { + + private Matrix4f modelTransformMatrix = new Matrix4f(); + private Transform modelTransform = new Transform(); + + @Override + public void updateModelTransform(Transform localTransform, Joint parent) { + localTransform.toTransformMatrix(modelTransformMatrix); + if (parent != null) { + ((MatrixJointModelTransform) parent.getJointModelTransform()).getModelTransformMatrix().mult(modelTransformMatrix, modelTransformMatrix); + } + modelTransform.fromTransformMatrix(modelTransformMatrix); + } + + public void getOffsetTransform(Matrix4f outTransform, Matrix4f inverseModelBindMatrix) { + outTransform.set(modelTransformMatrix).mult(inverseModelBindMatrix, outTransform); + } + + @Override + public void applyBindPose(Transform localTransform, Matrix4f inverseModelBindMatrix, Joint parent) { + modelTransformMatrix.set(inverseModelBindMatrix).invertLocal(); // model transform = model bind + if (parent != null) { + ((MatrixJointModelTransform) parent.getJointModelTransform()).getModelTransformMatrix().invert().mult(modelTransformMatrix, modelTransformMatrix); + } + localTransform.fromTransformMatrix(modelTransformMatrix); + } + + public Matrix4f getModelTransformMatrix() { + return modelTransformMatrix; + } + + @Override + public Transform getModelTransform() { + return modelTransform; + } + + @Override + public void write(JmeExporter ex) throws IOException { + OutputCapsule oc = ex.getCapsule(this); + oc.write(modelTransformMatrix, "modelTransformMatrix", new Matrix4f()); + } + + @Override + public void read(JmeImporter im) throws IOException { + InputCapsule ic = im.getCapsule(this); + modelTransformMatrix = (Matrix4f) ic.readSavable("modelTransformMatrix", new Matrix4f()); + modelTransform.fromTransformMatrix(modelTransformMatrix); + } + + @Override + public Object jmeClone() { + try { + MatrixJointModelTransform clone = (MatrixJointModelTransform) super.clone(); + return clone; + } catch (CloneNotSupportedException ex) { + throw new AssertionError(); + } + } + + @Override + public void cloneFields(Cloner cloner, Object original) { + modelTransformMatrix = modelTransformMatrix.clone(); + } +} diff --git a/jme3-core/src/main/java/com/jme3/anim/SeparateJointModelTransform.java b/jme3-core/src/main/java/com/jme3/anim/SeparateJointModelTransform.java new file mode 100644 index 000000000..f5089f3d7 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/SeparateJointModelTransform.java @@ -0,0 +1,74 @@ +package com.jme3.anim; + +import com.jme3.anim.util.JointModelTransform; +import com.jme3.export.*; +import com.jme3.math.Matrix4f; +import com.jme3.math.Transform; +import com.jme3.util.clone.Cloner; + +import java.io.IOException; + +/** + * This JointModelTransform implementation accumulates model transform in a Transform class + * This does NOT support proper non uniform scale in the armature hierarchy. + * But the effect might be useful in some circumstances. + * Note that this is how the old animation system was working, so you might want to use this + * if your model has non uniform scale and was migrated from old j3o model. + */ +public class SeparateJointModelTransform implements JointModelTransform { + + private Transform modelTransform = new Transform(); + + @Override + public void updateModelTransform(Transform localTransform, Joint parent) { + modelTransform.set(localTransform); + if (parent != null) { + modelTransform.combineWithParent(parent.getModelTransform()); + } + } + + public void getOffsetTransform(Matrix4f outTransform, Matrix4f inverseModelBindMatrix) { + modelTransform.toTransformMatrix(outTransform).mult(inverseModelBindMatrix, outTransform); + } + + @Override + public void applyBindPose(Transform localTransform, Matrix4f inverseModelBindMatrix, Joint parent) { + localTransform.fromTransformMatrix(inverseModelBindMatrix); + localTransform.invert(); //model transform + if (parent != null) { + localTransform.combineWithParent(parent.getModelTransform().invert()); + } + } + + @Override + public Transform getModelTransform() { + return modelTransform; + } + + @Override + public void write(JmeExporter ex) throws IOException { + OutputCapsule oc = ex.getCapsule(this); + oc.write(modelTransform, "modelTransform", new Transform()); + } + + @Override + public void read(JmeImporter im) throws IOException { + InputCapsule ic = im.getCapsule(this); + modelTransform = (Transform) ic.readSavable("modelTransform", new Transform()); + } + + @Override + public Object jmeClone() { + try { + SeparateJointModelTransform clone = (SeparateJointModelTransform) super.clone(); + return clone; + } catch (CloneNotSupportedException ex) { + throw new AssertionError(); + } + } + + @Override + public void cloneFields(Cloner cloner, Object original) { + modelTransform = modelTransform.clone(); + } +} diff --git a/jme3-core/src/main/java/com/jme3/anim/util/JointModelTransform.java b/jme3-core/src/main/java/com/jme3/anim/util/JointModelTransform.java new file mode 100644 index 000000000..48cc68c67 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/util/JointModelTransform.java @@ -0,0 +1,22 @@ +package com.jme3.anim.util; + +import com.jme3.anim.Joint; +import com.jme3.export.Savable; +import com.jme3.math.Matrix4f; +import com.jme3.math.Transform; +import com.jme3.util.clone.JmeCloneable; + +/** + * Implementations of this interface holds accumulated model transform of a Joint. + * Implementation might choose different accumulation strategy. + */ +public interface JointModelTransform extends JmeCloneable, Savable { + + void updateModelTransform(Transform localTransform, Joint parent); + + void getOffsetTransform(Matrix4f outTransform, Matrix4f inverseModelBindMatrix); + + void applyBindPose(Transform localTransform, Matrix4f inverseModelBindMatrix, Joint parent); + + Transform getModelTransform(); +} 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 a3d4210d0..ba0d96510 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java @@ -51,6 +51,8 @@ public class TestArmature extends SimpleApplication { Joint[] joints = new Joint[]{root, j1, j2, j3}; final Armature armature = new Armature(joints); +// armature.setModelTransformClass(SeparateJointModelTransform.class); + armature.setBindPose(); AnimClip clip = new AnimClip("anim");