|
@@ -8,7 +8,10 @@ import com.jme3.animation.Skeleton;
|
|
|
import com.jme3.math.Matrix4f;
|
|
|
import com.jme3.math.Quaternion;
|
|
|
import com.jme3.math.Vector3f;
|
|
|
+import com.jme3.scene.Spatial;
|
|
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
|
|
+import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
|
|
|
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
|
|
|
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
|
|
import com.jme3.scene.plugins.blender.file.Structure;
|
|
|
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
|
@@ -19,27 +22,36 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
|
|
* @author Marcin Roguski (Kaelthas)
|
|
|
*/
|
|
|
public class BoneContext {
|
|
|
- private BlenderContext blenderContext;
|
|
|
+ // the flags of the bone
|
|
|
+ private static final int CONNECTED_TO_PARENT = 0x10;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The bones' matrices have, unlike objects', the coordinate system identical to JME's (Y axis is UP, X to the right and Z toward us).
|
|
|
+ * So in order to have them loaded properly we need to transform their armature matrix (which blender sees as rotated) to make sure we get identical results.
|
|
|
+ */
|
|
|
+ private static final Matrix4f BONE_ARMATURE_TRANSFORMATION_MATRIX = new Matrix4f(1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1);
|
|
|
+
|
|
|
+ private BlenderContext blenderContext;
|
|
|
/** The OMA of the bone's armature object. */
|
|
|
- private Long armatureObjectOMA;
|
|
|
+ private Long armatureObjectOMA;
|
|
|
/** The structure of the bone. */
|
|
|
- private Structure boneStructure;
|
|
|
+ private Structure boneStructure;
|
|
|
/** Bone's name. */
|
|
|
- private String boneName;
|
|
|
- /** The bone's armature matrix. */
|
|
|
- private Matrix4f armatureMatrix;
|
|
|
+ private String boneName;
|
|
|
+ /** The bone's flag. */
|
|
|
+ private int flag;
|
|
|
+ /** The bone's matrix in world space. */
|
|
|
+ private Matrix4f globalBoneMatrix;
|
|
|
+ /** The bone's matrix in the model space. */
|
|
|
+ private Matrix4f boneMatrixInModelSpace;
|
|
|
/** The parent context. */
|
|
|
- private BoneContext parent;
|
|
|
+ private BoneContext parent;
|
|
|
/** The children of this context. */
|
|
|
- private List<BoneContext> children = new ArrayList<BoneContext>();
|
|
|
+ private List<BoneContext> children = new ArrayList<BoneContext>();
|
|
|
/** Created bone (available after calling 'buildBone' method). */
|
|
|
- private Bone bone;
|
|
|
- /** The bone's rest matrix. */
|
|
|
- private Matrix4f restMatrix;
|
|
|
- /** Bone's total inverse transformation. */
|
|
|
- private Matrix4f inverseTotalTransformation;
|
|
|
+ private Bone bone;
|
|
|
/** The length of the bone. */
|
|
|
- private float length;
|
|
|
+ private float length;
|
|
|
|
|
|
/**
|
|
|
* Constructor. Creates the basic set of bone's data.
|
|
@@ -79,21 +91,26 @@ public class BoneContext {
|
|
|
this.boneStructure = boneStructure;
|
|
|
this.armatureObjectOMA = armatureObjectOMA;
|
|
|
boneName = boneStructure.getFieldValue("name").toString();
|
|
|
+ flag = ((Number) boneStructure.getFieldValue("flag")).intValue();
|
|
|
length = ((Number) boneStructure.getFieldValue("length")).floatValue();
|
|
|
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
|
|
- armatureMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", blenderContext.getBlenderKey().isFixUpAxis());
|
|
|
|
|
|
- // compute the bone's rest matrix
|
|
|
- restMatrix = armatureMatrix.clone();
|
|
|
- inverseTotalTransformation = restMatrix.invert();
|
|
|
- if (parent != null) {
|
|
|
- restMatrix = parent.inverseTotalTransformation.mult(restMatrix);
|
|
|
- }
|
|
|
+ // first get the bone matrix in its armature space
|
|
|
+ globalBoneMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", blenderContext.getBlenderKey().isFixUpAxis());
|
|
|
+ // then make sure it is rotated in a proper way to fit the jme bone transformation conventions
|
|
|
+ globalBoneMatrix.multLocal(BONE_ARMATURE_TRANSFORMATION_MATRIX);
|
|
|
+
|
|
|
+ Spatial armature = (Spatial) blenderContext.getLoadedFeature(armatureObjectOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
|
|
+ ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
|
|
+ Matrix4f armatureWorldMatrix = constraintHelper.toMatrix(armature.getWorldTransform());
|
|
|
+
|
|
|
+ // and now compute the final bone matrix in world space
|
|
|
+ globalBoneMatrix = armatureWorldMatrix.mult(globalBoneMatrix);
|
|
|
|
|
|
// create the children
|
|
|
List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext);
|
|
|
for (Structure child : childbase) {
|
|
|
- this.children.add(new BoneContext(child, armatureObjectOMA, this, blenderContext));
|
|
|
+ children.add(new BoneContext(child, armatureObjectOMA, this, blenderContext));
|
|
|
}
|
|
|
|
|
|
blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this);
|
|
@@ -104,31 +121,36 @@ public class BoneContext {
|
|
|
*
|
|
|
* @param bones
|
|
|
* a list of bones where the newly created bone will be added
|
|
|
- * @param objectToArmatureMatrix
|
|
|
- * object to armature transformation matrix
|
|
|
+ * @param skeletonOwnerOma
|
|
|
+ * the spatial of the object that will own the skeleton
|
|
|
* @param blenderContext
|
|
|
* the blender context
|
|
|
* @return newly created bone
|
|
|
*/
|
|
|
- public Bone buildBone(List<Bone> bones, Matrix4f objectToArmatureMatrix, BlenderContext blenderContext) {
|
|
|
+ public Bone buildBone(List<Bone> bones, Long skeletonOwnerOma, BlenderContext blenderContext) {
|
|
|
Long boneOMA = boneStructure.getOldMemoryAddress();
|
|
|
bone = new Bone(boneName);
|
|
|
bones.add(bone);
|
|
|
blenderContext.addLoadedFeatures(boneOMA, boneName, boneStructure, bone);
|
|
|
+ ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
|
|
|
|
|
- Vector3f poseLocation = restMatrix.toTranslationVector();
|
|
|
- Quaternion rotation = restMatrix.toRotationQuat().normalizeLocal();
|
|
|
- Vector3f scale = restMatrix.toScaleVector();
|
|
|
- if (parent == null) {
|
|
|
- Quaternion rotationQuaternion = objectToArmatureMatrix.toRotationQuat().normalizeLocal();
|
|
|
- scale.multLocal(objectToArmatureMatrix.toScaleVector());
|
|
|
- rotationQuaternion.multLocal(poseLocation.addLocal(objectToArmatureMatrix.toTranslationVector()));
|
|
|
- rotation.multLocal(rotationQuaternion);
|
|
|
+ Structure skeletonOwnerObjectStructure = (Structure) blenderContext.getLoadedFeature(skeletonOwnerOma, LoadedFeatureDataType.LOADED_STRUCTURE);
|
|
|
+ Matrix4f invertedObjectOwnerGlobalMatrix = objectHelper.getMatrix(skeletonOwnerObjectStructure, "imat", blenderContext.getBlenderKey().isFixUpAxis());
|
|
|
+ if (objectHelper.isParent(skeletonOwnerOma, armatureObjectOMA)) {
|
|
|
+ boneMatrixInModelSpace = globalBoneMatrix.mult(invertedObjectOwnerGlobalMatrix);
|
|
|
+ } else {
|
|
|
+ boneMatrixInModelSpace = invertedObjectOwnerGlobalMatrix.mult(globalBoneMatrix);
|
|
|
}
|
|
|
|
|
|
+ Matrix4f boneLocalMatrix = parent == null ? boneMatrixInModelSpace : parent.boneMatrixInModelSpace.invert().multLocal(boneMatrixInModelSpace);
|
|
|
+
|
|
|
+ Vector3f poseLocation = parent == null || !this.is(CONNECTED_TO_PARENT) ? boneLocalMatrix.toTranslationVector() : new Vector3f(0, parent.length, 0);
|
|
|
+ Quaternion rotation = boneLocalMatrix.toRotationQuat().normalizeLocal();
|
|
|
+ Vector3f scale = boneLocalMatrix.toScaleVector();
|
|
|
+
|
|
|
bone.setBindTransforms(poseLocation, rotation, scale);
|
|
|
for (BoneContext child : children) {
|
|
|
- bone.addChild(child.buildBone(bones, objectToArmatureMatrix, blenderContext));
|
|
|
+ bone.addChild(child.buildBone(bones, skeletonOwnerOma, blenderContext));
|
|
|
}
|
|
|
|
|
|
return bone;
|
|
@@ -168,4 +190,14 @@ public class BoneContext {
|
|
|
public Skeleton getSkeleton() {
|
|
|
return blenderContext.getSkeleton(armatureObjectOMA);
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Tells if the bone is of specified property defined by its flag.
|
|
|
+ * @param flagMask
|
|
|
+ * the mask of the flag (constants defined in this class)
|
|
|
+ * @return <b>true</b> if the bone IS of specified proeprty and <b>false</b> otherwise
|
|
|
+ */
|
|
|
+ private boolean is(int flagMask) {
|
|
|
+ return (flag & flagMask) != 0;
|
|
|
+ }
|
|
|
}
|