Bugfix: sorting constraint computation in the proper order which should

decrease the amount of unwanted artifacts appearing in some models
during animations.
experimental
jmekaelthas 10 years ago
parent 858fd433ca
commit 65c3ff668c
  1. 24
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java
  2. 17
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java
  3. 24
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/Constraint.java
  4. 164
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java
  5. 6
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SkeletonConstraint.java
  6. 7
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SpatialConstraint.java
  7. 23
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/VirtualTrack.java
  8. 21
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java
  9. 12
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDistLimit.java
  10. 26
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java
  11. 20
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLike.java
  12. 19
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLimit.java
  13. 13
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionMaintainVolume.java
  14. 6
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionNull.java
  15. 10
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLike.java
  16. 10
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLimit.java
  17. 9
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLike.java
  18. 10
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLimit.java
  19. 9
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionTransLike.java
  20. 6
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/UnsupportedConstraintDefinition.java

@ -366,6 +366,30 @@ public class BoneContext {
return (flag & flagMask) != 0; return (flag & flagMask) != 0;
} }
/**
* @return the root bone context of this bone context
*/
public BoneContext getRoot() {
BoneContext result = this;
while (result.parent != null) {
result = result.parent;
}
return result;
}
/**
* @return a number of bones from this bone to its root
*/
public int getDistanceFromRoot() {
int result = 0;
BoneContext boneContext = this;
while (boneContext.parent != null) {
boneContext = boneContext.parent;
++result;
}
return result;
}
@Override @Override
public String toString() { public String toString() {
return "BoneContext: " + boneName; return "BoneContext: " + boneName;

@ -62,7 +62,7 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
} }
} }
} }
return true; return constraintDefinition == null ? true : constraintDefinition.isTargetRequired();
} }
@Override @Override
@ -70,4 +70,19 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
super.apply(frame); super.apply(frame);
blenderContext.getBoneContext(ownerOMA).getBone().updateModelTransforms(); blenderContext.getBoneContext(ownerOMA).getBone().updateModelTransforms();
} }
@Override
public Long getTargetOMA() {
if(targetOMA != null && subtargetName != null && !subtargetName.trim().isEmpty()) {
Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE);
if(nodeTarget != null) {
if(blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) {
BoneContext boneContext = blenderContext.getBoneByName(targetOMA, subtargetName);
return boneContext != null ? boneContext.getBoneOma() : 0L;
}
return targetOMA;
}
}
return 0L;
}
} }

@ -116,14 +116,31 @@ public abstract class Constraint {
*/ */
public abstract boolean validate(); public abstract boolean validate();
/**
* @return the OMA of the target or 0 if no target is specified for the constraint
*/
public abstract Long getTargetOMA();
/** /**
* Applies the constraint to owner (and in some cases can alter other bones of the skeleton). * Applies the constraint to owner (and in some cases can alter other bones of the skeleton).
* @param frame * @param frame
* the frame of the animation * the frame of the animation
*/ */
public void apply(int frame) { public void apply(int frame) {
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "Applying constraint: {0} for frame {1}", new Object[] { name, frame });
}
Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null; Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
constraintDefinition.bake(ownerSpace, targetSpace, targetTransform, (float)ipo.calculateValue(frame)); constraintDefinition.bake(ownerSpace, targetSpace, targetTransform, (float) ipo.calculateValue(frame));
}
/**
* @return determines if the definition of the constraint will change the bone in any way; in most cases
* it is possible to tell that even before the constraint baking simulation is started, so we can discard such bones from constraint
* computing to improve the computation speed and lower the computations complexity
*/
public boolean isTrackToBeChanged() {
return constraintDefinition == null ? false : constraintDefinition.isTrackToBeChanged();
} }
@Override @Override
@ -163,4 +180,9 @@ public abstract class Constraint {
} }
return true; return true;
} }
@Override
public String toString() {
return "Constraint(name = " + name + ", def = " + constraintDefinition + ")";
}
} }

@ -1,6 +1,8 @@
package com.jme3.scene.plugins.blender.constraints; package com.jme3.scene.plugins.blender.constraints;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -132,7 +134,7 @@ public class SimulationNode {
} }
} }
Node node = blenderContext.getControlledNode(skeleton); Node node = blenderContext.getControlledNode(skeleton);
Long animatedNodeOMA = ((Number)blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, node)).longValue(); Long animatedNodeOMA = ((Number) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, node)).longValue();
animations = blenderContext.getAnimations(animatedNodeOMA); animations = blenderContext.getAnimations(animatedNodeOMA);
} else { } else {
animations = blenderContext.getAnimations(featureOMA); animations = blenderContext.getAnimations(featureOMA);
@ -206,7 +208,7 @@ public class SimulationNode {
int maxFrame = (int) animationTimeBoundaries[0]; int maxFrame = (int) animationTimeBoundaries[0];
float maxTime = animationTimeBoundaries[1]; float maxTime = animationTimeBoundaries[1];
VirtualTrack vTrack = new VirtualTrack(maxFrame, maxTime); VirtualTrack vTrack = new VirtualTrack(spatial.getName(), maxFrame, maxTime);
for (Track track : animation.getTracks()) { for (Track track : animation.getTracks()) {
for (int frame = 0; frame < maxFrame; ++frame) { for (int frame = 0; frame < maxFrame; ++frame) {
spatial.setLocalTranslation(((SpatialTrack) track).getTranslations()[frame]); spatial.setLocalTranslation(((SpatialTrack) track).getTranslations()[frame]);
@ -252,6 +254,8 @@ public class SimulationNode {
if (animations != null) { if (animations != null) {
TempVars vars = TempVars.get(); TempVars vars = TempVars.get();
AnimChannel animChannel = animControl.createChannel(); AnimChannel animChannel = animControl.createChannel();
List<Bone> bonesWithConstraints = this.collectBonesWithConstraints(skeleton);
for (Animation animation : animations) { for (Animation animation : animations) {
float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation); float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation);
int maxFrame = (int) animationTimeBoundaries[0]; int maxFrame = (int) animationTimeBoundaries[0];
@ -271,19 +275,16 @@ public class SimulationNode {
} }
// ... and then apply constraints from the root bone to the last child ... // ... and then apply constraints from the root bone to the last child ...
for (Bone rootBone : skeleton.getRoots()) { for (Bone rootBone : bonesWithConstraints) {
if (skeleton.getBoneIndex(rootBone) > 0) {
// ommit the 0 - indexed root bone as it is the bone added by importer
this.applyConstraints(rootBone, alteredOmas, frame); this.applyConstraints(rootBone, alteredOmas, frame);
} }
}
// ... add virtual tracks if neccessary, for bones that were altered but had no tracks before ... // ... add virtual tracks if neccessary, for bones that were altered but had no tracks before ...
for (Long boneOMA : alteredOmas) { for (Long boneOMA : alteredOmas) {
BoneContext boneContext = blenderContext.getBoneContext(boneOMA); BoneContext boneContext = blenderContext.getBoneContext(boneOMA);
int boneIndex = skeleton.getBoneIndex(boneContext.getBone()); int boneIndex = skeleton.getBoneIndex(boneContext.getBone());
if (!tracks.containsKey(boneIndex)) { if (!tracks.containsKey(boneIndex)) {
tracks.put(boneIndex, new VirtualTrack(maxFrame, maxTime)); tracks.put(boneIndex, new VirtualTrack(boneContext.getBone().getName(), maxFrame, maxTime));
} }
} }
alteredOmas.clear(); alteredOmas.clear();
@ -349,9 +350,6 @@ public class SimulationNode {
alteredOmas.add(boneContext.getBoneOma()); alteredOmas.add(boneContext.getBoneOma());
} }
} }
for (Bone child : bone.getChildren()) {
this.applyConstraints(child, alteredOmas, frame);
}
} }
/** /**
@ -366,6 +364,150 @@ public class SimulationNode {
} }
} }
/**
* Collects the bones that will take part in constraint computations.
* The result will not include bones whose constraints will not change them or are invalid.
* The bones are sorted so that the constraint applying is done in the proper order.
* @param skeleton
* the simulated skeleton
* @return a list of bones that will take part in constraints computations
*/
private List<Bone> collectBonesWithConstraints(Skeleton skeleton) {
Map<BoneContext, List<Constraint>> bonesWithConstraints = new HashMap<BoneContext, List<Constraint>>();
for (int i = 1; i < skeleton.getBoneCount(); ++i) {// ommit the 0 - indexed root bone as it is the bone added by importer
Bone bone = skeleton.getBone(i);
BoneContext boneContext = blenderContext.getBoneContext(bone);
List<Constraint> constraints = this.findConstraints(boneContext.getBoneOma(), blenderContext);
if (constraints != null && constraints.size() > 0) {
bonesWithConstraints.put(boneContext, constraints);
}
}
// first sort out constraints that are not implemented or invalid or will not affect the bone's tracks
List<BoneContext> bonesToRemove = new ArrayList<BoneContext>(bonesWithConstraints.size());
for (Entry<BoneContext, List<Constraint>> entry : bonesWithConstraints.entrySet()) {
List<Constraint> validConstraints = new ArrayList<Constraint>(entry.getValue().size());
for (Constraint constraint : entry.getValue()) {// TODO: sprawdziæ czy wprowadza jakiekolwiek zmiany
if (constraint.isImplemented() && constraint.validate() && constraint.isTrackToBeChanged()) {
validConstraints.add(constraint);
}
}
if (validConstraints.size() > 0) {
entry.setValue(validConstraints);
} else {
bonesToRemove.add(entry.getKey());
}
}
for (BoneContext boneContext : bonesToRemove) {
bonesWithConstraints.remove(boneContext);
}
List<BoneContext> bonesConstrainedWithoutTarget = new ArrayList<BoneContext>();
Set<Long> remainedOMAS = new HashSet<Long>();
// later move all bones with not dependant constraints to the front
bonesToRemove.clear();
for (Entry<BoneContext, List<Constraint>> entry : bonesWithConstraints.entrySet()) {
boolean hasDependantConstraints = false;
for (Constraint constraint : entry.getValue()) {
if (constraint.targetOMA != null) {
hasDependantConstraints = true;
break;
}
}
if (!hasDependantConstraints) {
bonesConstrainedWithoutTarget.add(entry.getKey());
bonesToRemove.add(entry.getKey());
} else {
remainedOMAS.add(entry.getKey().getBoneOma());
}
}
for (BoneContext boneContext : bonesToRemove) {
bonesWithConstraints.remove(boneContext);
}
this.sortBonesByChain(bonesConstrainedWithoutTarget);
// another step is to add those bones whose constraints depend only on bones already added to the result or to those
// that are not included neither in the result nor in the remaining map
// do this as long as bones are being moved to the result and the 'bonesWithConstraints' is not empty
List<BoneContext> bonesConstrainedWithTarget = new ArrayList<BoneContext>();
do {
bonesToRemove.clear();
for (Entry<BoneContext, List<Constraint>> entry : bonesWithConstraints.entrySet()) {
boolean unconstrainedBone = true;
for (Constraint constraint : entry.getValue()) {
if (remainedOMAS.contains(constraint.getTargetOMA())) {
unconstrainedBone = false;
break;
}
}
if (unconstrainedBone) {
bonesToRemove.add(entry.getKey());
bonesConstrainedWithTarget.add(entry.getKey());
}
}
for (BoneContext boneContext : bonesToRemove) {
bonesWithConstraints.remove(boneContext);
remainedOMAS.remove(boneContext.getBoneOma());
}
} while (bonesWithConstraints.size() > 0 && bonesToRemove.size() > 0);
this.sortBonesByChain(bonesConstrainedWithoutTarget);
// prepare the result
List<Bone> result = new ArrayList<Bone>();
for (BoneContext boneContext : bonesConstrainedWithoutTarget) {
result.add(boneContext.getBone());
}
for (BoneContext boneContext : bonesConstrainedWithTarget) {
result.add(boneContext.getBone());
}
// in the end prepare the mapping between bone OMA
if (bonesWithConstraints.size() > 0) {
LOGGER.warning("Some bones have loops in their constraints' definitions. The result might not be properly computed!");
for (BoneContext boneContext : bonesWithConstraints.keySet()) {
result.add(boneContext.getBone());
}
}
return result;
}
/**
* The method sorts the given bones from root to top.
* If the list contains bones from different branches then those branches will be listed
* one after another - which means that bones will be grouped by branches they belong to.
* @param bones
* a list of bones
*/
private void sortBonesByChain(List<BoneContext> bones) {
Map<BoneContext, List<BoneContext>> branches = new HashMap<BoneContext, List<BoneContext>>();
for (BoneContext bone : bones) {
BoneContext root = bone.getRoot();
List<BoneContext> list = branches.get(root);
if (list == null) {
list = new ArrayList<BoneContext>();
branches.put(root, list);
}
list.add(bone);
}
// sort the bones in each branch from root to leaf
bones.clear();
for (Entry<BoneContext, List<BoneContext>> entry : branches.entrySet()) {
Collections.sort(entry.getValue(), new Comparator<BoneContext>() {
@Override
public int compare(BoneContext o1, BoneContext o2) {
return o1.getDistanceFromRoot() - o2.getDistanceFromRoot();
}
});
bones.addAll(entry.getValue());
}
}
/** /**
* Computes the maximum frame and time for the animation. Different tracks * Computes the maximum frame and time for the animation. Different tracks
* can have different lengths so here the maximum one is being found. * can have different lengths so here the maximum one is being found.
@ -376,7 +518,7 @@ public class SimulationNode {
*/ */
private float[] computeAnimationTimeBoundaries(Animation animation) { private float[] computeAnimationTimeBoundaries(Animation animation) {
int maxFrame = Integer.MIN_VALUE; int maxFrame = Integer.MIN_VALUE;
float maxTime = Float.MIN_VALUE; float maxTime = -Float.MAX_VALUE;
for (Track track : animation.getTracks()) { for (Track track : animation.getTracks()) {
if (track instanceof BoneTrack) { if (track instanceof BoneTrack) {
maxFrame = Math.max(maxFrame, ((BoneTrack) track).getTranslations().length); maxFrame = Math.max(maxFrame, ((BoneTrack) track).getTranslations().length);

@ -32,4 +32,10 @@ import com.jme3.scene.plugins.blender.file.Structure;
public void apply(int frame) { public void apply(int frame) {
LOGGER.warning("Applying constraints to skeleton is not supported."); LOGGER.warning("Applying constraints to skeleton is not supported.");
} }
@Override
public Long getTargetOMA() {
LOGGER.warning("Constraints for skeleton are not supported.");
return null;
}
} }

@ -22,6 +22,11 @@ import com.jme3.scene.plugins.blender.file.Structure;
if (targetOMA != null) { if (targetOMA != null) {
return blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE) != null; return blenderContext.getLoadedFeature(targetOMA, LoadedDataType.FEATURE) != null;
} }
return true; return constraintDefinition == null ? true : constraintDefinition.isTargetRequired();
}
@Override
public Long getTargetOMA() {
return targetOMA;
} }
} }

@ -16,6 +16,8 @@ import com.jme3.math.Vector3f;
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
/* package */class VirtualTrack { /* package */class VirtualTrack {
/** The name of the track (for debugging purposes). */
private String name;
/** The last frame for the track. */ /** The last frame for the track. */
public int maxFrame; public int maxFrame;
/** The max time for the track. */ /** The max time for the track. */
@ -35,7 +37,8 @@ import com.jme3.math.Vector3f;
* @param maxTime * @param maxTime
* the max time for the track * the max time for the track
*/ */
public VirtualTrack(int maxFrame, float maxTime) { public VirtualTrack(String name, int maxFrame, float maxTime) {
this.name = name;
this.maxFrame = maxFrame; this.maxFrame = maxFrame;
this.maxTime = maxTime; this.maxTime = maxTime;
} }
@ -101,7 +104,7 @@ import com.jme3.math.Vector3f;
*/ */
private float[] createTimes() { private float[] createTimes() {
float[] times = new float[maxFrame]; float[] times = new float[maxFrame];
float dT = maxTime / (float) maxFrame; float dT = maxTime / maxFrame;
float t = 0; float t = 0;
for (int i = 0; i < maxFrame; ++i) { for (int i = 0; i < maxFrame; ++i) {
times[i] = t; times[i] = t;
@ -143,4 +146,20 @@ import com.jme3.math.Vector3f;
list.add(element); list.add(element);
} }
} }
@Override
public String toString() {
StringBuilder result = new StringBuilder(2048);
result.append("TRACK: ").append(name).append('\n');
if (translations != null && translations.size() > 0) {
result.append("TRANSLATIONS: ").append(translations.toString()).append('\n');
}
if (rotations != null && rotations.size() > 0) {
result.append("ROTATIONS: ").append(rotations.toString()).append('\n');
}
if (scales != null && scales.size() > 0) {
result.append("SCALES: ").append(scales.toString()).append('\n');
}
return result.toString();
}
} }

@ -28,6 +28,8 @@ public abstract class ConstraintDefinition {
protected Long ownerOMA; protected Long ownerOMA;
/** Stores the OMA addresses of all features whose transform had been altered beside the constraint owner. */ /** Stores the OMA addresses of all features whose transform had been altered beside the constraint owner. */
protected Set<Long> alteredOmas; protected Set<Long> alteredOmas;
/** The variable that determines if the constraint will alter the track in any way. */
protected boolean trackToBeChanged = true;
/** /**
* Loads a constraint definition based on the constraint definition * Loads a constraint definition based on the constraint definition
@ -52,6 +54,20 @@ public abstract class ConstraintDefinition {
this.ownerOMA = ownerOMA; this.ownerOMA = ownerOMA;
} }
/**
* @return determines if the definition of the constraint will change the bone in any way; in most cases
* it is possible to tell that even before the constraint baking simulation is started, so we can discard such bones from constraint
* computing to improve the computation speed and lower the computations complexity
*/
public boolean isTrackToBeChanged() {
return trackToBeChanged;
}
/**
* @return determines if this constraint definition requires a defined target or not
*/
public abstract boolean isTargetRequired();
/** /**
* This method is here because we have no guarantee that the owner is loaded * This method is here because we have no guarantee that the owner is loaded
* when constraint is being created. So use it to get the owner when it is * when constraint is being created. So use it to get the owner when it is
@ -132,4 +148,9 @@ public abstract class ConstraintDefinition {
* the influence of the constraint (from range <0; 1>) * the influence of the constraint (from range <0; 1>)
*/ */
public abstract void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence); public abstract void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence);
@Override
public String toString() {
return this.getConstraintTypeName();
}
} }

@ -29,13 +29,12 @@ import com.jme3.scene.plugins.blender.file.Structure;
@Override @Override
public void bake(Space ownerSpace, Space targetSpace, 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 && if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)) {
blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)) {
// distance limit does not work on bones who are connected to their parent // distance limit does not work on bones who are connected to their parent
return; return;
} }
if(influence == 0 || targetTransform == null) { if (influence == 0 || targetTransform == null) {
return ;// no need to do anything return;// no need to do anything
} }
Transform ownerTransform = this.getOwnerTransform(ownerSpace); Transform ownerTransform = this.getOwnerTransform(ownerSpace);
@ -73,6 +72,11 @@ import com.jme3.scene.plugins.blender.file.Structure;
this.applyOwnerTransform(ownerTransform, ownerSpace); this.applyOwnerTransform(ownerTransform, ownerSpace);
} }
@Override
public boolean isTargetRequired() {
return true;
}
@Override @Override
public String getConstraintTypeName() { public String getConstraintTypeName() {
return "Limit distance"; return "Limit distance";

@ -29,8 +29,6 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
private int bonesAffected; private int bonesAffected;
/** The total length of the bone chain. Useful for optimisation of computations speed in some cases. */ /** The total length of the bone chain. Useful for optimisation of computations speed in some cases. */
private float chainLength; private float chainLength;
/** Tells if there is anything to compute at all. */
private boolean needToCompute = true;
/** Indicates if the tail of the bone should be used or not. */ /** Indicates if the tail of the bone should be used or not. */
private boolean useTail; private boolean useTail;
/** The amount of iterations of the algorithm. */ /** The amount of iterations of the algorithm. */
@ -43,23 +41,23 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
useTail = (flag & FLAG_USE_TAIL) != 0; useTail = (flag & FLAG_USE_TAIL) != 0;
if ((flag & FLAG_POSITION) == 0) { if ((flag & FLAG_POSITION) == 0) {
needToCompute = false; trackToBeChanged = false;
} }
if (needToCompute) { if (trackToBeChanged) {
alteredOmas = new HashSet<Long>(); alteredOmas = new HashSet<Long>();
} }
} }
@Override @Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (influence == 0 || !needToCompute || targetTransform == null) { if (influence == 0 || !trackToBeChanged || targetTransform == null) {
return;// no need to do anything return;// no need to do anything
} }
Quaternion q = new Quaternion(); Quaternion q = new Quaternion();
Vector3f t = targetTransform.getTranslation(); Vector3f t = targetTransform.getTranslation();
List<BoneContext> bones = this.loadBones(); List<BoneContext> bones = this.loadBones();
if(bones.size() == 0) { if (bones.size() == 0) {
return;// no need to do anything return;// no need to do anything
} }
float distanceFromTarget = Float.MAX_VALUE; float distanceFromTarget = Float.MAX_VALUE;
@ -186,4 +184,20 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
} }
return bones; return bones;
} }
@Override
public boolean isTrackToBeChanged() {
if (trackToBeChanged) {
// need to check the bone structure too (when constructor was called not all of the bones might have been loaded yet)
// that is why it is also checked here
List<BoneContext> bones = this.loadBones();
trackToBeChanged = bones.size() > 0;
}
return trackToBeChanged;
}
@Override
public boolean isTargetRequired() {
return true;
}
} }

@ -39,18 +39,21 @@ import com.jme3.scene.plugins.blender.file.Structure;
flag |= invY << 1; flag |= invY << 1;
flag |= z >> 1; flag |= z >> 1;
flag |= invZ >> 1; flag |= invZ >> 1;
trackToBeChanged = (flag & LOCLIKE_X) != 0 || (flag & LOCLIKE_Y) != 0 || (flag & LOCLIKE_Z) != 0;
} }
} }
@Override @Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { public boolean isTrackToBeChanged() {
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null &&
blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)) {
// location copy does not work on bones who are connected to their parent // location copy does not work on bones who are connected to their parent
return; return trackToBeChanged && !(this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT));
} }
if(influence == 0 || targetTransform == null) {
return ;// no need to do anything @Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (influence == 0 || targetTransform == null || !this.isTrackToBeChanged()) {
return;
} }
Transform ownerTransform = this.getOwnerTransform(ownerSpace); Transform ownerTransform = this.getOwnerTransform(ownerSpace);
@ -96,4 +99,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() { public String getConstraintTypeName() {
return "Copy location"; return "Copy location";
} }
@Override
public boolean isTargetRequired() {
return true;
}
} }

@ -52,14 +52,20 @@ import com.jme3.scene.plugins.blender.file.Structure;
limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue(); limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue(); limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
} }
trackToBeChanged = (flag & (LIMIT_XMIN | LIMIT_XMAX | LIMIT_YMIN | LIMIT_YMAX | LIMIT_ZMIN | LIMIT_ZMAX)) != 0;
} }
@Override @Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { public boolean isTrackToBeChanged() {
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null &&
blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)) {
// location limit does not work on bones who are connected to their parent // location limit does not work on bones who are connected to their parent
return; return trackToBeChanged && !(this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT));
}
@Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (influence == 0 || !this.isTrackToBeChanged()) {
return;// no need to do anything
} }
Transform ownerTransform = this.getOwnerTransform(ownerSpace); Transform ownerTransform = this.getOwnerTransform(ownerSpace);
@ -92,4 +98,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() { public String getConstraintTypeName() {
return "Limit location"; return "Limit location";
} }
@Override
public boolean isTargetRequired() {
return false;
}
} }

@ -6,6 +6,11 @@ import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space; import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
/**
* This class represents 'Maintain volume' constraint type in blender.
*
* @author Marcin Roguski (Kaelthas)
*/
public class ConstraintDefinitionMaintainVolume extends ConstraintDefinition { public class ConstraintDefinitionMaintainVolume extends ConstraintDefinition {
private static final int FLAG_MASK_X = 0; private static final int FLAG_MASK_X = 0;
private static final int FLAG_MASK_Y = 1; private static final int FLAG_MASK_Y = 1;
@ -16,11 +21,12 @@ public class ConstraintDefinitionMaintainVolume extends ConstraintDefinition {
public ConstraintDefinitionMaintainVolume(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { public ConstraintDefinitionMaintainVolume(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
super(constraintData, ownerOMA, blenderContext); super(constraintData, ownerOMA, blenderContext);
volume = (float) Math.sqrt(((Number) constraintData.getFieldValue("volume")).floatValue()); volume = (float) Math.sqrt(((Number) constraintData.getFieldValue("volume")).floatValue());
trackToBeChanged = volume != 1 && (flag & (FLAG_MASK_X | FLAG_MASK_Y | FLAG_MASK_Z)) != 0;
} }
@Override @Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (volume != 1 && influence > 0) { if (trackToBeChanged && influence > 0) {
// the maintain volume constraint is applied directly to object's scale, so no need to do it again // the maintain volume constraint is applied directly to object's scale, so no need to do it again
// but in case of bones we need to make computations // but in case of bones we need to make computations
if (this.getOwner() instanceof Bone) { if (this.getOwner() instanceof Bone) {
@ -47,4 +53,9 @@ public class ConstraintDefinitionMaintainVolume extends ConstraintDefinition {
public String getConstraintTypeName() { public String getConstraintTypeName() {
return "Maintain volume"; return "Maintain volume";
} }
@Override
public boolean isTargetRequired() {
return false;
}
} }

@ -14,6 +14,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
public ConstraintDefinitionNull(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { public ConstraintDefinitionNull(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
super(constraintData, ownerOMA, blenderContext); super(constraintData, ownerOMA, blenderContext);
trackToBeChanged = false;
} }
@Override @Override
@ -25,4 +26,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() { public String getConstraintTypeName() {
return "Null"; return "Null";
} }
@Override
public boolean isTargetRequired() {
return false;
}
} }

@ -25,12 +25,13 @@ import com.jme3.scene.plugins.blender.file.Structure;
public ConstraintDefinitionRotLike(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) { public ConstraintDefinitionRotLike(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
super(constraintData, ownerOMA, blenderContext); super(constraintData, ownerOMA, blenderContext);
trackToBeChanged = (flag & (ROTLIKE_X | ROTLIKE_Y | ROTLIKE_Z)) != 0;
} }
@Override @Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if(influence == 0 || targetTransform == null) { if (influence == 0 || targetTransform == null || !trackToBeChanged) {
return ;// no need to do anything return;// no need to do anything
} }
Transform ownerTransform = this.getOwnerTransform(ownerSpace); Transform ownerTransform = this.getOwnerTransform(ownerSpace);
@ -78,4 +79,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() { public String getConstraintTypeName() {
return "Copy rotation"; return "Copy rotation";
} }
@Override
public boolean isTargetRequired() {
return true;
}
} }

@ -65,10 +65,15 @@ import com.jme3.scene.plugins.blender.file.Structure;
* if(limits[i][0] > limits[i][1]) { float temp = limits[i][0]; * if(limits[i][0] > limits[i][1]) { float temp = limits[i][0];
* limits[i][0] = limits[i][1]; limits[i][1] = temp; } } * limits[i][0] = limits[i][1]; limits[i][1] = temp; } }
*/ */
trackToBeChanged = (flag & (LIMIT_XROT | LIMIT_YROT | LIMIT_ZROT)) != 0;
} }
@Override @Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (influence == 0 || !trackToBeChanged) {
return;
}
Transform ownerTransform = this.getOwnerTransform(ownerSpace); Transform ownerTransform = this.getOwnerTransform(ownerSpace);
ownerTransform.getRotation().toAngles(angles); ownerTransform.getRotation().toAngles(angles);
@ -116,4 +121,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() { public String getConstraintTypeName() {
return "Limit rotation"; return "Limit rotation";
} }
@Override
public boolean isTargetRequired() {
return false;
}
} }

@ -27,12 +27,14 @@ import com.jme3.scene.plugins.blender.file.Structure;
// them // them
flag |= y << 1; flag |= y << 1;
flag |= z >> 1; flag |= z >> 1;
trackToBeChanged = (flag & (SIZELIKE_X | SIZELIKE_Y | SIZELIKE_Z)) != 0;
} }
} }
@Override @Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if(influence == 0 || targetTransform == null) { if (influence == 0 || targetTransform == null || !trackToBeChanged) {
return;// no need to do anything return;// no need to do anything
} }
Transform ownerTransform = this.getOwnerTransform(ownerSpace); Transform ownerTransform = this.getOwnerTransform(ownerSpace);
@ -64,4 +66,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() { public String getConstraintTypeName() {
return "Copy scale"; return "Copy scale";
} }
@Override
public boolean isTargetRequired() {
return true;
}
} }

@ -50,10 +50,15 @@ import com.jme3.scene.plugins.blender.file.Structure;
limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue(); limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue(); limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
} }
trackToBeChanged = (flag & (LIMIT_XMIN | LIMIT_XMAX | LIMIT_YMIN | LIMIT_YMAX | LIMIT_ZMIN | LIMIT_ZMAX)) != 0;
} }
@Override @Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (influence == 0 || !trackToBeChanged) {
return;
}
Transform ownerTransform = this.getOwnerTransform(ownerSpace); Transform ownerTransform = this.getOwnerTransform(ownerSpace);
Vector3f scale = ownerTransform.getScale(); Vector3f scale = ownerTransform.getScale();
@ -83,4 +88,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() { public String getConstraintTypeName() {
return "Limit scale"; return "Limit scale";
} }
@Override
public boolean isTargetRequired() {
return false;
}
} }

@ -35,8 +35,8 @@ public class ConstraintDefinitionTransLike extends ConstraintDefinition {
@Override @Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) { public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if(influence == 0 || targetTransform == null) { if (influence == 0 || targetTransform == null) {
return ;// no need to do anything return;// no need to do anything
} }
Object target = this.getTarget();// Bone or Node Object target = this.getTarget();// Bone or Node
Object owner = this.getOwner();// Bone or Node Object owner = this.getOwner();// Bone or Node
@ -73,4 +73,9 @@ public class ConstraintDefinitionTransLike extends ConstraintDefinition {
public String getConstraintTypeName() { public String getConstraintTypeName() {
return "Copy transforms"; return "Copy transforms";
} }
@Override
public boolean isTargetRequired() {
return true;
}
} }

@ -16,6 +16,7 @@ import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
public UnsupportedConstraintDefinition(String typeName) { public UnsupportedConstraintDefinition(String typeName) {
super(null, null, null); super(null, null, null);
this.typeName = typeName; this.typeName = typeName;
trackToBeChanged = false;
} }
@Override @Override
@ -31,4 +32,9 @@ import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
public String getConstraintTypeName() { public String getConstraintTypeName() {
return typeName; return typeName;
} }
@Override
public boolean isTargetRequired() {
return false;
}
} }

Loading…
Cancel
Save