Feature: improved IK algorithm by taking axis locks for bones into

consideration. Also rotation limits, stretch factor and stiffness are
also loaded from the blend file to be used later.
experimental
jmekaelthas 10 years ago
parent de2d7eebf7
commit 34cdd21488
  1. 124
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java
  2. 22
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java

@ -13,6 +13,8 @@ import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.DynamicArray;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
@ -33,6 +35,13 @@ public class BoneContext {
*/
public 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 static final int IKFLAG_LOCK_X = 0x01;
private static final int IKFLAG_LOCK_Y = 0x02;
private static final int IKFLAG_LOCK_Z = 0x04;
private static final int IKFLAG_LIMIT_X = 0x08;
private static final int IKFLAG_LIMIT_Y = 0x10;
private static final int IKFLAG_LIMIT_Z = 0x20;
private BlenderContext blenderContext;
/** The OMA of the bone's armature object. */
private Long armatureObjectOMA;
@ -58,6 +67,21 @@ public class BoneContext {
private float length;
/** The bone's deform envelope. */
private BoneEnvelope boneEnvelope;
// The below data is used only for IK constraint computations.
/** The bone's stretch value. */
private float ikStretch;
/** Bone's rotation minimum values. */
private Vector3f limitMin;
/** Bone's rotation maximum values. */
private Vector3f limitMax;
/** The bone's stiffness values (how much it rotates during IK computations. */
private Vector3f stiffness;
/** Values that indicate if any axis' rotation should be limited by some angle. */
private boolean[] limits;
/** Values that indicate if any axis' rotation should be disabled during IK computations. */
private boolean[] locks;
/**
* Constructor. Creates the basic set of bone's data.
@ -91,6 +115,7 @@ public class BoneContext {
* an exception is thrown when problem with blender data reading
* occurs
*/
@SuppressWarnings("unchecked")
private BoneContext(Structure boneStructure, Long armatureObjectOMA, BoneContext parent, BlenderContext blenderContext) throws BlenderFileException {
this.parent = parent;
this.blenderContext = blenderContext;
@ -108,7 +133,8 @@ public class BoneContext {
globalBoneMatrix.multLocal(BONE_ARMATURE_TRANSFORMATION_MATRIX);
}
Spatial armature = (Spatial) objectHelper.toObject(blenderContext.getFileBlock(armatureObjectOMA).getStructure(blenderContext), blenderContext);
Structure armatureStructure = blenderContext.getFileBlock(armatureObjectOMA).getStructure(blenderContext);
Spatial armature = (Spatial) objectHelper.toObject(armatureStructure, blenderContext);
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
Matrix4f armatureWorldMatrix = constraintHelper.toMatrix(armature.getWorldTransform(), new Matrix4f());
@ -120,6 +146,32 @@ public class BoneContext {
boneEnvelope = new BoneEnvelope(boneStructure, armatureWorldMatrix, blenderContext.getBlenderKey().isFixUpAxis());
}
// load bone's pose channel data
Pointer pPose = (Pointer) armatureStructure.getFieldValue("pose");
if (pPose != null && pPose.isNotNull()) {
List<Structure> poseChannels = ((Structure) pPose.fetchData().get(0).getFieldValue("chanbase")).evaluateListBase();
for (Structure poseChannel : poseChannels) {
Long boneOMA = ((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress();
if (boneOMA.equals(this.boneStructure.getOldMemoryAddress())) {
ikStretch = ((Number) poseChannel.getFieldValue("ikstretch")).floatValue();
DynamicArray<Number> limitMin = (DynamicArray<Number>) poseChannel.getFieldValue("limitmin");
this.limitMin = new Vector3f(limitMin.get(0).floatValue(), limitMin.get(1).floatValue(), limitMin.get(2).floatValue());
DynamicArray<Number> limitMax = (DynamicArray<Number>) poseChannel.getFieldValue("limitmax");
this.limitMax = new Vector3f(limitMax.get(0).floatValue(), limitMax.get(1).floatValue(), limitMax.get(2).floatValue());
DynamicArray<Number> stiffness = (DynamicArray<Number>) poseChannel.getFieldValue("stiffness");
this.stiffness = new Vector3f(stiffness.get(0).floatValue(), stiffness.get(1).floatValue(), stiffness.get(2).floatValue());
int ikFlag = ((Number) poseChannel.getFieldValue("ikflag")).intValue();
locks = new boolean[] { (ikFlag & IKFLAG_LOCK_X) != 0, (ikFlag & IKFLAG_LOCK_Y) != 0, (ikFlag & IKFLAG_LOCK_Z) != 0 };
// limits are enabled when locks are disabled, so we ween to take that into account here
limits = new boolean[] { (ikFlag & IKFLAG_LIMIT_X & ~IKFLAG_LOCK_X) != 0, (ikFlag & IKFLAG_LIMIT_Y & ~IKFLAG_LOCK_Y) != 0, (ikFlag & IKFLAG_LIMIT_Z & ~IKFLAG_LOCK_Z) != 0 };
break;// we have found what we need, no need to search further
}
}
}
// create the children
List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase();
for (Structure child : childbase) {
@ -234,6 +286,76 @@ public class BoneContext {
return boneEnvelope;
}
/**
* @return bone's stretch factor
*/
public float getIkStretch() {
return ikStretch;
}
/**
* @return indicates if the X rotation should be limited
*/
public boolean isLimitX() {
return limits != null ? limits[0] : false;
}
/**
* @return indicates if the Y rotation should be limited
*/
public boolean isLimitY() {
return limits != null ? limits[1] : false;
}
/**
* @return indicates if the Z rotation should be limited
*/
public boolean isLimitZ() {
return limits != null ? limits[2] : false;
}
/**
* @return indicates if the X rotation should be disabled
*/
public boolean isLockX() {
return locks != null ? locks[0] : false;
}
/**
* @return indicates if the Y rotation should be disabled
*/
public boolean isLockY() {
return locks != null ? locks[1] : false;
}
/**
* @return indicates if the Z rotation should be disabled
*/
public boolean isLockZ() {
return locks != null ? locks[2] : false;
}
/**
* @return the minimum values in rotation limitation (if limitation is enabled for specific axis).
*/
public Vector3f getLimitMin() {
return limitMin;
}
/**
* @return the maximum values in rotation limitation (if limitation is enabled for specific axis).
*/
public Vector3f getLimitMax() {
return limitMax;
}
/**
* @return the stiffness of the bone
*/
public Vector3f getStiffness() {
return stiffness;
}
/**
* Tells if the bone is of specified property defined by its flag.
* @param flagMask

@ -81,6 +81,15 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
if (angle != 0) {
Vector3f cross = currentDir.crossLocal(target).normalizeLocal();
q.fromAngleAxis(angle, cross);
if(boneContext.isLockX()) {
q.set(0, q.getY(), q.getZ(), q.getW());
}
if(boneContext.isLockY()) {
q.set(q.getX(), 0, q.getZ(), q.getW());
}
if(boneContext.isLockZ()) {
q.set(q.getX(), q.getY(), 0, q.getW());
}
boneTransform.getRotation().set(q.multLocal(boneTransform.getRotation()));
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneTransform);
@ -97,7 +106,7 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
Bone bone = boneContext.getBone();
Transform topBoneTransform = constraintHelper.getTransform(topBone.getArmatureObjectOMA(), topBone.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD);
Transform boneWorldTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD);
Vector3f e = topBoneTransform.getTranslation().addLocal(topBoneTransform.getRotation().mult(Vector3f.UNIT_Y).multLocal(topBone.getLength()));// effector
Vector3f j = boneWorldTransform.getTranslation(); // current join position
@ -108,6 +117,16 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
Vector3f cross = currentDir.crossLocal(target).normalizeLocal();
q.fromAngleAxis(angle, cross);
if(boneContext.isLockX()) {
q.set(0, q.getY(), q.getZ(), q.getW());
}
if(boneContext.isLockY()) {
q.set(q.getX(), 0, q.getZ(), q.getW());
}
if(boneContext.isLockZ()) {
q.set(q.getX(), q.getY(), 0, q.getW());
}
boneWorldTransform.getRotation().set(q.multLocal(boneWorldTransform.getRotation()));
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneWorldTransform);
}
@ -130,6 +149,7 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
private List<BoneContext> loadBones() {
List<BoneContext> bones = new ArrayList<BoneContext>();
Bone bone = (Bone) this.getOwner();
chainLength = 0;
while (bone != null) {
BoneContext boneContext = blenderContext.getBoneContext(bone);
chainLength += boneContext.getLength();

Loading…
Cancel
Save