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. 28
      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. 170
      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. 16
      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. 30
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLike.java
  12. 27
      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. 14
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLike.java
  16. 16
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLimit.java
  17. 15
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLike.java
  18. 16
      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

@ -67,9 +67,9 @@ 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. */
@ -366,6 +366,30 @@ public class BoneContext {
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
public String toString() {
return "BoneContext: " + boneName;

@ -62,7 +62,7 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
}
}
}
return true;
return constraintDefinition == null ? true : constraintDefinition.isTargetRequired();
}
@Override
@ -70,4 +70,19 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
super.apply(frame);
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();
/**
* @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).
* @param frame
* the frame of the animation
*/
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;
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
@ -163,4 +180,9 @@ public abstract class Constraint {
}
return true;
}
@Override
public String toString() {
return "Constraint(name = " + name + ", def = " + constraintDefinition + ")";
}
}

@ -1,6 +1,8 @@
package com.jme3.scene.plugins.blender.constraints;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -132,7 +134,7 @@ public class SimulationNode {
}
}
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);
} else {
animations = blenderContext.getAnimations(featureOMA);
@ -206,7 +208,7 @@ public class SimulationNode {
int maxFrame = (int) animationTimeBoundaries[0];
float maxTime = animationTimeBoundaries[1];
VirtualTrack vTrack = new VirtualTrack(maxFrame, maxTime);
VirtualTrack vTrack = new VirtualTrack(spatial.getName(), maxFrame, maxTime);
for (Track track : animation.getTracks()) {
for (int frame = 0; frame < maxFrame; ++frame) {
spatial.setLocalTranslation(((SpatialTrack) track).getTranslations()[frame]);
@ -252,6 +254,8 @@ public class SimulationNode {
if (animations != null) {
TempVars vars = TempVars.get();
AnimChannel animChannel = animControl.createChannel();
List<Bone> bonesWithConstraints = this.collectBonesWithConstraints(skeleton);
for (Animation animation : animations) {
float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation);
int maxFrame = (int) animationTimeBoundaries[0];
@ -271,11 +275,8 @@ public class SimulationNode {
}
// ... and then apply constraints from the root bone to the last child ...
for (Bone rootBone : skeleton.getRoots()) {
if (skeleton.getBoneIndex(rootBone) > 0) {
// ommit the 0 - indexed root bone as it is the bone added by importer
this.applyConstraints(rootBone, alteredOmas, frame);
}
for (Bone rootBone : bonesWithConstraints) {
this.applyConstraints(rootBone, alteredOmas, frame);
}
// ... add virtual tracks if neccessary, for bones that were altered but had no tracks before ...
@ -283,7 +284,7 @@ public class SimulationNode {
BoneContext boneContext = blenderContext.getBoneContext(boneOMA);
int boneIndex = skeleton.getBoneIndex(boneContext.getBone());
if (!tracks.containsKey(boneIndex)) {
tracks.put(boneIndex, new VirtualTrack(maxFrame, maxTime));
tracks.put(boneIndex, new VirtualTrack(boneContext.getBone().getName(), maxFrame, maxTime));
}
}
alteredOmas.clear();
@ -292,12 +293,12 @@ public class SimulationNode {
for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
Bone bone = skeleton.getBone(trackEntry.getKey());
Transform startTransform = boneStartTransforms.get(bone);
// track contains differences between the frame position and bind positions of bones/spatials
Vector3f bonePositionDifference = bone.getLocalPosition().subtract(startTransform.getTranslation());
Quaternion boneRotationDifference = startTransform.getRotation().inverse().mult(bone.getLocalRotation()).normalizeLocal();
Vector3f boneScaleDifference = bone.getLocalScale().divide(startTransform.getScale());
trackEntry.getValue().setTransform(frame, new Transform(bonePositionDifference, boneRotationDifference, boneScaleDifference));
}
}
@ -349,9 +350,6 @@ public class SimulationNode {
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
* can have different lengths so here the maximum one is being found.
@ -376,7 +518,7 @@ public class SimulationNode {
*/
private float[] computeAnimationTimeBoundaries(Animation animation) {
int maxFrame = Integer.MIN_VALUE;
float maxTime = Float.MIN_VALUE;
float maxTime = -Float.MAX_VALUE;
for (Track track : animation.getTracks()) {
if (track instanceof BoneTrack) {
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) {
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) {
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)
*/
/* package */class VirtualTrack {
/** The name of the track (for debugging purposes). */
private String name;
/** The last frame for the track. */
public int maxFrame;
/** The max time for the track. */
@ -35,7 +37,8 @@ import com.jme3.math.Vector3f;
* @param maxTime
* 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.maxTime = maxTime;
}
@ -101,7 +104,7 @@ import com.jme3.math.Vector3f;
*/
private float[] createTimes() {
float[] times = new float[maxFrame];
float dT = maxTime / (float) maxFrame;
float dT = maxTime / maxFrame;
float t = 0;
for (int i = 0; i < maxFrame; ++i) {
times[i] = t;
@ -143,4 +146,20 @@ import com.jme3.math.Vector3f;
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;
/** Stores the OMA addresses of all features whose transform had been altered beside the constraint owner. */
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
@ -52,6 +54,20 @@ public abstract class ConstraintDefinition {
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
* 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>)
*/
public abstract void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence);
@Override
public String toString() {
return this.getConstraintTypeName();
}
}

@ -26,18 +26,17 @@ import com.jme3.scene.plugins.blender.file.Structure;
mode = ((Number) constraintData.getFieldValue("mode")).intValue();
dist = ((Number) constraintData.getFieldValue("dist")).floatValue();
}
@Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null &&
blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)) {
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null && blenderContext.getBoneContext(ownerOMA).is(BoneContext.CONNECTED_TO_PARENT)) {
// distance limit does not work on bones who are connected to their parent
return;
}
if(influence == 0 || targetTransform == null) {
return ;// no need to do anything
if (influence == 0 || targetTransform == null) {
return;// no need to do anything
}
Transform ownerTransform = this.getOwnerTransform(ownerSpace);
Vector3f v = ownerTransform.getTranslation().subtract(targetTransform.getTranslation());
@ -73,6 +72,11 @@ import com.jme3.scene.plugins.blender.file.Structure;
this.applyOwnerTransform(ownerTransform, ownerSpace);
}
@Override
public boolean isTargetRequired() {
return true;
}
@Override
public String getConstraintTypeName() {
return "Limit distance";

@ -29,8 +29,6 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
private int bonesAffected;
/** The total length of the bone chain. Useful for optimisation of computations speed in some cases. */
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. */
private boolean useTail;
/** The amount of iterations of the algorithm. */
@ -43,23 +41,23 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
useTail = (flag & FLAG_USE_TAIL) != 0;
if ((flag & FLAG_POSITION) == 0) {
needToCompute = false;
trackToBeChanged = false;
}
if (needToCompute) {
if (trackToBeChanged) {
alteredOmas = new HashSet<Long>();
}
}
@Override
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
}
Quaternion q = new Quaternion();
Vector3f t = targetTransform.getTranslation();
List<BoneContext> bones = this.loadBones();
if(bones.size() == 0) {
if (bones.size() == 0) {
return;// no need to do anything
}
float distanceFromTarget = Float.MAX_VALUE;
@ -186,4 +184,20 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
}
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;
}
}

@ -34,27 +34,30 @@ import com.jme3.scene.plugins.blender.file.Structure;
int invZ = flag & LOCLIKE_Z_INVERT;
// clear the other flags to swap them
flag &= LOCLIKE_X | LOCLIKE_X_INVERT | LOCLIKE_OFFSET;
flag |= y << 1;
flag |= invY << 1;
flag |= z >> 1;
flag |= invZ >> 1;
trackToBeChanged = (flag & LOCLIKE_X) != 0 || (flag & LOCLIKE_Y) != 0 || (flag & LOCLIKE_Z) != 0;
}
}
@Override
public boolean isTrackToBeChanged() {
// location copy does not work on bones who are connected to their parent
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 (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
if (influence == 0 || targetTransform == null || !this.isTrackToBeChanged()) {
return;
}
if(influence == 0 || targetTransform == null) {
return ;// no need to do anything
}
Transform ownerTransform = this.getOwnerTransform(ownerSpace);
Vector3f ownerLocation = ownerTransform.getTranslation();
Vector3f targetLocation = targetTransform.getTranslation();
@ -88,7 +91,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
ownerLocation.addLocal(startLocation);
}
this.applyOwnerTransform(ownerTransform, ownerSpace);
}
@ -96,4 +99,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() {
return "Copy location";
}
@Override
public boolean isTargetRequired() {
return true;
}
}

@ -52,18 +52,24 @@ import com.jme3.scene.plugins.blender.file.Structure;
limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).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
public boolean isTrackToBeChanged() {
// location limit does not work on bones who are connected to their parent
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 (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
return;
if (influence == 0 || !this.isTrackToBeChanged()) {
return;// no need to do anything
}
Transform ownerTransform = this.getOwnerTransform(ownerSpace);
Vector3f translation = ownerTransform.getTranslation();
if ((flag & LIMIT_XMIN) != 0 && translation.x < limits[0][0]) {
@ -84,7 +90,7 @@ 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;
}
this.applyOwnerTransform(ownerTransform, ownerSpace);
}
@ -92,4 +98,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() {
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.file.Structure;
/**
* This class represents 'Maintain volume' constraint type in blender.
*
* @author Marcin Roguski (Kaelthas)
*/
public class ConstraintDefinitionMaintainVolume extends ConstraintDefinition {
private static final int FLAG_MASK_X = 0;
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) {
super(constraintData, ownerOMA, blenderContext);
volume = (float) Math.sqrt(((Number) constraintData.getFieldValue("volume")).floatValue());
trackToBeChanged = volume != 1 && (flag & (FLAG_MASK_X | FLAG_MASK_Y | FLAG_MASK_Z)) != 0;
}
@Override
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
// but in case of bones we need to make computations
if (this.getOwner() instanceof Bone) {
@ -47,4 +53,9 @@ public class ConstraintDefinitionMaintainVolume extends ConstraintDefinition {
public String getConstraintTypeName() {
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) {
super(constraintData, ownerOMA, blenderContext);
trackToBeChanged = false;
}
@Override
@ -25,4 +26,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() {
return "Null";
}
@Override
public boolean isTargetRequired() {
return false;
}
}

@ -25,15 +25,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
public ConstraintDefinitionRotLike(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
super(constraintData, ownerOMA, blenderContext);
trackToBeChanged = (flag & (ROTLIKE_X | ROTLIKE_Y | ROTLIKE_Z)) != 0;
}
@Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if(influence == 0 || targetTransform == null) {
return ;// no need to do anything
if (influence == 0 || targetTransform == null || !trackToBeChanged) {
return;// no need to do anything
}
Transform ownerTransform = this.getOwnerTransform(ownerSpace);
Quaternion ownerRotation = ownerTransform.getRotation();
ownerAngles = ownerRotation.toAngles(ownerAngles);
targetAngles = targetTransform.getRotation().toAngles(targetAngles);
@ -70,7 +71,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
// ownerLocation.addLocal(startLocation);
// TODO
}
this.applyOwnerTransform(ownerTransform, ownerSpace);
}
@ -78,4 +79,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() {
return "Copy rotation";
}
@Override
public boolean isTargetRequired() {
return true;
}
}

@ -65,12 +65,17 @@ import com.jme3.scene.plugins.blender.file.Structure;
* if(limits[i][0] > limits[i][1]) { float temp = limits[i][0];
* limits[i][0] = limits[i][1]; limits[i][1] = temp; } }
*/
trackToBeChanged = (flag & (LIMIT_XROT | LIMIT_YROT | LIMIT_ZROT)) != 0;
}
@Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (influence == 0 || !trackToBeChanged) {
return;
}
Transform ownerTransform = this.getOwnerTransform(ownerSpace);
ownerTransform.getRotation().toAngles(angles);
// make sure that the rotations are always in range [0, 2PI)
// TODO: same comment as in constructor
@ -108,7 +113,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
angles[2] -= difference;
}
ownerTransform.getRotation().fromAngles(angles);
this.applyOwnerTransform(ownerTransform, ownerSpace);
}
@ -116,4 +121,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() {
return "Limit rotation";
}
@Override
public boolean isTargetRequired() {
return false;
}
}

@ -27,16 +27,18 @@ import com.jme3.scene.plugins.blender.file.Structure;
// them
flag |= y << 1;
flag |= z >> 1;
trackToBeChanged = (flag & (SIZELIKE_X | SIZELIKE_Y | SIZELIKE_Z)) != 0;
}
}
@Override
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
}
Transform ownerTransform = this.getOwnerTransform(ownerSpace);
Vector3f ownerScale = ownerTransform.getScale();
Vector3f targetScale = targetTransform.getScale();
@ -56,7 +58,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
ownerScale.z = targetScale.z * influence + (1.0f - influence) * ownerScale.z;
}
ownerScale.addLocal(offset);
this.applyOwnerTransform(ownerTransform, ownerSpace);
}
@ -64,4 +66,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() {
return "Copy scale";
}
@Override
public boolean isTargetRequired() {
return true;
}
}

@ -50,12 +50,17 @@ import com.jme3.scene.plugins.blender.file.Structure;
limits[2][0] = ((Number) constraintData.getFieldValue("zmin")).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
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if (influence == 0 || !trackToBeChanged) {
return;
}
Transform ownerTransform = this.getOwnerTransform(ownerSpace);
Vector3f scale = ownerTransform.getScale();
if ((flag & LIMIT_XMIN) != 0 && scale.x < limits[0][0]) {
scale.x -= (scale.x - limits[0][0]) * influence;
@ -75,7 +80,7 @@ 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;
}
this.applyOwnerTransform(ownerTransform, ownerSpace);
}
@ -83,4 +88,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
public String getConstraintTypeName() {
return "Limit scale";
}
@Override
public boolean isTargetRequired() {
return false;
}
}

@ -35,8 +35,8 @@ public class ConstraintDefinitionTransLike extends ConstraintDefinition {
@Override
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
if(influence == 0 || targetTransform == null) {
return ;// no need to do anything
if (influence == 0 || targetTransform == null) {
return;// no need to do anything
}
Object target = this.getTarget();// Bone or Node
Object owner = this.getOwner();// Bone or Node
@ -73,4 +73,9 @@ public class ConstraintDefinitionTransLike extends ConstraintDefinition {
public String getConstraintTypeName() {
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) {
super(null, null, null);
this.typeName = typeName;
trackToBeChanged = false;
}
@Override
@ -31,4 +32,9 @@ import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
public String getConstraintTypeName() {
return typeName;
}
@Override
public boolean isTargetRequired() {
return false;
}
}

Loading…
Cancel
Save