- fixed issues with bones transformation applying and reading in world space
- fixed issue with lack of animation track for some bones whose constraints affected the animation

Refactoring:
- improved (and simplified) bone loading

Features:
- added basic implementation of the IK constraint (still a lot to be done but works for simplest cases)

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10845 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
experimental
Kae..pl 11 years ago
parent 0ab43e0649
commit 0921540124
  1. 15
      engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java
  2. 27
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java
  3. 37
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java
  4. 29
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
  5. 100
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/SimulationNode.java
  6. 9
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/SpatialConstraint.java
  7. 31
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java
  8. 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDistLimit.java
  9. 2
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java
  10. 115
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java
  11. 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLike.java
  12. 13
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLimit.java
  13. 3
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionNull.java
  14. 9
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLike.java
  15. 11
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLimit.java
  16. 11
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLike.java
  17. 12
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLimit.java
  18. 3
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/UnsupportedConstraintDefinition.java

@ -34,6 +34,8 @@ public class BoneContext {
private BlenderContext blenderContext;
/** The OMA of the bone's armature object. */
private Long armatureObjectOMA;
/** The OMA of the model that owns the bone's skeleton. */
private Long skeletonOwnerOma;
/** The structure of the bone. */
private Structure boneStructure;
/** Bone's name. */
@ -128,6 +130,7 @@ public class BoneContext {
* @return newly created bone
*/
public Bone buildBone(List<Bone> bones, Long skeletonOwnerOma, BlenderContext blenderContext) {
this.skeletonOwnerOma = skeletonOwnerOma;
Long boneOMA = boneStructure.getOldMemoryAddress();
bone = new Bone(boneName);
bones.add(bone);
@ -183,6 +186,13 @@ public class BoneContext {
public Long getArmatureObjectOMA() {
return armatureObjectOMA;
}
/**
* @return the OMA of the model that owns the bone's skeleton
*/
public Long getSkeletonOwnerOma() {
return skeletonOwnerOma;
}
/**
* @return the skeleton the bone of this context belongs to
@ -200,4 +210,9 @@ public class BoneContext {
private boolean is(int flagMask) {
return (flag & flagMask) != 0;
}
@Override
public String toString() {
return "BoneContext: " + bone.getName();
}
}

@ -3,7 +3,6 @@ package com.jme3.scene.plugins.blender.constraints;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.math.Transform;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
@ -21,8 +20,6 @@ import com.jme3.scene.plugins.blender.file.Structure;
/* package */class BoneConstraint extends Constraint {
private static final Logger LOGGER = Logger.getLogger(BoneConstraint.class.getName());
protected boolean isNodeTarget;
/**
* The bone constraint constructor.
*
@ -45,14 +42,14 @@ import com.jme3.scene.plugins.blender.file.Structure;
public boolean validate() {
if (targetOMA != null) {
Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
if(nodeTarget == null) {
if (nodeTarget == null) {
LOGGER.log(Level.WARNING, "Cannot find target for constraint: {0}.", name);
return false;
}
// the second part of the if expression verifies if the found node
// (if any) is an armature node
if (blenderContext.getMarkerValue(ArmatureHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) {
if(subtargetName.trim().isEmpty()) {
if (subtargetName.trim().isEmpty()) {
LOGGER.log(Level.WARNING, "No bone target specified for constraint: {0}.", name);
return false;
}
@ -63,28 +60,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
LOGGER.log(Level.WARNING, "Bone constraint {0} must target bone in the its own skeleton! Targeting bone in another skeleton is not supported!", name);
return false;
}
} else {
isNodeTarget = true;
}
}
return true;
}
@Override
public void apply(int frame) {
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
if (targetOMA != null) {
if (isNodeTarget) {
Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
constraintDefinition.bake(ownerTransform, targetTransform, this.ipo.calculateValue(frame));
} else {
Transform targetTransform = constraintHelper.getTransform(targetOMA, subtargetName, targetSpace);
constraintDefinition.bake(ownerTransform, targetTransform, this.ipo.calculateValue(frame));
}
} else {
constraintDefinition.bake(ownerTransform, null, this.ipo.calculateValue(frame));
}
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
}
}

@ -1,8 +1,10 @@
package com.jme3.scene.plugins.blender.constraints;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.math.Transform;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
@ -58,15 +60,15 @@ public abstract class Constraint {
*/
public Constraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
this.blenderContext = blenderContext;
this.name = constraintStructure.getFieldValue("name").toString();
name = constraintStructure.getFieldValue("name").toString();
Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
if (pData.isNotNull()) {
Structure data = pData.fetchData(blenderContext.getInputStream()).get(0);
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, ownerOMA, blenderContext);
Pointer pTar = (Pointer) data.getFieldValue("tar");
if (pTar != null && pTar.isNotNull()) {
this.targetOMA = pTar.getOldMemoryAddress();
this.targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
targetOMA = pTar.getOldMemoryAddress();
targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
Object subtargetValue = data.getFieldValue("subtarget");
if (subtargetValue != null) {// not all constraint data have the
// subtarget field
@ -77,10 +79,10 @@ public abstract class Constraint {
// Null constraint has no data, so create it here
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, null, blenderContext);
}
this.ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
this.ipo = influenceIpo;
ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
ipo = influenceIpo;
this.ownerOMA = ownerOMA;
this.constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
LOGGER.log(Level.INFO, "Created constraint: {0} with definition: {1}", new Object[] { name, constraintDefinition });
}
@ -100,6 +102,13 @@ public abstract class Constraint {
return constraintDefinition.getConstraintTypeName();
}
/**
* @return the OMAs of the features whose transform had been altered beside the constraint owner
*/
public Set<Long> getAlteredOmas() {
return constraintDefinition.getAlteredOmas();
}
/**
* Performs validation before baking. Checks factors that can prevent
* constraint from baking that could not be checked during constraint
@ -107,14 +116,22 @@ public abstract class Constraint {
*/
public abstract boolean validate();
public abstract void apply(int frame);
/**
* Applies the constraint to owner (and in some cases can alter other bones of the skeleton).
* @param frame
* the frame of the animation
*/
public void apply(int frame) {
Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
constraintDefinition.bake(ownerSpace, targetSpace, targetTransform, ipo.calculateValue(frame));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((ownerOMA == null) ? 0 : ownerOMA.hashCode());
result = prime * result + (name == null ? 0 : name.hashCode());
result = prime * result + (ownerOMA == null ? 0 : ownerOMA.hashCode());
return result;
}
@ -126,7 +143,7 @@ public abstract class Constraint {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
if (this.getClass() != obj.getClass()) {
return false;
}
Constraint other = (Constraint) obj;

@ -228,11 +228,14 @@ public class ConstraintHelper extends AbstractBlenderHelper {
switch (space) {
case CONSTRAINT_SPACE_WORLD:
return new Transform(bone.getModelSpacePosition(), bone.getModelSpaceRotation(), bone.getModelSpaceScale());
Spatial model = (Spatial) blenderContext.getLoadedFeature(targetBoneContext.getSkeletonOwnerOma(), LoadedFeatureDataType.LOADED_FEATURE);
Transform worldTransform = new Transform(bone.getModelSpacePosition(), bone.getModelSpaceRotation(), bone.getModelSpaceScale());
worldTransform.getTranslation().addLocal(model.getWorldTranslation());
worldTransform.getRotation().multLocal(model.getWorldRotation());
worldTransform.getScale().multLocal(model.getWorldScale());
return worldTransform;
case CONSTRAINT_SPACE_LOCAL:
Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
localTransform.setScale(bone.getLocalScale());
return localTransform;
return new Transform(bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale());
case CONSTRAINT_SPACE_POSE:
Node nodeWithAnimationControl = blenderContext.getControlledNode(targetBoneContext.getSkeleton());
Matrix4f m = this.toMatrix(nodeWithAnimationControl.getWorldTransform());
@ -313,15 +316,16 @@ public class ConstraintHelper extends AbstractBlenderHelper {
bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
break;
case CONSTRAINT_SPACE_WORLD:
Matrix4f boneMatrix = this.toMatrix(transform);
Matrix4f boneMatrixInWorldSpace = this.toMatrix(transform);
Matrix4f invertedModelMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD)).invertLocal();
Matrix4f boneMatrixInModelSpace = invertedModelMatrix.mult(boneMatrixInWorldSpace);
Bone parent = bone.getParent();
if (parent != null) {
Matrix4f invertedParentWorldMatrix = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale()).invertLocal();
boneMatrix = invertedParentWorldMatrix.multLocal(boneMatrix);
}
Matrix4f invertedParentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale()).invertLocal();
boneMatrix = invertedNodeMatrix.multLocal(boneMatrix);
bone.setBindTransforms(boneMatrix.toTranslationVector(), boneMatrix.toRotationQuat(), boneMatrix.toScaleVector());
boneMatrixInModelSpace = invertedParentMatrixInModelSpace.mult(boneMatrixInModelSpace);
}
bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector());
break;
case CONSTRAINT_SPACE_POSE:
Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform());
@ -391,11 +395,10 @@ public class ConstraintHelper extends AbstractBlenderHelper {
* @return 4x4 matrix that represents the given transform
*/
public Matrix4f toMatrix(Transform transform) {
Matrix4f result = Matrix4f.IDENTITY;
if (transform != null) {
result = this.toMatrix(transform.getTranslation(), transform.getRotation(), transform.getScale());
return this.toMatrix(transform.getTranslation(), transform.getRotation(), transform.getScale());
}
return result;
return Matrix4f.IDENTITY.clone();
}
/**

@ -2,9 +2,11 @@ package com.jme3.scene.plugins.blender.constraints;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -38,31 +40,33 @@ import com.jme3.util.TempVars;
* @author Marcin Roguski (Kaelthas)
*/
public class SimulationNode {
private static final Logger LOGGER = Logger.getLogger(SimulationNode.class.getName());
private static final Logger LOGGER = Logger.getLogger(SimulationNode.class.getName());
/** The blender context. */
private BlenderContext blenderContext;
/** The name of the node (for debugging purposes). */
private String name;
private String name;
/** A list of children for the node (either bones or child spatials). */
private List<SimulationNode> children = new ArrayList<SimulationNode>();
private List<SimulationNode> children = new ArrayList<SimulationNode>();
/** A list of constraints that the current node has. */
private List<Constraint> constraints;
private List<Constraint> constraints;
/** A list of node's animations. */
private List<Animation> animations;
private List<Animation> animations;
/** The nodes spatial (if null then the boneContext should be set). */
private Spatial spatial;
private Spatial spatial;
/** The skeleton of the bone (not null if the node simulated the bone). */
private Skeleton skeleton;
private Skeleton skeleton;
/** Animation controller for the node's feature. */
private AnimControl animControl;
private AnimControl animControl;
/**
* The star transform of a spatial. Needed to properly reset the spatial to
* its start position.
*/
private Transform spatialStartTransform;
private Transform spatialStartTransform;
/** Star transformations for bones. Needed to properly reset the bones. */
private Map<Bone, Transform> boneStartTransforms;
private Map<Bone, Transform> boneStartTransforms;
/**
* Builds the nodes tree for the given feature. The feature (bone or
@ -89,12 +93,13 @@ public class SimulationNode {
* indicates if the feature is a root bone or root spatial or not
*/
private SimulationNode(Long featureOMA, BlenderContext blenderContext, boolean rootNode) {
this.blenderContext = blenderContext;
Node spatial = (Node) blenderContext.getLoadedFeature(featureOMA, LoadedFeatureDataType.LOADED_FEATURE);
if (blenderContext.getMarkerValue(ArmatureHelper.ARMATURE_NODE_MARKER, spatial) != null) {
this.skeleton = blenderContext.getSkeleton(featureOMA);
skeleton = blenderContext.getSkeleton(featureOMA);
Node nodeWithAnimationControl = blenderContext.getControlledNode(skeleton);
this.animControl = nodeWithAnimationControl.getControl(AnimControl.class);
animControl = nodeWithAnimationControl.getControl(AnimControl.class);
boneStartTransforms = new HashMap<Bone, Transform>();
for (int i = 0; i < skeleton.getBoneCount(); ++i) {
@ -106,10 +111,10 @@ public class SimulationNode {
throw new IllegalStateException("Given spatial must be a root node!");
}
this.spatial = spatial;
this.spatialStartTransform = spatial.getLocalTransform().clone();
spatialStartTransform = spatial.getLocalTransform().clone();
}
this.name = '>' + spatial.getName() + '<';
name = '>' + spatial.getName() + '<';
constraints = this.findConstraints(featureOMA, blenderContext);
if (constraints == null) {
@ -143,14 +148,14 @@ public class SimulationNode {
LOGGER.info("Removing invalid constraints.");
List<Constraint> validConstraints = new ArrayList<Constraint>(constraints.size());
for (Constraint constraint : this.constraints) {
for (Constraint constraint : constraints) {
if (constraint.validate()) {
validConstraints.add(constraint);
} else {
LOGGER.log(Level.WARNING, "Constraint {0} is invalid and will not be applied.", constraint.name);
}
}
this.constraints = validConstraints;
constraints = validConstraints;
}
/**
@ -246,6 +251,7 @@ public class SimulationNode {
private void simulateSkeleton() {
if (constraints != null && constraints.size() > 0) {
boolean applyStaticConstraints = true;
Set<Long> alteredOmas = new HashSet<Long>();
if (animations != null) {
TempVars vars = TempVars.get();
@ -256,49 +262,45 @@ public class SimulationNode {
float maxTime = animationTimeBoundaries[1];
Map<Integer, VirtualTrack> tracks = new HashMap<Integer, VirtualTrack>();
Map<Integer, Transform> previousTransforms = new HashMap<Integer, Transform>();
Map<Integer, Transform> previousTransforms = this.getInitialTransforms();
for (int frame = 0; frame < maxFrame; ++frame) {
// this MUST be done here, otherwise setting next frame of animation will
// lead to possible errors
this.reset();
// first set proper time for all bones in all the tracks ...
for (Track track : animation.getTracks()) {
float time = ((BoneTrack) track).getTimes()[frame];
Integer boneIndex = ((BoneTrack) track).getTargetBoneIndex();
track.setTime(time, 1, animControl, animChannel, vars);
skeleton.updateWorldVectors();
Transform previousTransform = previousTransforms.get(boneIndex);
if (previousTransform == null) {
Bone bone = skeleton.getBone(boneIndex);
previousTransform = new Transform();
previousTransform.setTranslation(bone.getLocalPosition());
previousTransform.setRotation(bone.getLocalRotation());
previousTransform.setScale(bone.getLocalScale());
previousTransforms.put(boneIndex, previousTransform);
}
}
// ... and then apply constraints ...
for (Constraint constraint : constraints) {
constraint.apply(frame);
if (constraint.getAlteredOmas() != null) {
alteredOmas.addAll(constraint.getAlteredOmas());
}
}
// ... add virtual tracks if neccessary, for bones that were altered but had no tracks before ...
for (Long boneOMA : alteredOmas) {
BoneContext boneContext = blenderContext.getBoneContext(boneOMA);
int boneIndex = skeleton.getBoneIndex(boneContext.getBone());
if (!tracks.containsKey(boneIndex)) {
tracks.put(boneIndex, new VirtualTrack(maxFrame, maxTime));
}
}
alteredOmas.clear();
// ... and fill in another frame in the result track
for (Track track : animation.getTracks()) {
Integer boneIndex = ((BoneTrack) track).getTargetBoneIndex();
for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
Integer boneIndex = trackEntry.getKey();
Bone bone = skeleton.getBone(boneIndex);
// take the initial transform of a bone
// take the initial transform of a bone and its virtual track
Transform previousTransform = previousTransforms.get(boneIndex);
VirtualTrack vTrack = tracks.get(boneIndex);
if (vTrack == null) {
vTrack = new VirtualTrack(maxFrame, maxTime);
tracks.put(boneIndex, vTrack);
}
VirtualTrack vTrack = trackEntry.getValue();
Vector3f bonePositionDifference = bone.getLocalPosition().subtract(previousTransform.getTranslation());
Quaternion boneRotationDifference = bone.getLocalRotation().mult(previousTransform.getRotation().inverse()).normalizeLocal();
@ -319,13 +321,18 @@ public class SimulationNode {
for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
Track newTrack = trackEntry.getValue().getAsBoneTrack(trackEntry.getKey());
if (newTrack != null) {
boolean trackReplaced = false;
for (Track track : animation.getTracks()) {
if (((BoneTrack) track).getTargetBoneIndex() == trackEntry.getKey().intValue()) {
animation.removeTrack(track);
animation.addTrack(newTrack);
trackReplaced = true;
break;
}
}
if (!trackReplaced) {
animation.addTrack(newTrack);
}
}
applyStaticConstraints = false;
}
@ -341,6 +348,7 @@ public class SimulationNode {
for (Constraint constraint : constraints) {
constraint.apply(0);
}
skeleton.updateWorldVectors();
}
}
}
@ -355,7 +363,6 @@ public class SimulationNode {
} else {
this.simulateSkeleton();
}
this.reset();
}
/**
@ -406,6 +413,19 @@ public class SimulationNode {
return result.size() > 0 ? result : null;
}
/**
* Creates the initial transforms for all bones in the skelketon.
* @return the map where the key is the bone index and the value us the bone's initial transformation
*/
private Map<Integer, Transform> getInitialTransforms() {
Map<Integer, Transform> result = new HashMap<Integer, Transform>();
for (int i = 0; i < skeleton.getBoneCount(); ++i) {
Bone bone = skeleton.getBone(i);
result.put(i, new Transform(bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale()));
}
return result;
}
@Override
public String toString() {
return name;

@ -1,6 +1,5 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.math.Transform;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
import com.jme3.scene.plugins.blender.animations.Ipo;
@ -25,12 +24,4 @@ import com.jme3.scene.plugins.blender.file.Structure;
}
return true;
}
@Override
public void apply(int frame) {
Transform ownerTransform = constraintHelper.getTransform(ownerOMA, null, ownerSpace);
Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
constraintDefinition.bake(ownerTransform, targetTransform, this.ipo.calculateValue(frame));
constraintHelper.applyTransform(ownerOMA, subtargetName, ownerSpace, ownerTransform);
}
}

@ -1,8 +1,12 @@
package com.jme3.scene.plugins.blender.constraints.definitions;
import java.util.Set;
import com.jme3.math.Transform;
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.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
/**
@ -11,14 +15,17 @@ import com.jme3.scene.plugins.blender.file.Structure;
* @author Marcin Roguski (Kaelthas)
*/
public abstract class ConstraintDefinition {
protected ConstraintHelper constraintHelper;
/** Constraints flag. Used to load user's options applied to the constraint. */
protected int flag;
protected int flag;
/** The constraint's owner. Loaded during runtime. */
private Object owner;
private Object owner;
/** The blender context. */
private BlenderContext blenderContext;
protected BlenderContext blenderContext;
/** The constraint's owner OMA. */
private Long ownerOMA;
protected Long ownerOMA;
/** Stores the OMA addresses of all features whose transform had been altered beside the constraint owner. */
protected Set<Long> alteredOmas;
/**
* Loads a constraint definition based on the constraint definition
@ -39,6 +46,7 @@ public abstract class ConstraintDefinition {
}
}
this.blenderContext = blenderContext;
constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
this.ownerOMA = ownerOMA;
}
@ -67,6 +75,13 @@ public abstract class ConstraintDefinition {
return true;
}
/**
* @return a list of all OMAs of the features that the constraint had altered beside its owner
*/
public Set<Long> getAlteredOmas() {
return alteredOmas;
}
/**
* @return the type name of the constraint
*/
@ -75,12 +90,14 @@ public abstract class ConstraintDefinition {
/**
* Bakes the constraint for the current feature (bone or spatial) position.
*
* @param ownerTransform
* the input transform (here the result is stored)
* @param ownerSpace
* the space where owner transform will be evaluated in
* @param targetSpace
* the space where target transform will be evaluated in
* @param targetTransform
* the target transform used by some of the constraints
* @param influence
* the influence of the constraint (from range <0; 1>)
*/
public abstract void bake(Transform ownerTransform, Transform targetTransform, float influence);
public abstract void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence);
}

@ -4,6 +4,8 @@ import com.jme3.animation.Bone;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
/**
@ -24,17 +26,19 @@ import com.jme3.scene.plugins.blender.file.Structure;
mode = ((Number) constraintData.getFieldValue("mode")).intValue();
dist = ((Number) constraintData.getFieldValue("dist")).floatValue();
}
@Override
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
// distance limit does not work on bones who have parent
return;
}
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
Vector3f v = ownerTransform.getTranslation().subtract(targetTransform.getTranslation());
float currentDistance = v.length();
switch (mode) {
case LIMITDIST_INSIDE:
if (currentDistance >= dist) {
@ -62,6 +66,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
default:
throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
}
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
}
@Override

@ -50,6 +50,7 @@ public class ConstraintDefinitionFactory {
CONSTRAINT_CLASSES.put("bRotLimitConstraint", ConstraintDefinitionRotLimit.class);
CONSTRAINT_CLASSES.put("bSizeLikeConstraint", ConstraintDefinitionSizeLike.class);
CONSTRAINT_CLASSES.put("bSizeLimitConstraint", ConstraintDefinitionSizeLimit.class);
CONSTRAINT_CLASSES.put("bKinematicConstraint", ConstraintDefinitionIK.class);
}
private static final Map<String, String> UNSUPPORTED_CONSTRAINTS = new HashMap<String, String>();
@ -58,7 +59,6 @@ public class ConstraintDefinitionFactory {
UNSUPPORTED_CONSTRAINTS.put("bChildOfConstraint", "Child of");
UNSUPPORTED_CONSTRAINTS.put("bClampToConstraint", "Clamp to");
UNSUPPORTED_CONSTRAINTS.put("bFollowPathConstraint", "Follow path");
UNSUPPORTED_CONSTRAINTS.put("bKinematicConstraint", "Inverse kinematic");
UNSUPPORTED_CONSTRAINTS.put("bLockTrackConstraint", "Lock track");
UNSUPPORTED_CONSTRAINTS.put("bMinMaxConstraint", "Min max");
UNSUPPORTED_CONSTRAINTS.put("bPythonConstraint", "Python/Script");

@ -0,0 +1,115 @@
package com.jme3.scene.plugins.blender.constraints.definitions;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import com.jme3.animation.Bone;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
public class ConstraintDefinitionIK extends ConstraintDefinition {
private static final int FLAG_POSITION = 0x20;
/** The number of affected bones. Zero means that all parent bones of the current bone should take part in baking. */
private int bonesAffected;
private float chainLength;
private BoneContext[] bones;
private boolean needToCompute = true;
public ConstraintDefinitionIK(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
super(constraintData, ownerOMA, blenderContext);
bonesAffected = ((Number) constraintData.getFieldValue("rootbone")).intValue();
if ((flag & FLAG_POSITION) == 0) {
needToCompute = false;
}
if (needToCompute) {
alteredOmas = new HashSet<Long>();
}
}
@Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (needToCompute) {
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
BoneContext[] boneContexts = this.getBones();
float b = chainLength;
Quaternion boneWorldRotation = new Quaternion();
for (int i = 0; i < boneContexts.length; ++i) {
Bone bone = boneContexts[i].getBone();
bone.updateWorldVectors();
Transform boneWorldTransform = constraintHelper.getTransform(boneContexts[i].getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD);
Vector3f head = boneWorldTransform.getTranslation();
Vector3f tail = head.add(bone.getModelSpaceRotation().mult(Vector3f.UNIT_Y.mult(boneContexts[i].getLength())));
Vector3f vectorA = tail.subtract(head);
float a = vectorA.length();
vectorA.normalizeLocal();
Vector3f vectorC = targetTransform.getTranslation().subtract(head);
float c = vectorC.length();
vectorC.normalizeLocal();
b -= a;
float theta = 0;
if (c >= a + b) {
theta = vectorA.angleBetween(vectorC);
} else if (c <= FastMath.abs(a - b) && i < boneContexts.length - 1) {
theta = vectorA.angleBetween(vectorC) - FastMath.HALF_PI;
} else {
theta = vectorA.angleBetween(vectorC) - FastMath.acos(-(b * b - a * a - c * c) / (2 * a * c));
}
if (theta != 0) {
Vector3f vectorR = vectorA.cross(vectorC);
boneWorldRotation.fromAngleAxis(theta, vectorR);
boneWorldTransform.getRotation().multLocal(boneWorldRotation);
constraintHelper.applyTransform(boneContexts[i].getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneWorldTransform);
}
bone.updateWorldVectors();
alteredOmas.add(boneContexts[i].getBoneOma());
}
}
}
@Override
public String getConstraintTypeName() {
return "Inverse kinematics";
}
/**
* @return the bone contexts of all bones that will be used in this constraint computations
*/
private BoneContext[] getBones() {
if (bones == null) {
List<BoneContext> bones = new ArrayList<BoneContext>();
Bone bone = (Bone) this.getOwner();
while (bone != null) {
BoneContext boneContext = blenderContext.getBoneContext(bone);
bones.add(0, boneContext);
chainLength += boneContext.getLength();
if (bonesAffected != 0 && bones.size() >= bonesAffected) {
break;
}
bone = bone.getParent();
}
this.bones = bones.toArray(new BoneContext[bones.size()]);
}
return bones;
}
}

@ -4,6 +4,8 @@ import com.jme3.animation.Bone;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
/**
@ -40,14 +42,18 @@ import com.jme3.scene.plugins.blender.file.Structure;
flag |= invZ >> 1;
}
}
@Override
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
// cannot copy the location of a bone attached to its parent,
// Blender forbids that
return;
}
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
Vector3f ownerLocation = ownerTransform.getTranslation();
Vector3f targetLocation = targetTransform.getTranslation();
@ -82,6 +88,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
ownerLocation.addLocal(startLocation);
}
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
}
@Override

@ -4,6 +4,8 @@ import com.jme3.animation.Bone;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
/**
@ -51,14 +53,17 @@ import com.jme3.scene.plugins.blender.file.Structure;
limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
}
}
@Override
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
// location limit does not work on bones who have parent
return;
}
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
Vector3f translation = ownerTransform.getTranslation();
if ((flag & LIMIT_XMIN) != 0 && translation.x < limits[0][0]) {
@ -79,6 +84,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
if ((flag & LIMIT_ZMAX) != 0 && translation.z > limits[2][1]) {
translation.z -= (translation.z - limits[2][1]) * influence;
}
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
}
@Override

@ -2,6 +2,7 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
import com.jme3.math.Transform;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
/**
@ -16,7 +17,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
}
@Override
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
// null constraint does nothing so no need to implement this one
}

@ -3,6 +3,8 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
/**
@ -27,7 +29,10 @@ import com.jme3.scene.plugins.blender.file.Structure;
}
@Override
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
Quaternion ownerRotation = ownerTransform.getRotation();
ownerAngles = ownerRotation.toAngles(ownerAngles);
targetAngles = targetTransform.getRotation().toAngles(targetAngles);
@ -64,6 +69,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
// ownerLocation.addLocal(startLocation);
// TODO
}
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
}
@Override

@ -3,6 +3,8 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
import com.jme3.math.FastMath;
import com.jme3.math.Transform;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
/**
@ -65,9 +67,12 @@ import com.jme3.scene.plugins.blender.file.Structure;
* limits[i][0] = limits[i][1]; limits[i][1] = temp; } }
*/
}
@Override
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
ownerTransform.getRotation().toAngles(angles);
// make sure that the rotations are always in range [0, 2PI)
// TODO: same comment as in constructor
@ -105,6 +110,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
angles[2] -= difference;
}
ownerTransform.getRotation().fromAngles(angles);
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
}
@Override

@ -3,6 +3,8 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
/**
@ -28,9 +30,12 @@ import com.jme3.scene.plugins.blender.file.Structure;
flag |= z >> 1;
}
}
@Override
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
Vector3f ownerScale = ownerTransform.getScale();
Vector3f targetScale = targetTransform.getScale();
@ -50,6 +55,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
ownerScale.z = targetScale.z * influence + (1.0f - influence) * ownerScale.z;
}
ownerScale.addLocal(offset);
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
}
@Override

@ -3,6 +3,8 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure;
/**
@ -50,11 +52,13 @@ import com.jme3.scene.plugins.blender.file.Structure;
limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
}
}
@Override
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
Vector3f scale = ownerTransform.getScale();
if ((flag & LIMIT_XMIN) != 0 && scale.x < limits[0][0]) {
scale.x -= (scale.x - limits[0][0]) * influence;
}
@ -73,6 +77,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
if ((flag & LIMIT_ZMAX) != 0 && scale.z > limits[2][1]) {
scale.z -= (scale.z - limits[2][1]) * influence;
}
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
}
@Override

@ -1,6 +1,7 @@
package com.jme3.scene.plugins.blender.constraints.definitions;
import com.jme3.math.Transform;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
/**
* This class represents a constraint that is defined by blender but not
@ -18,7 +19,7 @@ import com.jme3.math.Transform;
}
@Override
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
}
@Override

Loading…
Cancel
Save