فهرست منبع

Support for object animation constraints loading.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7684 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
Kae..pl 14 سال پیش
والد
کامیت
697a02e7c5

+ 679 - 667
engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ConstraintHelper.java

@@ -32,121 +32,121 @@ import java.util.logging.Level;
  */
 public class ConstraintHelper extends AbstractBlenderHelper {
 
-    /**
-     * A table containing implementations of influence functions for constraints. It should contain functions for
-     * blender at least 249 and higher.
-     */
-    protected static AbstractInfluenceFunction[] influenceFunctions;
-    /**
-     * Constraints stored for object with the given old memory address.
-     */
-    protected Map<Long, Constraint[]> constraints = new HashMap<Long, Constraint[]>();
-
-    /**
-     * Helper constructor. It's main task is to generate the affection functions. These functions are common to all
-     * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall
-     * consider refactoring. The constructor parses the given blender version and stores the result. Some
-     * functionalities may differ in different blender versions.
-     * @param blenderVersion
-     *        the version read from the blend file
-     */
-    public ConstraintHelper(String blenderVersion, DataRepository dataRepository) {
-        super(blenderVersion);
-        this.initializeConstraintFunctions(dataRepository);
-    }
+	/**
+	 * A table containing implementations of influence functions for constraints. It should contain functions for
+	 * blender at least 249 and higher.
+	 */
+	protected static AbstractInfluenceFunction[] influenceFunctions;
+	/**
+	 * Constraints stored for object with the given old memory address.
+	 */
+	protected Map<Long, Constraint[]> constraints = new HashMap<Long, Constraint[]>();
+
+	/**
+	 * Helper constructor. It's main task is to generate the affection functions. These functions are common to all
+	 * ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall
+	 * consider refactoring. The constructor parses the given blender version and stores the result. Some
+	 * functionalities may differ in different blender versions.
+	 * @param blenderVersion
+	 *        the version read from the blend file
+	 */
+	public ConstraintHelper(String blenderVersion, DataRepository dataRepository) {
+		super(blenderVersion);
+		this.initializeConstraintFunctions(dataRepository);
+	}
 
 	/**
 	 * This method initializes constraint functions for Blender 2.49.
 	 * @param dataRepository
 	 *           			the data repository
 	 */
-    private synchronized void initializeConstraintFunctions(DataRepository dataRepository) {
-    	if (influenceFunctions == null) {
-            influenceFunctions = new AbstractInfluenceFunction[ConstraintType.getLastDefinedTypeValue() + 1];
-            //ACTION constraint (TODO: to implement)
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ACTION.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ACTION, dataRepository) {
-            };
-
-            //CHILDOF constraint (TODO: to implement)
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CHILDOF.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CHILDOF, dataRepository) {
-            };
-
-            //CLAMPTO constraint
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CLAMPTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CLAMPTO, dataRepository) {
-
-                @Override
-                public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
-                    this.validateConstraintType(constraint.getData());
-                    LOGGER.log(Level.INFO, "{0} not active! Curves not yet implemented!", constraint.getName());//TODO: implement when curves are implemented
-                }
-            };
-
-            //DISTLIMIT constraint
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_DISTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_DISTLIMIT, dataRepository) {
-
-                @Override
-                public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
-                    Structure constraintStructure = constraint.getData();
-                    this.validateConstraintType(constraintStructure);
-                    Vector3f targetLocation = this.getTargetLocation(constraint);
-                    BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
-                    if (boneTrack != null) {
-                        //TODO: target vertex group !!!
-                        float dist = ((Number) constraintStructure.getFieldValue("dist")).floatValue();
-                        int mode = ((Number) constraintStructure.getFieldValue("mode")).intValue();
-
-                        int maxFrames = boneTrack.getTimes().length;
-                        Vector3f[] translations = boneTrack.getTranslations();
-                        for (int frame = 0; frame < maxFrames; ++frame) {
-                            Vector3f v = translations[frame].subtract(targetLocation);
-                            float currentDistance = v.length();
-                            float influence = constraint.getIpo().calculateValue(frame);
-                            float modifier = 0.0f;
-                            switch (mode) {
-                                case LIMITDIST_INSIDE:
-                                    if (currentDistance >= dist) {
-                                        modifier = (dist - currentDistance) / currentDistance;
-                                    }
-                                    break;
-                                case LIMITDIST_ONSURFACE:
-                                    modifier = (dist - currentDistance) / currentDistance;
-                                    break;
-                                case LIMITDIST_OUTSIDE:
-                                    if (currentDistance <= dist) {
-                                        modifier = (dist - currentDistance) / currentDistance;
-                                    }
-                                    break;
-                                default:
-                                    throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
-                            }
-                            translations[frame].addLocal(v.multLocal(modifier * influence));
-                        }
-                        boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales());
-                    }
-                }
-            };
-
-            //FOLLOWPATH constraint
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH, dataRepository) {
-
-                @Override
-                public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
-                    this.validateConstraintType(constraint.getData());
-                    LOGGER.log(Level.INFO, "{0} not active! Curves not yet implemented!", constraint.getName());//TODO: implement when curves are implemented
-                }
-            };
-
-            //KINEMATIC constraint (TODO: to implement)
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_KINEMATIC.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_KINEMATIC, dataRepository) {
-
-                @Override
-                public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
-                    Structure constraintStructure = constraint.getData();
-                    this.validateConstraintType(constraintStructure);
-                    /*Long boneOMA = constraint.getBoneOMA();
+	private synchronized void initializeConstraintFunctions(DataRepository dataRepository) {
+		if (influenceFunctions == null) {
+			influenceFunctions = new AbstractInfluenceFunction[ConstraintType.getLastDefinedTypeValue() + 1];
+			//ACTION constraint (TODO: to implement)
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ACTION.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ACTION, dataRepository) {
+			};
+
+			//CHILDOF constraint (TODO: to implement)
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CHILDOF.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CHILDOF, dataRepository) {
+			};
+
+			//CLAMPTO constraint
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_CLAMPTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_CLAMPTO, dataRepository) {
+
+				@Override
+				public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
+					this.validateConstraintType(constraint.getData());
+					LOGGER.log(Level.INFO, "{0} not active! Curves not yet implemented!", constraint.getName());//TODO: implement when curves are implemented
+				}
+			};
+
+			//DISTLIMIT constraint
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_DISTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_DISTLIMIT, dataRepository) {
+
+				@Override
+				public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
+					Structure constraintStructure = constraint.getData();
+					this.validateConstraintType(constraintStructure);
+					Vector3f targetLocation = this.getTargetLocation(constraint);
+					BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
+					if (boneTrack != null) {
+						//TODO: target vertex group !!!
+						float dist = ((Number) constraintStructure.getFieldValue("dist")).floatValue();
+						int mode = ((Number) constraintStructure.getFieldValue("mode")).intValue();
+
+						int maxFrames = boneTrack.getTimes().length;
+						Vector3f[] translations = boneTrack.getTranslations();
+						for (int frame = 0; frame < maxFrames; ++frame) {
+							Vector3f v = translations[frame].subtract(targetLocation);
+							float currentDistance = v.length();
+							float influence = constraint.getIpo().calculateValue(frame);
+							float modifier = 0.0f;
+							switch (mode) {
+								case LIMITDIST_INSIDE:
+									if (currentDistance >= dist) {
+										modifier = (dist - currentDistance) / currentDistance;
+									}
+									break;
+								case LIMITDIST_ONSURFACE:
+									modifier = (dist - currentDistance) / currentDistance;
+									break;
+								case LIMITDIST_OUTSIDE:
+									if (currentDistance <= dist) {
+										modifier = (dist - currentDistance) / currentDistance;
+									}
+									break;
+								default:
+									throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
+							}
+							translations[frame].addLocal(v.multLocal(modifier * influence));
+						}
+						boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales());
+					}
+				}
+			};
+
+			//FOLLOWPATH constraint
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH, dataRepository) {
+
+				@Override
+				public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
+					this.validateConstraintType(constraint.getData());
+					LOGGER.log(Level.INFO, "{0} not active! Curves not yet implemented!", constraint.getName());//TODO: implement when curves are implemented
+				}
+			};
+
+			//KINEMATIC constraint (TODO: to implement)
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_KINEMATIC.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_KINEMATIC, dataRepository) {
+
+				@Override
+				public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
+					Structure constraintStructure = constraint.getData();
+					this.validateConstraintType(constraintStructure);
+					/*Long boneOMA = constraint.getBoneOMA();
                     //IK solver is only attached to bones
                     Bone ownerBone = (Bone)dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE);
-                    
+
                     //get the target point
                     Object targetObject = this.getTarget(constraint, LoadedFeatureDataType.LOADED_FEATURE);
                     Vector3f pt = null;//Point Target
@@ -172,7 +172,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
                     }
                     Quaternion rotation = new Quaternion();
                     int maxFrames = bones[0].track.getTimes().length;//all tracks should have the same amount of frames
-                    
+
                     for(int frame = 0; frame < maxFrames; ++frame) {
                     float error = IK_SOLVER_ERROR;
                     int iteration = 0;
@@ -181,10 +181,10 @@ public class ConstraintHelper extends AbstractBlenderHelper {
                     for(int i = 0; i < bones.length - 1; ++i) {
                     Vector3f pe = bones[i].getEndPoint();
                     Vector3f pc = bones[i + 1].getWorldTranslation().clone();
-                    
+
                     Vector3f peSUBpc = pe.subtract(pc).normalizeLocal();
                     Vector3f ptSUBpc = pt.subtract(pc).normalizeLocal();
-                    
+
                     float theta = FastMath.acos(peSUBpc.dot(ptSUBpc));
                     Vector3f direction = peSUBpc.cross(ptSUBpc).normalizeLocal();
                     bones[i].rotate(rotation.fromAngleAxis(theta, direction), frame);
@@ -194,570 +194,582 @@ public class ConstraintHelper extends AbstractBlenderHelper {
                     }
                     System.out.println("error = " + error + "   iterations = " + iteration);
                     }
-                    
+
                     for(CalculationBone bone : bones) {
                     bone.applyCalculatedTracks();
                     }
-                    
+
                     System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
                     for(int i=0;i<bones.length;++i) {
                     System.out.println(Arrays.toString(bones[i].track.getTranslations()));
                     System.out.println(Arrays.toString(bones[i].track.getRotations()));
                     System.out.println("===============================");
                     }*/
-                }
-
-                /**
-                 * This method returns bones used for rotation calculations.
-                 * @param bone
-                 *        the bone to which the constraint is applied
-                 * @param skeleton
-                 *        the skeleton owning the bone and its ancestors
-                 * @param boneAnimation
-                 *        the bone animation data that stores the traces for the skeleton's bones
-                 * @return a list of bones to imitate the bone's movement during IK solving
-                 */
-                private CalculationBone[] getBonesToCalculate(Bone bone, Skeleton skeleton, BoneAnimation boneAnimation) {
-                    List<CalculationBone> bonesList = new ArrayList<CalculationBone>();
-                    Bone currentBone = bone;
-                    do {
-                        int boneIndex = skeleton.getBoneIndex(currentBone);
-                        for (int i = 0; i < boneAnimation.getTracks().length; ++i) {
-                            if (boneAnimation.getTracks()[i].getTargetBoneIndex() == boneIndex) {
-                                bonesList.add(new CalculationBone(currentBone, boneAnimation.getTracks()[i]));
-                                break;
-                            }
-                        }
-                        currentBone = currentBone.getParent();
-                    } while (currentBone != null);
-                    //attaching children
-                    CalculationBone[] result = bonesList.toArray(new CalculationBone[bonesList.size()]);
-                    for (int i = result.length - 1; i > 0; --i) {
-                        result[i].attachChild(result[i - 1]);
-                    }
-                    return result;
-                }
-            };
-
-            //LOCKTRACK constraint (TODO: to implement)
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCKTRACK.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCKTRACK, dataRepository) {
-            };
-
-            //LOCLIKE constraint
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIKE, dataRepository) {
-
-                @Override
-                public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
-                    Structure constraintData = constraint.getData();
-                    this.validateConstraintType(constraintData);
-                    BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
-                    if (boneTrack != null) {
-                        Vector3f targetLocation = this.getTargetLocation(constraint);
-                        int flag = ((Number) constraintData.getFieldValue("flag")).intValue();
-                        Vector3f[] translations = boneTrack.getTranslations();
-                        int maxFrames = translations.length;
-                        for (int frame = 0; frame < maxFrames; ++frame) {
-                            Vector3f offset = Vector3f.ZERO;
-                            if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original location to the copied location
-                                offset = translations[frame].clone();
-                            }
-
-                            if ((flag & LOCLIKE_X) != 0) {
-                                translations[frame].x = targetLocation.x;
-                                if ((flag & LOCLIKE_X_INVERT) != 0) {
-                                    translations[frame].x = -translations[frame].x;
-                                }
-                            } else if ((flag & LOCLIKE_Y) != 0) {
-                                translations[frame].y = targetLocation.y;
-                                if ((flag & LOCLIKE_Y_INVERT) != 0) {
-                                    translations[frame].y = -translations[frame].y;
-                                }
-                            } else if ((flag & LOCLIKE_Z) != 0) {
-                                translations[frame].z = targetLocation.z;
-                                if ((flag & LOCLIKE_Z_INVERT) != 0) {
-                                    translations[frame].z = -translations[frame].z;
-                                }
-                            }
-                            translations[frame].addLocal(offset);//TODO: ipo influence
-                        }
-                        boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales());
-                    }
-                }
-            };
-
-            //LOCLIMIT constraint
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIMIT, dataRepository) {
-
-                @Override
-                public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
-                    Structure constraintStructure = constraint.getData();
-                    this.validateConstraintType(constraintStructure);
-                    BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
-                    if (boneTrack != null) {
-                        int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue();
-                        Vector3f[] translations = boneTrack.getTranslations();
-                        int maxFrames = translations.length;
-                        for (int frame = 0; frame < maxFrames; ++frame) {
-                            float influence = constraint.getIpo().calculateValue(frame);
-                            if ((flag & LIMIT_XMIN) != 0) {
-                                float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue();
-                                if (translations[frame].x < xmin) {
-                                    translations[frame].x -= (translations[frame].x - xmin) * influence;
-                                }
-                            }
-                            if ((flag & LIMIT_XMAX) != 0) {
-                                float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue();
-                                if (translations[frame].x > xmax) {
-                                    translations[frame].x -= (translations[frame].x - xmax) * influence;
-                                }
-                            }
-                            if ((flag & LIMIT_YMIN) != 0) {
-                                float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue();
-                                if (translations[frame].y < ymin) {
-                                    translations[frame].y -= (translations[frame].y - ymin) * influence;
-                                }
-                            }
-                            if ((flag & LIMIT_YMAX) != 0) {
-                                float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue();
-                                if (translations[frame].y > ymax) {
-                                    translations[frame].y -= (translations[frame].y - ymax) * influence;
-                                }
-                            }
-                            if ((flag & LIMIT_ZMIN) != 0) {
-                                float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue();
-                                if (translations[frame].z < zmin) {
-                                    translations[frame].z -= (translations[frame].z - zmin) * influence;
-                                }
-                            }
-                            if ((flag & LIMIT_ZMAX) != 0) {
-                                float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue();
-                                if (translations[frame].z > zmax) {
-                                    translations[frame].z -= (translations[frame].z - zmax) * influence;
-                                }
-                            }//TODO: consider constraint space !!!
-                        }
-                        boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales());
-                    }
-                }
-            };
-
-            //MINMAX constraint (TODO: to implement)
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_MINMAX.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_MINMAX, dataRepository) {
-            };
-
-            //NULL constraint - does nothing
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_NULL.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_NULL, dataRepository) {
-            };
-
-            //PYTHON constraint (TODO: to implement)
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_PYTHON.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_PYTHON, dataRepository) {
-            };
-
-            //RIGIDBODYJOINT constraint (TODO: to implement)
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT, dataRepository) {
-            };
-
-            //ROTLIKE constraint
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIKE, dataRepository) {
-
-                @Override
-                public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
-                    Structure constraintData = constraint.getData();
-                    this.validateConstraintType(constraintData);
-                    BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
-                    if (boneTrack != null) {
-                        Quaternion targetRotation = this.getTargetRotation(constraint);
-                        int flag = ((Number) constraintData.getFieldValue("flag")).intValue();
-                        float[] targetAngles = targetRotation.toAngles(null);
-                        Quaternion[] rotations = boneTrack.getRotations();
-                        int maxFrames = rotations.length;
-                        for (int frame = 0; frame < maxFrames; ++frame) {
-                            float[] angles = rotations[frame].toAngles(null);
-
-                            Quaternion offset = Quaternion.IDENTITY;
-                            if ((flag & ROTLIKE_OFFSET) != 0) {//we add the original rotation to the copied rotation
-                                offset = rotations[frame].clone();
-                            }
-
-                            if ((flag & ROTLIKE_X) != 0) {
-                                angles[0] = targetAngles[0];
-                                if ((flag & ROTLIKE_X_INVERT) != 0) {
-                                    angles[0] = -angles[0];
-                                }
-                            } else if ((flag & ROTLIKE_Y) != 0) {
-                                angles[1] = targetAngles[1];
-                                if ((flag & ROTLIKE_Y_INVERT) != 0) {
-                                    angles[1] = -angles[1];
-                                }
-                            } else if ((flag & ROTLIKE_Z) != 0) {
-                                angles[2] = targetAngles[2];
-                                if ((flag & ROTLIKE_Z_INVERT) != 0) {
-                                    angles[2] = -angles[2];
-                                }
-                            }
-                            rotations[frame].fromAngles(angles).multLocal(offset);//TODO: ipo influence
-                        }
-                        boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales());
-                    }
-                }
-            };
-
-            //ROTLIMIT constraint
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIMIT, dataRepository) {
-
-                @Override
-                public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
-                    Structure constraintStructure = constraint.getData();
-                    this.validateConstraintType(constraintStructure);
-                    BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
-                    if (boneTrack != null) {
-                        int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue();
-                        Quaternion[] rotations = boneTrack.getRotations();
-                        int maxFrames = rotations.length;
-                        for (int frame = 0; frame < maxFrames; ++frame) {
-                            float[] angles = rotations[frame].toAngles(null);
-                            float influence = constraint.getIpo().calculateValue(frame);
-                            if ((flag & LIMIT_XROT) != 0) {
-                                float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue() * FastMath.DEG_TO_RAD;
-                                float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue() * FastMath.DEG_TO_RAD;
-                                float difference = 0.0f;
-                                if (angles[0] < xmin) {
-                                    difference = (angles[0] - xmin) * influence;
-                                } else if (angles[0] > xmax) {
-                                    difference = (angles[0] - xmax) * influence;
-                                }
-                                angles[0] -= difference;
-                            }
-                            if ((flag & LIMIT_YROT) != 0) {
-                                float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue() * FastMath.DEG_TO_RAD;
-                                float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue() * FastMath.DEG_TO_RAD;
-                                float difference = 0.0f;
-                                if (angles[1] < ymin) {
-                                    difference = (angles[1] - ymin) * influence;
-                                } else if (angles[1] > ymax) {
-                                    difference = (angles[1] - ymax) * influence;
-                                }
-                                angles[1] -= difference;
-                            }
-                            if ((flag & LIMIT_ZROT) != 0) {
-                                float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue() * FastMath.DEG_TO_RAD;
-                                float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue() * FastMath.DEG_TO_RAD;
-                                float difference = 0.0f;
-                                if (angles[2] < zmin) {
-                                    difference = (angles[2] - zmin) * influence;
-                                } else if (angles[2] > zmax) {
-                                    difference = (angles[2] - zmax) * influence;
-                                }
-                                angles[2] -= difference;
-                            }
-                            rotations[frame].fromAngles(angles);//TODO: consider constraint space !!!
-                        }
-                        boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales());
-                    }
-                }
-            };
-
-            //SHRINKWRAP constraint (TODO: to implement)
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP, dataRepository) {
-            };
-
-            //SIZELIKE constraint
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIKE, dataRepository) {
-
-                @Override
-                public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
-                    Structure constraintData = constraint.getData();
-                    this.validateConstraintType(constraintData);
-                    Vector3f targetScale = this.getTargetLocation(constraint);
-                    BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
-                    if (boneTrack != null) {
-                        int flag = ((Number) constraintData.getFieldValue("flag")).intValue();
-                        Vector3f[] scales = boneTrack.getScales();
-                        int maxFrames = scales.length;
-                        for (int frame = 0; frame < maxFrames; ++frame) {
-                            Vector3f offset = Vector3f.ZERO;
-                            if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original scale to the copied scale
-                                offset = scales[frame].clone();
-                            }
-
-                            if ((flag & SIZELIKE_X) != 0) {
-                                scales[frame].x = targetScale.x;
-                            } else if ((flag & SIZELIKE_Y) != 0) {
-                                scales[frame].y = targetScale.y;
-                            } else if ((flag & SIZELIKE_Z) != 0) {
-                                scales[frame].z = targetScale.z;
-                            }
-                            scales[frame].addLocal(offset);//TODO: ipo influence
-                            //TODO: add or multiply???
-                        }
-                        boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales);
-                    }
-                }
-            };
-
-            //SIZELIMIT constraint
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIMIT, dataRepository) {
-
-                @Override
-                public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
-                    Structure constraintStructure = constraint.getData();
-                    this.validateConstraintType(constraintStructure);
-                    BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
-                    if (boneTrack != null) {
-                        int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue();
-                        Vector3f[] scales = boneTrack.getScales();
-                        int maxFrames = scales.length;
-                        for (int frame = 0; frame < maxFrames; ++frame) {
-                            float influence = constraint.getIpo().calculateValue(frame);
-                            if ((flag & LIMIT_XMIN) != 0) {
-                                float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue();
-                                if (scales[frame].x < xmin) {
-                                    scales[frame].x -= (scales[frame].x - xmin) * influence;
-                                }
-                            }
-                            if ((flag & LIMIT_XMAX) != 0) {
-                                float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue();
-                                if (scales[frame].x > xmax) {
-                                    scales[frame].x -= (scales[frame].x - xmax) * influence;
-                                }
-                            }
-                            if ((flag & LIMIT_YMIN) != 0) {
-                                float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue();
-                                if (scales[frame].y < ymin) {
-                                    scales[frame].y -= (scales[frame].y - ymin) * influence;
-                                }
-                            }
-                            if ((flag & LIMIT_YMAX) != 0) {
-                                float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue();
-                                if (scales[frame].y > ymax) {
-                                    scales[frame].y -= (scales[frame].y - ymax) * influence;
-                                }
-                            }
-                            if ((flag & LIMIT_ZMIN) != 0) {
-                                float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue();
-                                if (scales[frame].z < zmin) {
-                                    scales[frame].z -= (scales[frame].z - zmin) * influence;
-                                }
-                            }
-                            if ((flag & LIMIT_ZMAX) != 0) {
-                                float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue();
-                                if (scales[frame].z > zmax) {
-                                    scales[frame].z -= (scales[frame].z - zmax) * influence;
-                                }
-                            }//TODO: consider constraint space !!!
-                        }
-                        boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales);
-                    }
-                }
-            };
-
-            //STRETCHTO constraint (TODO: to implement)
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_STRETCHTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_STRETCHTO, dataRepository) {
-            };
-
-            //TRANSFORM constraint (TODO: to implement)
-            influenceFunctions[ConstraintType.CONSTRAINT_TYPE_TRANSFORM.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_TRANSFORM, dataRepository) {
-            };
-        }
-    }
-    
-    /**
-     * This method reads constraints for for the given structure. The constraints are loaded only once for object/bone.
-     * @param ownerOMA
-     *        the owner's old memory address
-     * @param objectStructure
-     *        the structure we read constraint's for
-     * @param dataRepository
-     *        the data repository
-     * @throws BlenderFileException
-     */
-    public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException {
-        // reading influence ipos for the constraints
-        IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class);
-        Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();
-        Pointer pActions = (Pointer) objectStructure.getFieldValue("action");
-        if (pActions.isNotNull()) {
-            List<Structure> actions = pActions.fetchData(dataRepository.getInputStream());
-            for (Structure action : actions) {
-                Structure chanbase = (Structure) action.getFieldValue("chanbase");
-                List<Structure> actionChannels = chanbase.evaluateListBase(dataRepository);
-                for (Structure actionChannel : actionChannels) {
-                    Map<String, Ipo> ipos = new HashMap<String, Ipo>();
-                    Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels");
-                    List<Structure> constraintChannels = constChannels.evaluateListBase(dataRepository);
-                    for (Structure constraintChannel : constraintChannels) {
-                        Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");
-                        if (pIpo.isNotNull()) {
-                            String constraintName = constraintChannel.getFieldValue("name").toString();
-                            Ipo ipo = ipoHelper.createIpo(pIpo.fetchData(dataRepository.getInputStream()).get(0), dataRepository);
-                            ipos.put(constraintName, ipo);
-                        }
-                    }
-                    String actionName = actionChannel.getFieldValue("name").toString();
-                    constraintsIpos.put(actionName, ipos);
-                }
-            }
-        }
-
-        //loading constraints connected with the object's bones
-        List<Constraint> constraintsList = new ArrayList<Constraint>();
-        Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");//TODO: what if the object has two armatures ????
-        if (pPose.isNotNull()) {
-            //getting pose channels
-            List<Structure> poseChannels = ((Structure) pPose.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(dataRepository);
-            for (Structure poseChannel : poseChannels) {
-                Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());
-                //the name is read directly from structure because bone might not yet be loaded
-                String name = dataRepository.getFileBlock(boneOMA).getStructure(dataRepository).getFieldValue("name").toString();
-                List<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(dataRepository);
-                for (Structure constraint : constraints) {
-                    int type = ((Number) constraint.getFieldValue("type")).intValue();
-                    String constraintName = constraint.getFieldValue("name").toString();
-                    Ipo ipo = constraintsIpos.get(name).get(constraintName);
-                    if (ipo == null) {
-                        float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
-                        ipo = ipoHelper.createIpo(enforce);
-                    }
-                    Space ownerSpace = Space.valueOf(((Number) constraint.getFieldValue("ownspace")).byteValue());
-                    Space targetSpace = Space.valueOf(((Number) constraint.getFieldValue("tarspace")).byteValue());
-                    Constraint c = new Constraint(constraint, influenceFunctions[type], boneOMA, ownerSpace, targetSpace, ipo, dataRepository);
-                    constraintsList.add(c);
-                }
-            }
-        }
-        /* TODO: reading constraints for objects (implement when object's animation will be available)
-        List<Structure> constraintChannels = ((Structure)objectStructure.getFieldValue("constraintChannels")).evaluateListBase(dataRepository);
-        for(Structure constraintChannel : constraintChannels) {
-        System.out.println(constraintChannel);
-        }
-        
-        //loading constraints connected with the object itself (TODO: test this)
-        if(!this.constraints.containsKey(objectStructure.getOldMemoryAddress())) {
-        List<Structure> constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(dataRepository);
-        Constraint[] result = new Constraint[constraints.size()];
-        int i = 0;
-        for(Structure constraint : constraints) {
-        int type = ((Number)constraint.getFieldValue("type")).intValue();
-        String name = constraint.getFieldValue("name").toString();
-        result[i++] = new Constraint(constraint, influenceFunctions[type], null, dataRepository);//TODO: influence ipos for object animation
-        }
-        this.constraints.put(objectStructure.getOldMemoryAddress(), result);
-        }
-         */
-        if (constraintsList.size() > 0) {
-            this.constraints.put(objectStructure.getOldMemoryAddress(), constraintsList.toArray(new Constraint[constraintsList.size()]));
-        }
-    }
-
-    /**
-     * This method returns a list of constraints of the feature's constraints. The order of constraints is important.
-     * @param ownerOMA
-     *        the owner's old memory address
-     * @return a table of constraints for the feature specified by old memory address
-     */
-    public Constraint[] getConstraints(Long ownerOMA) {
-        return constraints.get(ownerOMA);
-    }
-
-    @Override
-    public void clearState() {
-        constraints.clear();
-    }
-
-    /**
-     * The purpose of this class is to imitate bone's movement when calculating inverse kinematics.
-     * @author Marcin Roguski
-     */
-    private static class CalculationBone extends Node {
-
-        /** The name of the bone. Only to be used in toString method. */
-        private String boneName;
-        /** The bone's tracks. Will be altered at the end of calculation process. */
-        private BoneTrack track;
-        /** The starting position of the bone. */
-        private Vector3f startTranslation;
-        /** The starting rotation of the bone. */
-        private Quaternion startRotation;
-        /** The starting scale of the bone. */
-        private Vector3f startScale;
-        private Vector3f[] translations;
-        private Quaternion[] rotations;
-        private Vector3f[] scales;
-
-        /**
-         * Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.
-         * @param bone
-         *        the bone this class will imitate
-         * @param track
-         *        the bone's tracks
-         */
-        public CalculationBone(Bone bone, BoneTrack track) {
-            this.boneName = bone.getName();
-            this.track = track;
-            this.startRotation = bone.getModelSpaceRotation().clone();
-            this.startTranslation = bone.getModelSpacePosition().clone();
-            this.startScale = bone.getModelSpaceScale().clone();
-            this.translations = track.getTranslations();
-            this.rotations = track.getRotations();
-            this.scales = track.getScales();
-            this.reset();
-        }
-
-        /**
-         * This method returns the end point of the bone. If the bone has parent it is calculated from the start point
-         * of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered
-         * to be 1 point up along Y axis (scale is applied if set to != 1.0);
-         * @return the end point of this bone
-         */
-        //TODO: set to Z axis if user defined it this way
-        public Vector3f getEndPoint() {
-            if (this.getParent() == null) {
-                return new Vector3f(0, this.getLocalScale().y, 0);
-            } else {
-                Node parent = this.getParent();
-                return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());
-            }
-        }
-
-        /**
-         * This method resets the calculation bone to the starting position.
-         */
-        public void reset() {
-            this.setLocalTranslation(startTranslation);
-            this.setLocalRotation(startRotation);
-            this.setLocalScale(startScale);
-        }
-
-        @Override
-        public int attachChild(Spatial child) {
-            if (this.getChildren() != null && this.getChildren().size() > 1) {
-                throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");
-            }
-            return super.attachChild(child);
-        }
-
-        public Spatial rotate(Quaternion rot, int frame) {
-            Spatial spatial = super.rotate(rot);
-            this.updateWorldTransforms();
-            if (this.getChildren() != null && this.getChildren().size() > 0) {
-                CalculationBone child = (CalculationBone) this.getChild(0);
-                child.updateWorldTransforms();
-            }
-            rotations[frame].set(this.getLocalRotation());
-            translations[frame].set(this.getLocalTranslation());
-            if (scales != null) {
-                scales[frame].set(this.getLocalScale());
-            }
-            return spatial;
-        }
-
-        public void applyCalculatedTracks() {
-            track.setKeyframes(track.getTimes(), translations, rotations);//TODO:scales
-        }
-
-        @Override
-        public String toString() {
-            return boneName + ": " + this.getLocalRotation() + " " + this.getLocalTranslation();
-        }
-    }
+				}
+
+				/**
+				 * This method returns bones used for rotation calculations.
+				 * @param bone
+				 *        the bone to which the constraint is applied
+				 * @param skeleton
+				 *        the skeleton owning the bone and its ancestors
+				 * @param boneAnimation
+				 *        the bone animation data that stores the traces for the skeleton's bones
+				 * @return a list of bones to imitate the bone's movement during IK solving
+				 */
+				private CalculationBone[] getBonesToCalculate(Bone bone, Skeleton skeleton, BoneAnimation boneAnimation) {
+					List<CalculationBone> bonesList = new ArrayList<CalculationBone>();
+					Bone currentBone = bone;
+					do {
+						int boneIndex = skeleton.getBoneIndex(currentBone);
+						for (int i = 0; i < boneAnimation.getTracks().length; ++i) {
+							if (boneAnimation.getTracks()[i].getTargetBoneIndex() == boneIndex) {
+								bonesList.add(new CalculationBone(currentBone, boneAnimation.getTracks()[i]));
+								break;
+							}
+						}
+						currentBone = currentBone.getParent();
+					} while (currentBone != null);
+					//attaching children
+					CalculationBone[] result = bonesList.toArray(new CalculationBone[bonesList.size()]);
+					for (int i = result.length - 1; i > 0; --i) {
+						result[i].attachChild(result[i - 1]);
+					}
+					return result;
+				}
+			};
+
+			//LOCKTRACK constraint (TODO: to implement)
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCKTRACK.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCKTRACK, dataRepository) {
+			};
+
+			//LOCLIKE constraint
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIKE, dataRepository) {
+
+				@Override
+				public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
+					Structure constraintData = constraint.getData();
+					this.validateConstraintType(constraintData);
+					BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
+					if (boneTrack != null) {
+						Vector3f targetLocation = this.getTargetLocation(constraint);
+						int flag = ((Number) constraintData.getFieldValue("flag")).intValue();
+						Vector3f[] translations = boneTrack.getTranslations();
+						int maxFrames = translations.length;
+						for (int frame = 0; frame < maxFrames; ++frame) {
+							Vector3f offset = Vector3f.ZERO;
+							if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original location to the copied location
+								offset = translations[frame].clone();
+							}
+
+							if ((flag & LOCLIKE_X) != 0) {
+								translations[frame].x = targetLocation.x;
+								if ((flag & LOCLIKE_X_INVERT) != 0) {
+									translations[frame].x = -translations[frame].x;
+								}
+							} else if ((flag & LOCLIKE_Y) != 0) {
+								translations[frame].y = targetLocation.y;
+								if ((flag & LOCLIKE_Y_INVERT) != 0) {
+									translations[frame].y = -translations[frame].y;
+								}
+							} else if ((flag & LOCLIKE_Z) != 0) {
+								translations[frame].z = targetLocation.z;
+								if ((flag & LOCLIKE_Z_INVERT) != 0) {
+									translations[frame].z = -translations[frame].z;
+								}
+							}
+							translations[frame].addLocal(offset);//TODO: ipo influence
+						}
+						boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales());
+					}
+				}
+			};
+
+			//LOCLIMIT constraint
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_LOCLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_LOCLIMIT, dataRepository) {
+
+				@Override
+				public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
+					Structure constraintStructure = constraint.getData();
+					this.validateConstraintType(constraintStructure);
+					BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
+					if (boneTrack != null) {
+						int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue();
+						Vector3f[] translations = boneTrack.getTranslations();
+						int maxFrames = translations.length;
+						for (int frame = 0; frame < maxFrames; ++frame) {
+							float influence = constraint.getIpo().calculateValue(frame);
+							if ((flag & LIMIT_XMIN) != 0) {
+								float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue();
+								if (translations[frame].x < xmin) {
+									translations[frame].x -= (translations[frame].x - xmin) * influence;
+								}
+							}
+							if ((flag & LIMIT_XMAX) != 0) {
+								float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue();
+								if (translations[frame].x > xmax) {
+									translations[frame].x -= (translations[frame].x - xmax) * influence;
+								}
+							}
+							if ((flag & LIMIT_YMIN) != 0) {
+								float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue();
+								if (translations[frame].y < ymin) {
+									translations[frame].y -= (translations[frame].y - ymin) * influence;
+								}
+							}
+							if ((flag & LIMIT_YMAX) != 0) {
+								float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue();
+								if (translations[frame].y > ymax) {
+									translations[frame].y -= (translations[frame].y - ymax) * influence;
+								}
+							}
+							if ((flag & LIMIT_ZMIN) != 0) {
+								float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue();
+								if (translations[frame].z < zmin) {
+									translations[frame].z -= (translations[frame].z - zmin) * influence;
+								}
+							}
+							if ((flag & LIMIT_ZMAX) != 0) {
+								float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue();
+								if (translations[frame].z > zmax) {
+									translations[frame].z -= (translations[frame].z - zmax) * influence;
+								}
+							}//TODO: consider constraint space !!!
+						}
+						boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales());
+					}
+				}
+			};
+
+			//MINMAX constraint (TODO: to implement)
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_MINMAX.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_MINMAX, dataRepository) {
+			};
+
+			//NULL constraint - does nothing
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_NULL.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_NULL, dataRepository) {
+			};
+
+			//PYTHON constraint (TODO: to implement)
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_PYTHON.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_PYTHON, dataRepository) {
+			};
+
+			//RIGIDBODYJOINT constraint (TODO: to implement)
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT, dataRepository) {
+			};
+
+			//ROTLIKE constraint
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIKE, dataRepository) {
+
+				@Override
+				public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
+					Structure constraintData = constraint.getData();
+					this.validateConstraintType(constraintData);
+					BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
+					if (boneTrack != null) {
+						Quaternion targetRotation = this.getTargetRotation(constraint);
+						int flag = ((Number) constraintData.getFieldValue("flag")).intValue();
+						float[] targetAngles = targetRotation.toAngles(null);
+						Quaternion[] rotations = boneTrack.getRotations();
+						int maxFrames = rotations.length;
+						for (int frame = 0; frame < maxFrames; ++frame) {
+							float[] angles = rotations[frame].toAngles(null);
+
+							Quaternion offset = Quaternion.IDENTITY;
+							if ((flag & ROTLIKE_OFFSET) != 0) {//we add the original rotation to the copied rotation
+								offset = rotations[frame].clone();
+							}
+
+							if ((flag & ROTLIKE_X) != 0) {
+								angles[0] = targetAngles[0];
+								if ((flag & ROTLIKE_X_INVERT) != 0) {
+									angles[0] = -angles[0];
+								}
+							} else if ((flag & ROTLIKE_Y) != 0) {
+								angles[1] = targetAngles[1];
+								if ((flag & ROTLIKE_Y_INVERT) != 0) {
+									angles[1] = -angles[1];
+								}
+							} else if ((flag & ROTLIKE_Z) != 0) {
+								angles[2] = targetAngles[2];
+								if ((flag & ROTLIKE_Z_INVERT) != 0) {
+									angles[2] = -angles[2];
+								}
+							}
+							rotations[frame].fromAngles(angles).multLocal(offset);//TODO: ipo influence
+						}
+						boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales());
+					}
+				}
+			};
+
+			//ROTLIMIT constraint
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_ROTLIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_ROTLIMIT, dataRepository) {
+
+				@Override
+				public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
+					Structure constraintStructure = constraint.getData();
+					this.validateConstraintType(constraintStructure);
+					BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
+					if (boneTrack != null) {
+						int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue();
+						Quaternion[] rotations = boneTrack.getRotations();
+						int maxFrames = rotations.length;
+						for (int frame = 0; frame < maxFrames; ++frame) {
+							float[] angles = rotations[frame].toAngles(null);
+							float influence = constraint.getIpo().calculateValue(frame);
+							if ((flag & LIMIT_XROT) != 0) {
+								float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue() * FastMath.DEG_TO_RAD;
+								float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue() * FastMath.DEG_TO_RAD;
+								float difference = 0.0f;
+								if (angles[0] < xmin) {
+									difference = (angles[0] - xmin) * influence;
+								} else if (angles[0] > xmax) {
+									difference = (angles[0] - xmax) * influence;
+								}
+								angles[0] -= difference;
+							}
+							if ((flag & LIMIT_YROT) != 0) {
+								float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue() * FastMath.DEG_TO_RAD;
+								float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue() * FastMath.DEG_TO_RAD;
+								float difference = 0.0f;
+								if (angles[1] < ymin) {
+									difference = (angles[1] - ymin) * influence;
+								} else if (angles[1] > ymax) {
+									difference = (angles[1] - ymax) * influence;
+								}
+								angles[1] -= difference;
+							}
+							if ((flag & LIMIT_ZROT) != 0) {
+								float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue() * FastMath.DEG_TO_RAD;
+								float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue() * FastMath.DEG_TO_RAD;
+								float difference = 0.0f;
+								if (angles[2] < zmin) {
+									difference = (angles[2] - zmin) * influence;
+								} else if (angles[2] > zmax) {
+									difference = (angles[2] - zmax) * influence;
+								}
+								angles[2] -= difference;
+							}
+							rotations[frame].fromAngles(angles);//TODO: consider constraint space !!!
+						}
+						boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), rotations, boneTrack.getScales());
+					}
+				}
+			};
+
+			//SHRINKWRAP constraint (TODO: to implement)
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP, dataRepository) {
+			};
+
+			//SIZELIKE constraint
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIKE.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIKE, dataRepository) {
+
+				@Override
+				public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
+					Structure constraintData = constraint.getData();
+					this.validateConstraintType(constraintData);
+					Vector3f targetScale = this.getTargetLocation(constraint);
+					BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
+					if (boneTrack != null) {
+						int flag = ((Number) constraintData.getFieldValue("flag")).intValue();
+						Vector3f[] scales = boneTrack.getScales();
+						int maxFrames = scales.length;
+						for (int frame = 0; frame < maxFrames; ++frame) {
+							Vector3f offset = Vector3f.ZERO;
+							if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original scale to the copied scale
+								offset = scales[frame].clone();
+							}
+
+							if ((flag & SIZELIKE_X) != 0) {
+								scales[frame].x = targetScale.x;
+							} else if ((flag & SIZELIKE_Y) != 0) {
+								scales[frame].y = targetScale.y;
+							} else if ((flag & SIZELIKE_Z) != 0) {
+								scales[frame].z = targetScale.z;
+							}
+							scales[frame].addLocal(offset);//TODO: ipo influence
+							//TODO: add or multiply???
+						}
+						boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales);
+					}
+				}
+			};
+
+			//SIZELIMIT constraint
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_SIZELIMIT.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_SIZELIMIT, dataRepository) {
+
+				@Override
+				public void affectAnimation(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
+					Structure constraintStructure = constraint.getData();
+					this.validateConstraintType(constraintStructure);
+					BoneTrack boneTrack = this.getBoneTrack(skeleton, boneAnimation, constraint);
+					if (boneTrack != null) {
+						int flag = ((Number) constraintStructure.getFieldValue("flag")).intValue();
+						Vector3f[] scales = boneTrack.getScales();
+						int maxFrames = scales.length;
+						for (int frame = 0; frame < maxFrames; ++frame) {
+							float influence = constraint.getIpo().calculateValue(frame);
+							if ((flag & LIMIT_XMIN) != 0) {
+								float xmin = ((Number) constraintStructure.getFieldValue("xmin")).floatValue();
+								if (scales[frame].x < xmin) {
+									scales[frame].x -= (scales[frame].x - xmin) * influence;
+								}
+							}
+							if ((flag & LIMIT_XMAX) != 0) {
+								float xmax = ((Number) constraintStructure.getFieldValue("xmax")).floatValue();
+								if (scales[frame].x > xmax) {
+									scales[frame].x -= (scales[frame].x - xmax) * influence;
+								}
+							}
+							if ((flag & LIMIT_YMIN) != 0) {
+								float ymin = ((Number) constraintStructure.getFieldValue("ymin")).floatValue();
+								if (scales[frame].y < ymin) {
+									scales[frame].y -= (scales[frame].y - ymin) * influence;
+								}
+							}
+							if ((flag & LIMIT_YMAX) != 0) {
+								float ymax = ((Number) constraintStructure.getFieldValue("ymax")).floatValue();
+								if (scales[frame].y > ymax) {
+									scales[frame].y -= (scales[frame].y - ymax) * influence;
+								}
+							}
+							if ((flag & LIMIT_ZMIN) != 0) {
+								float zmin = ((Number) constraintStructure.getFieldValue("zmin")).floatValue();
+								if (scales[frame].z < zmin) {
+									scales[frame].z -= (scales[frame].z - zmin) * influence;
+								}
+							}
+							if ((flag & LIMIT_ZMAX) != 0) {
+								float zmax = ((Number) constraintStructure.getFieldValue("zmax")).floatValue();
+								if (scales[frame].z > zmax) {
+									scales[frame].z -= (scales[frame].z - zmax) * influence;
+								}
+							}//TODO: consider constraint space !!!
+						}
+						boneTrack.setKeyframes(boneTrack.getTimes(), boneTrack.getTranslations(), boneTrack.getRotations(), scales);
+					}
+				}
+			};
+
+			//STRETCHTO constraint (TODO: to implement)
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_STRETCHTO.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_STRETCHTO, dataRepository) {
+			};
+
+			//TRANSFORM constraint (TODO: to implement)
+			influenceFunctions[ConstraintType.CONSTRAINT_TYPE_TRANSFORM.getConstraintId()] = new AbstractInfluenceFunction(ConstraintType.CONSTRAINT_TYPE_TRANSFORM, dataRepository) {
+			};
+		}
+	}
+
+	/**
+	 * This method reads constraints for for the given structure. The constraints are loaded only once for object/bone.
+	 * @param ownerOMA
+	 *        the owner's old memory address
+	 * @param objectStructure
+	 *        the structure we read constraint's for
+	 * @param dataRepository
+	 *        the data repository
+	 * @throws BlenderFileException
+	 */
+	public void loadConstraints(Structure objectStructure, DataRepository dataRepository) throws BlenderFileException {
+		// reading influence ipos for the constraints
+		IpoHelper ipoHelper = dataRepository.getHelper(IpoHelper.class);
+		Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();
+		Pointer pActions = (Pointer) objectStructure.getFieldValue("action");
+		if (pActions.isNotNull()) {
+			List<Structure> actions = pActions.fetchData(dataRepository.getInputStream());
+			for (Structure action : actions) {
+				Structure chanbase = (Structure) action.getFieldValue("chanbase");
+				List<Structure> actionChannels = chanbase.evaluateListBase(dataRepository);
+				for (Structure actionChannel : actionChannels) {
+					Map<String, Ipo> ipos = new HashMap<String, Ipo>();
+					Structure constChannels = (Structure) actionChannel.getFieldValue("constraintChannels");
+					List<Structure> constraintChannels = constChannels.evaluateListBase(dataRepository);
+					for (Structure constraintChannel : constraintChannels) {
+						Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");
+						if (pIpo.isNotNull()) {
+							String constraintName = constraintChannel.getFieldValue("name").toString();
+							Ipo ipo = ipoHelper.createIpo(pIpo.fetchData(dataRepository.getInputStream()).get(0), dataRepository);
+							ipos.put(constraintName, ipo);
+						}
+					}
+					String actionName = actionChannel.getFieldValue("name").toString();
+					constraintsIpos.put(actionName, ipos);
+				}
+			}
+		}
+
+		//loading constraints connected with the object's bones
+		List<Constraint> constraintsList = new ArrayList<Constraint>();
+		Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");//TODO: what if the object has two armatures ????
+		if (pPose.isNotNull()) {
+			//getting pose channels
+			List<Structure> poseChannels = ((Structure) pPose.fetchData(dataRepository.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(dataRepository);
+			for (Structure poseChannel : poseChannels) {
+				Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());
+				//the name is read directly from structure because bone might not yet be loaded
+				String name = dataRepository.getFileBlock(boneOMA).getStructure(dataRepository).getFieldValue("name").toString();
+				List<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(dataRepository);
+				for (Structure constraint : constraints) {
+					int type = ((Number) constraint.getFieldValue("type")).intValue();
+					String constraintName = constraint.getFieldValue("name").toString();
+					Ipo ipo = constraintsIpos.get(name).get(constraintName);
+					if (ipo == null) {
+						float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
+						ipo = ipoHelper.createIpo(enforce);
+					}
+					Space ownerSpace = Space.valueOf(((Number) constraint.getFieldValue("ownspace")).byteValue());
+					Space targetSpace = Space.valueOf(((Number) constraint.getFieldValue("tarspace")).byteValue());
+					Constraint c = new Constraint(constraint, influenceFunctions[type], boneOMA, ownerSpace, targetSpace, ipo, dataRepository);
+					constraintsList.add(c);
+				}
+			}
+		}
+		// TODO: reading constraints for objects (implement when object's animation will be available)
+		List<Structure> constraintChannels = ((Structure)objectStructure.getFieldValue("constraintChannels")).evaluateListBase(dataRepository);
+		for(Structure constraintChannel : constraintChannels) {
+			System.out.println(constraintChannel);
+		}
+
+		//loading constraints connected with the object itself (TODO: test this)
+		if(!this.constraints.containsKey(objectStructure.getOldMemoryAddress())) {
+			List<Structure> constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(dataRepository);
+			Constraint[] result = new Constraint[constraints.size()];
+			int i = 0;
+			
+			for(Structure constraint : constraints) {
+				int type = ((Number)constraint.getFieldValue("type")).intValue();
+				String constraintName = constraint.getFieldValue("name").toString();
+				String objectName = objectStructure.getName();
+				
+				Map<String, Ipo> objectConstraintsIpos = constraintsIpos.get(objectName);
+				Ipo ipo = objectConstraintsIpos!=null ? objectConstraintsIpos.get(constraintName) : null;
+				if (ipo == null) {
+					float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
+					ipo = ipoHelper.createIpo(enforce);
+				}
+				Space ownerSpace = Space.valueOf(((Number) constraint.getFieldValue("ownspace")).byteValue());
+				Space targetSpace = Space.valueOf(((Number) constraint.getFieldValue("tarspace")).byteValue());
+				result[i++] = new Constraint(constraint, influenceFunctions[type], null, 
+											 ownerSpace, targetSpace, ipo, dataRepository);//TODO: influence ipos for object animation
+			}
+			this.constraints.put(objectStructure.getOldMemoryAddress(), result);
+		}
+		
+		if (constraintsList.size() > 0) {
+			this.constraints.put(objectStructure.getOldMemoryAddress(), constraintsList.toArray(new Constraint[constraintsList.size()]));
+		}
+	}
+
+	/**
+	 * This method returns a list of constraints of the feature's constraints. The order of constraints is important.
+	 * @param ownerOMA
+	 *        the owner's old memory address
+	 * @return a table of constraints for the feature specified by old memory address
+	 */
+	public Constraint[] getConstraints(Long ownerOMA) {
+		return constraints.get(ownerOMA);
+	}
+
+	@Override
+	public void clearState() {
+		constraints.clear();
+	}
+
+	/**
+	 * The purpose of this class is to imitate bone's movement when calculating inverse kinematics.
+	 * @author Marcin Roguski
+	 */
+	private static class CalculationBone extends Node {
+
+		/** The name of the bone. Only to be used in toString method. */
+		private String boneName;
+		/** The bone's tracks. Will be altered at the end of calculation process. */
+		private BoneTrack track;
+		/** The starting position of the bone. */
+		private Vector3f startTranslation;
+		/** The starting rotation of the bone. */
+		private Quaternion startRotation;
+		/** The starting scale of the bone. */
+		private Vector3f startScale;
+		private Vector3f[] translations;
+		private Quaternion[] rotations;
+		private Vector3f[] scales;
+
+		/**
+		 * Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.
+		 * @param bone
+		 *        the bone this class will imitate
+		 * @param track
+		 *        the bone's tracks
+		 */
+		public CalculationBone(Bone bone, BoneTrack track) {
+			this.boneName = bone.getName();
+			this.track = track;
+			this.startRotation = bone.getModelSpaceRotation().clone();
+			this.startTranslation = bone.getModelSpacePosition().clone();
+			this.startScale = bone.getModelSpaceScale().clone();
+			this.translations = track.getTranslations();
+			this.rotations = track.getRotations();
+			this.scales = track.getScales();
+			this.reset();
+		}
+
+		/**
+		 * This method returns the end point of the bone. If the bone has parent it is calculated from the start point
+		 * of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered
+		 * to be 1 point up along Y axis (scale is applied if set to != 1.0);
+		 * @return the end point of this bone
+		 */
+		//TODO: set to Z axis if user defined it this way
+		public Vector3f getEndPoint() {
+			if (this.getParent() == null) {
+				return new Vector3f(0, this.getLocalScale().y, 0);
+			} else {
+				Node parent = this.getParent();
+				return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());
+			}
+		}
+
+		/**
+		 * This method resets the calculation bone to the starting position.
+		 */
+		public void reset() {
+			this.setLocalTranslation(startTranslation);
+			this.setLocalRotation(startRotation);
+			this.setLocalScale(startScale);
+		}
+
+		@Override
+		public int attachChild(Spatial child) {
+			if (this.getChildren() != null && this.getChildren().size() > 1) {
+				throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");
+			}
+			return super.attachChild(child);
+		}
+
+		public Spatial rotate(Quaternion rot, int frame) {
+			Spatial spatial = super.rotate(rot);
+			this.updateWorldTransforms();
+			if (this.getChildren() != null && this.getChildren().size() > 0) {
+				CalculationBone child = (CalculationBone) this.getChild(0);
+				child.updateWorldTransforms();
+			}
+			rotations[frame].set(this.getLocalRotation());
+			translations[frame].set(this.getLocalTranslation());
+			if (scales != null) {
+				scales[frame].set(this.getLocalScale());
+			}
+			return spatial;
+		}
+
+		public void applyCalculatedTracks() {
+			track.setKeyframes(track.getTimes(), translations, rotations);//TODO:scales
+		}
+
+		@Override
+		public String toString() {
+			return boneName + ": " + this.getLocalRotation() + " " + this.getLocalTranslation();
+		}
+	}
 }

+ 4 - 6
engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ModifierHelper.java

@@ -663,16 +663,14 @@ public class ModifierHelper extends AbstractBlenderHelper {
 
 			Vector3f[] translations = new Vector3f[tablesLength];
 			Quaternion[] rotations = new Quaternion[tablesLength];
-			Vector3f[] scales = sourceScales == null ? null : new Vector3f[tablesLength];
+			Vector3f[] scales = new Vector3f[tablesLength];
 			for (int j = 0; j < tablesLength; ++j) {
 				translations[j] = sourceTranslations[j].clone();
 				rotations[j] = sourceRotations[j].clone();
-				if (sourceScales != null) {// only scales may not be applied
-					scales[j] = sourceScales[j].clone();
-				}
+				scales[j] = sourceScales != null ? sourceScales[j].clone() : new Vector3f(1.0f, 1.0f, 1.0f);
 			}
-			boneTracks[i] = new BoneTrack(sourceTracks[i].getTargetBoneIndex(), sourceTracks[i].getTimes(),// times do not change, no need
-																											// to clone them,
+			// times do not change, no need to clone them
+			boneTracks[i] = new BoneTrack(sourceTracks[i].getTargetBoneIndex(), sourceTracks[i].getTimes(),
 					translations, rotations, scales);
 		}
 		result.setTracks(boneTracks);

+ 1 - 1
engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ObjectHelper.java

@@ -403,7 +403,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
 			Bone bone = new Bone(null);
 			bone.setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale());
 
-			return new Modifier(Modifier.ARMATURE_MODIFIER_DATA, new AnimData(new Skeleton(new Bone[] {bone}), animations), null);
+			return new Modifier(Modifier.ARMATURE_MODIFIER_DATA, new AnimData(new Skeleton(new Bone[] {bone}), animations), objectStructure.getOldMemoryAddress());
 		}
 		return null;
 	}

+ 1 - 1
engine/src/blender/com/jme3/scene/plugins/blender/structures/AbstractInfluenceFunction.java

@@ -117,7 +117,7 @@ public abstract class AbstractInfluenceFunction {
     protected BoneTrack getBoneTrack(Skeleton skeleton, BoneAnimation boneAnimation, Constraint constraint) {
         Long boneOMA = constraint.getBoneOMA();
         Bone bone = (Bone) dataRepository.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE);
-        int boneIndex = skeleton.getBoneIndex(bone);
+        int boneIndex = bone==null ? 0 : skeleton.getBoneIndex(bone);//bone==null may mean the object animation
         if (boneIndex != -1) {
             //searching for track for this bone
             for (BoneTrack boneTrack : boneAnimation.getTracks()) {

BIN
engine/src/test-data/Blender/2.4x/constraints.blend