Prechádzať zdrojové kódy

Updating blender sources to the most recent state.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/branches/gradle-restructure@10999 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
Kae..pl 11 rokov pred
rodič
commit
f71490c7dd
17 zmenil súbory, kde vykonal 744 pridanie a 897 odobranie
  1. 145 36
      jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
  2. 19 14
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java
  3. 7 5
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java
  4. 4 0
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java
  5. 0 29
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationData.java
  6. 420 0
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java
  7. 0 276
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java
  8. 4 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java
  9. 8 8
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java
  10. 0 194
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/IpoHelper.java
  11. 3 3
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java
  12. 7 8
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
  13. 6 8
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java
  14. 107 143
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java
  15. 5 78
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java
  16. 0 89
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java
  17. 9 4
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java

+ 145 - 36
jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java

@@ -33,9 +33,14 @@ package com.jme3.asset;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Queue;
 
+import com.jme3.animation.Animation;
 import com.jme3.bounding.BoundingVolume;
 import com.jme3.collision.Collidable;
 import com.jme3.collision.CollisionResults;
@@ -52,7 +57,6 @@ import com.jme3.scene.LightNode;
 import com.jme3.scene.Node;
 import com.jme3.scene.SceneGraphVisitor;
 import com.jme3.scene.Spatial;
-import com.jme3.scene.plugins.blender.animations.AnimationData;
 import com.jme3.texture.Texture;
 
 /**
@@ -61,66 +65,70 @@ import com.jme3.texture.Texture;
  */
 public class BlenderKey extends ModelKey {
 
-    protected static final int         DEFAULT_FPS               = 25;
+    protected static final int          DEFAULT_FPS               = 25;
     /**
      * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time
      * between the frames.
      */
-    protected int                      fps                       = DEFAULT_FPS;
+    protected int                       fps                       = DEFAULT_FPS;
     /**
      * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.
      */
-    protected int                      featuresToLoad            = FeaturesToLoad.ALL;
+    protected int                       featuresToLoad            = FeaturesToLoad.ALL;
     /** This variable determines if assets that are not linked to the objects should be loaded. */
-    protected boolean                  loadUnlinkedAssets;
+    protected boolean                   loadUnlinkedAssets;
     /** The root path for all the assets. */
-    protected String                   assetRootPath;
+    protected String                    assetRootPath;
     /** This variable indicate if Y axis is UP axis. If not then Z is up. By default set to true. */
-    protected boolean                  fixUpAxis                 = true;
+    protected boolean                   fixUpAxis                 = true;
     /** Generated textures resolution (PPU - Pixels Per Unit). */
-    protected int                      generatedTexturePPU       = 128;
+    protected int                       generatedTexturePPU       = 128;
     /**
      * The name of world settings that the importer will use. If not set or specified name does not occur in the file
      * then the first world settings in the file will be used.
      */
-    protected String                   usedWorld;
+    protected String                    usedWorld;
     /**
      * User's default material that is set fo objects that have no material definition in blender. The default value is
      * null. If the value is null the importer will use its own default material (gray color - like in blender).
      */
-    protected Material                 defaultMaterial;
+    protected Material                  defaultMaterial;
     /** Face cull mode. By default it is disabled. */
-    protected FaceCullMode             faceCullMode              = FaceCullMode.Back;
+    protected FaceCullMode              faceCullMode              = FaceCullMode.Back;
     /**
      * Variable describes which layers will be loaded. N-th bit set means N-th layer will be loaded.
      * If set to -1 then the current layer will be loaded.
      */
-    protected int                      layersToLoad              = -1;
+    protected int                       layersToLoad              = -1;
     /** A variable that toggles the object custom properties loading. */
-    protected boolean                  loadObjectProperties      = true;
+    protected boolean                   loadObjectProperties      = true;
     /**
      * Maximum texture size. Might be dependant on the graphic card.
      * This value is taken from <b>org.lwjgl.opengl.GL11.GL_MAX_TEXTURE_SIZE</b>.
      */
-    protected int                      maxTextureSize            = 8192;
+    protected int                       maxTextureSize            = 8192;
     /** Allows to toggle generated textures loading. Disabled by default because it very often takes too much memory and needs to be used wisely. */
-    protected boolean                  loadGeneratedTextures;
+    protected boolean                   loadGeneratedTextures;
     /** Tells if the mipmaps will be generated by jme or not. By default generation is dependant on the blender settings. */
-    protected MipmapGenerationMethod   mipmapGenerationMethod    = MipmapGenerationMethod.GENERATE_WHEN_NEEDED;
+    protected MipmapGenerationMethod    mipmapGenerationMethod    = MipmapGenerationMethod.GENERATE_WHEN_NEEDED;
     /**
      * If the sky has only generated textures applied then they will have the following size (both width and height). If 2d textures are used then the generated
      * textures will get their proper size.
      */
-    protected int                      skyGeneratedTextureSize   = 1000;
+    protected int                       skyGeneratedTextureSize   = 1000;
     /** The radius of a shape that will be used while creating the generated texture for the sky. The higher it is the larger part of the texture will be seen. */
-    protected float                    skyGeneratedTextureRadius = 1;
+    protected float                     skyGeneratedTextureRadius = 1;
     /** The shape against which the generated texture for the sky will be created. */
-    protected SkyGeneratedTextureShape skyGeneratedTextureShape  = SkyGeneratedTextureShape.SPHERE;
+    protected SkyGeneratedTextureShape  skyGeneratedTextureShape  = SkyGeneratedTextureShape.SPHERE;
     /**
      * This field tells if the importer should optimise the use of textures or not. If set to true, then textures of the same mapping type will be merged together
      * and textures that in the final result will never be visible - will be discarded.
      */
-    protected boolean                  optimiseTextures;
+    protected boolean                   optimiseTextures;
+    /** A map between node name and its animation names. */
+    protected Map<String, List<String>> nodeAnimationMap          = new HashMap<String, List<String>>();
+    /** A map between node name and its skeleton animation names. */
+    protected Map<String, List<String>> skeletonAnimationMap      = new HashMap<String, List<String>>();
 
     /**
      * Constructor used by serialization mechanisms.
@@ -473,6 +481,58 @@ public class BlenderKey extends ModelKey {
         return defaultMaterial;
     }
 
+    /**
+     * Adds spatial animation name for specified node.
+     * @param nodeName
+     *            the name of the node
+     * @param animationName
+     *            the spatial animation name
+     */
+    public void addNodeAnimation(String nodeName, String animationName) {
+        List<String> animations = nodeAnimationMap.get(nodeName);
+        if (animations == null) {
+            animations = new ArrayList<String>();
+            nodeAnimationMap.put(nodeName, animations);
+        }
+        animations.add(animationName);
+    }
+
+    /**
+     * Returns all spatial animation names for the given node.
+     * @param nodeName
+     *            the name of the node
+     * @return all spatial animations names or null if none are defined
+     */
+    public List<String> getNodeAnimationNames(String nodeName) {
+        return nodeAnimationMap.get(nodeName);
+    }
+
+    /**
+     * Adds bone animation name for specified node.
+     * @param nodeName
+     *            the name of the node
+     * @param animationName
+     *            the bone animation name
+     */
+    public void addSkeletonAnimation(String nodeName, String animationName) {
+        List<String> animations = skeletonAnimationMap.get(nodeName);
+        if (animations == null) {
+            animations = new ArrayList<String>();
+            skeletonAnimationMap.put(nodeName, animations);
+        }
+        animations.add(animationName);
+    }
+
+    /**
+     * Returns all bone animation names for the given node.
+     * @param nodeName
+     *            the name of the node
+     * @return all bone animations names or null if none are defined
+     */
+    public List<String> getSkeletonAnimationNames(String nodeName) {
+        return skeletonAnimationMap.get(nodeName);
+    }
+
     @Override
     public void write(JmeExporter e) throws IOException {
         super.write(e);
@@ -492,6 +552,22 @@ public class BlenderKey extends ModelKey {
         oc.write(skyGeneratedTextureRadius, "sky-generated-texture-radius", 1f);
         oc.write(skyGeneratedTextureShape, "sky-generated-texture-shape", SkyGeneratedTextureShape.SPHERE);
         oc.write(optimiseTextures, "optimise-textures", false);
+
+        oc.write(nodeAnimationMap.size(), "node-anims-map-size", 0);
+        int counter = 0;
+        for (Entry<String, List<String>> entry : nodeAnimationMap.entrySet()) {
+            oc.write(entry.getKey(), "node-anim-" + counter, null);
+            oc.write(entry.getValue().toArray(new String[entry.getValue().size()]), "node-anims-" + counter, null);
+            ++counter;
+        }
+
+        oc.write(skeletonAnimationMap.size(), "skeleton-anims-map-size", 0);
+        counter = 0;
+        for (Entry<String, List<String>> entry : skeletonAnimationMap.entrySet()) {
+            oc.write(entry.getKey(), "skeleton-anim-" + counter, null);
+            oc.write(entry.getValue().toArray(new String[entry.getValue().size()]), "skeleton-anims-" + counter, null);
+            ++counter;
+        }
     }
 
     @Override
@@ -513,6 +589,26 @@ public class BlenderKey extends ModelKey {
         skyGeneratedTextureRadius = ic.readFloat("sky-generated-texture-radius", 1f);
         skyGeneratedTextureShape = ic.readEnum("sky-generated-texture-shape", SkyGeneratedTextureShape.class, SkyGeneratedTextureShape.SPHERE);
         optimiseTextures = ic.readBoolean("optimise-textures", false);
+
+        int animsSize = ic.readInt("node-anims-map-size", 0);
+        nodeAnimationMap = new HashMap<String, List<String>>(animsSize);
+        if (animsSize > 0) {
+            for (int i = 0; i < animsSize; ++i) {
+                String nodeName = ic.readString("node-anim-" + i, null);
+                String[] anims = ic.readStringArray("node-anims-" + i, null);
+                nodeAnimationMap.put(nodeName, new ArrayList<String>(Arrays.asList(anims)));// must create ArrayList because 'asList' method returns unmodifiable list
+            }
+        }
+
+        animsSize = ic.readInt("skeleton-anims-map-size", 0);
+        skeletonAnimationMap = new HashMap<String, List<String>>(animsSize);
+        if (animsSize > 0) {
+            for (int i = 0; i < animsSize; ++i) {
+                String nodeName = ic.readString("skeleton-anim-" + i, null);
+                String[] anims = ic.readStringArray("skeleton-anims-" + i, null);
+                skeletonAnimationMap.put(nodeName, new ArrayList<String>(Arrays.asList(anims)));// must create ArrayList because 'asList' method returns unmodifiable list
+            }
+        }
     }
 
     @Override
@@ -532,7 +628,9 @@ public class BlenderKey extends ModelKey {
         result = prime * result + (loadUnlinkedAssets ? 1231 : 1237);
         result = prime * result + maxTextureSize;
         result = prime * result + (mipmapGenerationMethod == null ? 0 : mipmapGenerationMethod.hashCode());
+        result = prime * result + (nodeAnimationMap == null ? 0 : nodeAnimationMap.hashCode());
         result = prime * result + (optimiseTextures ? 1231 : 1237);
+        result = prime * result + (skeletonAnimationMap == null ? 0 : skeletonAnimationMap.hashCode());
         result = prime * result + Float.floatToIntBits(skyGeneratedTextureRadius);
         result = prime * result + (skyGeneratedTextureShape == null ? 0 : skyGeneratedTextureShape.hashCode());
         result = prime * result + skyGeneratedTextureSize;
@@ -545,10 +643,7 @@ public class BlenderKey extends ModelKey {
         if (this == obj) {
             return true;
         }
-        if (!super.equals(obj)) {
-            return false;
-        }
-        if (this.getClass() != obj.getClass()) {
+        if (!(obj instanceof BlenderKey)) {
             return false;
         }
         BlenderKey other = (BlenderKey) obj;
@@ -599,9 +694,23 @@ public class BlenderKey extends ModelKey {
         if (mipmapGenerationMethod != other.mipmapGenerationMethod) {
             return false;
         }
+        if (nodeAnimationMap == null) {
+            if (other.nodeAnimationMap != null) {
+                return false;
+            }
+        } else if (!nodeAnimationMap.equals(other.nodeAnimationMap)) {
+            return false;
+        }
         if (optimiseTextures != other.optimiseTextures) {
             return false;
         }
+        if (skeletonAnimationMap == null) {
+            if (other.skeletonAnimationMap != null) {
+                return false;
+            }
+        } else if (!skeletonAnimationMap.equals(other.skeletonAnimationMap)) {
+            return false;
+        }
         if (Float.floatToIntBits(skyGeneratedTextureRadius) != Float.floatToIntBits(other.skyGeneratedTextureRadius)) {
             return false;
         }
@@ -662,28 +771,28 @@ public class BlenderKey extends ModelKey {
     public static class LoadingResults extends Spatial {
 
         /** Bitwise mask of features that are to be loaded. */
-        private final int           featuresToLoad;
+        private final int        featuresToLoad;
         /** The scenes from the file. */
-        private List<Node>          scenes;
+        private List<Node>       scenes;
         /** Objects from all scenes. */
-        private List<Node>          objects;
+        private List<Node>       objects;
         /** Materials from all objects. */
-        private List<Material>      materials;
+        private List<Material>   materials;
         /** Textures from all objects. */
-        private List<Texture>       textures;
+        private List<Texture>    textures;
         /** Animations of all objects. */
-        private List<AnimationData> animations;
+        private List<Animation>  animations;
         /** All cameras from the file. */
-        private List<CameraNode>    cameras;
+        private List<CameraNode> cameras;
         /** All lights from the file. */
-        private List<LightNode>     lights;
+        private List<LightNode>  lights;
         /** Loaded sky. */
-        private Spatial             sky;
+        private Spatial          sky;
         /**
          * The background color of the render loaded from the horizon color of the world. If no world is used than the gray color
          * is set to default (as in blender editor.
          */
-        private ColorRGBA           backgroundColor = ColorRGBA.Gray;
+        private ColorRGBA        backgroundColor = ColorRGBA.Gray;
 
         /**
          * Private constructor prevents users to create an instance of this class from outside the
@@ -705,7 +814,7 @@ public class BlenderKey extends ModelKey {
                     }
                 }
                 if ((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) {
-                    animations = new ArrayList<AnimationData>();
+                    animations = new ArrayList<Animation>();
                 }
             }
             if ((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) {
@@ -839,7 +948,7 @@ public class BlenderKey extends ModelKey {
         /**
          * @return all loaded animations
          */
-        public List<AnimationData> getAnimations() {
+        public List<Animation> getAnimations() {
             return animations;
         }
 

+ 19 - 14
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java

@@ -39,6 +39,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Stack;
 
+import com.jme3.animation.Animation;
 import com.jme3.animation.Bone;
 import com.jme3.animation.Skeleton;
 import com.jme3.asset.AssetManager;
@@ -46,7 +47,6 @@ import com.jme3.asset.BlenderKey;
 import com.jme3.material.Material;
 import com.jme3.math.ColorRGBA;
 import com.jme3.scene.Node;
-import com.jme3.scene.plugins.blender.animations.AnimationData;
 import com.jme3.scene.plugins.blender.animations.BoneContext;
 import com.jme3.scene.plugins.blender.constraints.Constraint;
 import com.jme3.scene.plugins.blender.file.BlenderInputStream;
@@ -100,8 +100,8 @@ public class BlenderContext {
     private Stack<Structure>                    parentStack            = new Stack<Structure>();
     /** A list of constraints for the specified object. */
     protected Map<Long, List<Constraint>>       constraints            = new HashMap<Long, List<Constraint>>();
-    /** Anim data loaded for features. */
-    private Map<Long, AnimationData>            animData               = new HashMap<Long, AnimationData>();
+    /** Animations loaded for features. */
+    private Map<Long, List<Animation>>          animations             = new HashMap<Long, List<Animation>>();
     /** Loaded skeletons. */
     private Map<Long, Skeleton>                 skeletons              = new HashMap<Long, Skeleton>();
     /** A map between skeleton and node it modifies. */
@@ -405,28 +405,33 @@ public class BlenderContext {
         }
         return result;
     }
-
+    
     /**
-     * This method sets the anim data for the specified OMA of its owner.
+     * This method adds the animation for the specified OMA of its owner.
      * 
      * @param ownerOMA
      *            the owner's old memory address
-     * @param animData
-     *            the animation data for the feature specified by ownerOMA
-     */
-    public void setAnimData(Long ownerOMA, AnimationData animData) {
-        this.animData.put(ownerOMA, animData);
+     * @param animation
+     *            the animation for the feature specified by ownerOMA
+     */
+    public void addAnimation(Long ownerOMA, Animation animation) {
+        List<Animation> animList = animations.get(ownerOMA);
+        if(animList == null) {
+            animList = new ArrayList<Animation>();
+            animations.put(ownerOMA, animList);
+        }
+        animations.put(ownerOMA, animList);
     }
-
+    
     /**
      * This method returns the animation data for the specified owner.
      * 
      * @param ownerOMA
      *            the old memory address of the animation data owner
-     * @return the animation data or null if none exists
+     * @return the animation or null if none exists
      */
-    public AnimationData getAnimData(Long ownerOMA) {
-        return animData.get(ownerOMA);
+    public List<Animation> getAnimations(Long ownerOMA) {
+        return animations.get(ownerOMA);
     }
 
     /**

+ 7 - 5
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java

@@ -47,8 +47,7 @@ import com.jme3.scene.CameraNode;
 import com.jme3.scene.LightNode;
 import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
-import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
-import com.jme3.scene.plugins.blender.animations.IpoHelper;
+import com.jme3.scene.plugins.blender.animations.AnimationHelper;
 import com.jme3.scene.plugins.blender.cameras.CameraHelper;
 import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
 import com.jme3.scene.plugins.blender.curves.CurvesHelper;
@@ -85,6 +84,10 @@ public class BlenderLoader implements AssetLoader {
             List<FileBlockHeader> sceneBlocks = new ArrayList<FileBlockHeader>();
             BlenderKey blenderKey = blenderContext.getBlenderKey();
             LoadingResults loadingResults = blenderKey.prepareLoadingResults();
+            
+            AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
+            animationHelper.loadAnimations();
+            
             for (FileBlockHeader block : blocks) {
                 switch (block.getCode()) {
                     case FileBlockHeader.BLOCK_OB00:// Object
@@ -240,7 +243,7 @@ public class BlenderLoader implements AssetLoader {
         blenderContext.setBlenderKey(blenderKey);
 
         // creating helpers
-        blenderContext.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber(), blenderContext));
+        blenderContext.putHelper(AnimationHelper.class, new AnimationHelper(inputStream.getVersionNumber(), blenderContext));
         blenderContext.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber(), blenderContext));
         blenderContext.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber(), blenderContext));
         blenderContext.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber(), blenderContext));
@@ -250,10 +253,9 @@ public class BlenderLoader implements AssetLoader {
         blenderContext.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber(), blenderContext));
         blenderContext.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber(), blenderContext));
         blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext));
-        blenderContext.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber(), blenderContext));
         blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderContext));
         blenderContext.putHelper(LandscapeHelper.class, new LandscapeHelper(inputStream.getVersionNumber(), blenderContext));
-
+        
         // reading the blocks (dna block is automatically saved in the blender context when found)
         FileBlockHeader sceneFileBlock = null;
         do {

+ 4 - 0
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java

@@ -43,6 +43,7 @@ import com.jme3.asset.BlenderKey.FeaturesToLoad;
 import com.jme3.scene.LightNode;
 import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.blender.animations.AnimationHelper;
 import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
 import com.jme3.scene.plugins.blender.file.BlenderFileException;
 import com.jme3.scene.plugins.blender.file.FileBlockHeader;
@@ -62,6 +63,9 @@ public class BlenderModelLoader extends BlenderLoader {
         try {
             this.setup(assetInfo);
 
+            AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
+            animationHelper.loadAnimations();
+            
             BlenderKey blenderKey = blenderContext.getBlenderKey();
             List<Node> rootObjects = new ArrayList<Node>();
             for (FileBlockHeader block : blocks) {

+ 0 - 29
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationData.java

@@ -1,29 +0,0 @@
-package com.jme3.scene.plugins.blender.animations;
-
-import java.util.List;
-
-import com.jme3.animation.Animation;
-import com.jme3.animation.Skeleton;
-
-/**
- * A simple class that sotres animation data.
- * If skeleton is null then we deal with object animation.
- * 
- * @author Marcin Roguski (Kaelthas)
- */
-public class AnimationData {
-    /** The skeleton. */
-    public final Skeleton skeleton;
-    /** The animations list. */
-    public final List<Animation> anims;
-
-    public AnimationData(List<Animation> anims) {
-        this.anims = anims;
-        skeleton = null;
-    }
-    
-    public AnimationData(Skeleton skeleton, List<Animation> anims) {
-        this.skeleton = skeleton;
-        this.anims = anims;
-    }
-}

+ 420 - 0
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java

@@ -0,0 +1,420 @@
+package com.jme3.scene.plugins.blender.animations;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.Animation;
+import com.jme3.animation.Bone;
+import com.jme3.animation.BoneTrack;
+import com.jme3.animation.Skeleton;
+import com.jme3.animation.SkeletonControl;
+import com.jme3.animation.SpatialTrack;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Node;
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.curves.BezierCurve;
+import com.jme3.scene.plugins.blender.file.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.BlenderInputStream;
+import com.jme3.scene.plugins.blender.file.FileBlockHeader;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.objects.ObjectHelper;
+
+/**
+ * The helper class that helps in animations loading.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class AnimationHelper extends AbstractBlenderHelper {
+    private static final Logger        LOGGER  = Logger.getLogger(AnimationHelper.class.getName());
+
+    /** A map of blender actions. */
+    private Map<String, BlenderAction> actions = new HashMap<String, BlenderAction>();
+
+    public AnimationHelper(String blenderVersion, BlenderContext blenderContext) {
+        super(blenderVersion, blenderContext);
+    }
+
+    /**
+     * Loads all animations that are stored in the blender file. The animations are not yet applied to the scene features.
+     * This should be called before objects are loaded.
+     * @throws BlenderFileException
+     *             an exception is thrown when problems with blender file reading occur
+     */
+    public void loadAnimations() throws BlenderFileException {
+        LOGGER.info("Loading animations that will be later applied to scene features.");
+        List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
+        if (actionHeaders != null) {
+            for (FileBlockHeader header : actionHeaders) {
+                Structure actionStructure = header.getStructure(blenderContext);
+                LOGGER.log(Level.INFO, "Found animation: {0}.", actionStructure.getName());
+                actions.put(actionStructure.getName(), this.getTracks(actionStructure, blenderContext));
+            }
+        }
+    }
+
+    /**
+     * The method applies animations to the given node. The names of the animations should be the same as actions names in the blender file.
+     * @param node
+     *            the node to whom the animations will be applied
+     * @param animationNames
+     *            the names of the animations to be applied
+     */
+    public void applyAnimations(Node node, List<String> animationNames) {
+        if (animationNames != null && animationNames.size() > 0) {
+            List<Animation> animations = new ArrayList<Animation>();
+            for (String animationName : animationNames) {
+                BlenderAction action = actions.get(animationName);
+                if (action != null) {
+                    SpatialTrack[] tracks = action.toTracks(node);
+                    if (tracks != null && tracks.length > 0) {
+                        Animation spatialAnimation = new Animation(animationName, action.getAnimationTime());
+                        spatialAnimation.setTracks(tracks);
+                        animations.add(spatialAnimation);
+                        blenderContext.addAnimation((Long) node.getUserData(ObjectHelper.OMA_MARKER), spatialAnimation);
+                    }
+                } else {
+                    LOGGER.log(Level.WARNING, "Cannot find animation named: {0}.", animationName);
+                }
+            }
+
+            if (animations.size() > 0) {
+                AnimControl control = new AnimControl();
+                HashMap<String, Animation> anims = new HashMap<String, Animation>(animations.size());
+                for (int i = 0; i < animations.size(); ++i) {
+                    Animation animation = animations.get(i);
+                    anims.put(animation.getName(), animation);
+                }
+                control.setAnimations(anims);
+                node.addControl(control);
+            }
+        }
+    }
+
+    /**
+     * The method applies skeleton animations to the given node.
+     * @param node
+     *            the node where the animations will be applied
+     * @param skeleton
+     *            the skeleton of the node
+     * @param animationNames
+     *            the names of the skeleton animations
+     */
+    public void applyAnimations(Node node, Skeleton skeleton, List<String> animationNames) {
+        node.addControl(new SkeletonControl(skeleton));
+        blenderContext.setNodeForSkeleton(skeleton, node);
+
+        if (animationNames != null && animationNames.size() > 0) {
+            List<Animation> animations = new ArrayList<Animation>();
+            for (String animationName : animationNames) {
+                BlenderAction action = actions.get(animationName);
+                if (action != null) {
+                    BoneTrack[] tracks = action.toTracks(skeleton);
+                    if (tracks != null && tracks.length > 0) {
+                        Animation boneAnimation = new Animation(animationName, action.getAnimationTime());
+                        boneAnimation.setTracks(tracks);
+                        animations.add(boneAnimation);
+                        for (BoneTrack track : tracks) {
+                            Bone bone = skeleton.getBone(track.getTargetBoneIndex());
+                            BoneContext boneContext = blenderContext.getBoneContext(bone);
+                            blenderContext.addAnimation(boneContext.getBoneOma(), boneAnimation);
+                        }
+                    }
+                } else {
+                    LOGGER.log(Level.WARNING, "Cannot find animation named: {0}.", animationName);
+                }
+            }
+            if (animations.size() > 0) {
+                AnimControl control = new AnimControl(skeleton);
+                HashMap<String, Animation> anims = new HashMap<String, Animation>(animations.size());
+                for (int i = 0; i < animations.size(); ++i) {
+                    Animation animation = animations.get(i);
+                    anims.put(animation.getName(), animation);
+                }
+                control.setAnimations(anims);
+                node.addControl(control);
+            }
+        }
+    }
+
+    /**
+     * This method creates an ipo object used for interpolation calculations.
+     * 
+     * @param ipoStructure
+     *            the structure with ipo definition
+     * @param blenderContext
+     *            the blender context
+     * @return the ipo object
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    public Ipo fromIpoStructure(Structure ipoStructure, BlenderContext blenderContext) throws BlenderFileException {
+        Structure curvebase = (Structure) ipoStructure.getFieldValue("curve");
+
+        // preparing bezier curves
+        Ipo result = null;
+        List<Structure> curves = curvebase.evaluateListBase();// IpoCurve
+        if (curves.size() > 0) {
+            BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
+            int frame = 0;
+            for (Structure curve : curves) {
+                Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
+                List<Structure> bezTriples = pBezTriple.fetchData();
+                int type = ((Number) curve.getFieldValue("adrcode")).intValue();
+                bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
+            }
+            curves.clear();
+            result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
+            blenderContext.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);
+        }
+        return result;
+    }
+
+    /**
+     * This method creates an ipo with only a single value. No track type is
+     * specified so do not use it for calculating tracks.
+     * 
+     * @param constValue
+     *            the value of this ipo
+     * @return constant ipo
+     */
+    public Ipo fromValue(float constValue) {
+        return new ConstIpo(constValue);
+    }
+
+    /**
+     * This method retuns the bone tracks for animation.
+     * 
+     * @param actionStructure
+     *            the structure containing the tracks
+     * @param blenderContext
+     *            the blender context
+     * @return a list of tracks for the specified animation
+     * @throws BlenderFileException
+     *             an exception is thrown when there are problems with the blend
+     *             file
+     */
+    private BlenderAction getTracks(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
+        if (blenderVersion < 250) {
+            return this.getTracks249(actionStructure, blenderContext);
+        } else {
+            return this.getTracks250(actionStructure, blenderContext);
+        }
+    }
+
+    /**
+     * This method retuns the bone tracks for animation for blender version 2.50
+     * and higher.
+     * 
+     * @param actionStructure
+     *            the structure containing the tracks
+     * @param blenderContext
+     *            the blender context
+     * @return a list of tracks for the specified animation
+     * @throws BlenderFileException
+     *             an exception is thrown when there are problems with the blend
+     *             file
+     */
+    private BlenderAction getTracks250(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
+        LOGGER.log(Level.FINE, "Getting tracks!");
+        Structure groups = (Structure) actionStructure.getFieldValue("groups");
+        List<Structure> actionGroups = groups.evaluateListBase();// bActionGroup
+        BlenderAction blenderAction = new BlenderAction(blenderContext.getBlenderKey().getFps());
+        int lastFrame = 1;
+        for (Structure actionGroup : actionGroups) {
+            String name = actionGroup.getFieldValue("name").toString();
+            List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase();
+            BezierCurve[] bezierCurves = new BezierCurve[channels.size()];
+            int channelCounter = 0;
+            for (Structure c : channels) {
+                int type = this.getCurveType(c, blenderContext);
+                Pointer pBezTriple = (Pointer) c.getFieldValue("bezt");
+                List<Structure> bezTriples = pBezTriple.fetchData();
+                bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);
+            }
+
+            Ipo ipo = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
+            lastFrame = Math.max(lastFrame, ipo.getLastFrame());
+            blenderAction.featuresTracks.put(name, ipo);
+        }
+        blenderAction.stopFrame = lastFrame;
+        return blenderAction;
+    }
+
+    /**
+     * This method retuns the bone tracks for animation for blender version 2.49
+     * (and probably several lower versions too).
+     * 
+     * @param actionStructure
+     *            the structure containing the tracks
+     * @param blenderContext
+     *            the blender context
+     * @return a list of tracks for the specified animation
+     * @throws BlenderFileException
+     *             an exception is thrown when there are problems with the blend
+     *             file
+     */
+    private BlenderAction getTracks249(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
+        LOGGER.log(Level.FINE, "Getting tracks!");
+        Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");
+        List<Structure> actionChannels = chanbase.evaluateListBase();// bActionChannel
+        BlenderAction blenderAction = new BlenderAction(blenderContext.getBlenderKey().getFps());
+        int lastFrame = 1;
+        for (Structure bActionChannel : actionChannels) {
+            String animatedFeatureName = bActionChannel.getFieldValue("name").toString();
+            Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");
+            if (!p.isNull()) {
+                Structure ipoStructure = p.fetchData().get(0);
+                Ipo ipo = this.fromIpoStructure(ipoStructure, blenderContext);
+                lastFrame = Math.max(lastFrame, ipo.getLastFrame());
+                blenderAction.featuresTracks.put(animatedFeatureName, ipo);
+            }
+        }
+        blenderAction.stopFrame = lastFrame;
+        return blenderAction;
+    }
+
+    /**
+     * This method returns the type of the ipo curve.
+     * 
+     * @param structure
+     *            the structure must contain the 'rna_path' field and
+     *            'array_index' field (the type is not important here)
+     * @param blenderContext
+     *            the blender context
+     * @return the type of the curve
+     */
+    public int getCurveType(Structure structure, BlenderContext blenderContext) {
+        // reading rna path first
+        BlenderInputStream bis = blenderContext.getInputStream();
+        int currentPosition = bis.getPosition();
+        Pointer pRnaPath = (Pointer) structure.getFieldValue("rna_path");
+        FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pRnaPath.getOldMemoryAddress());
+        bis.setPosition(dataFileBlock.getBlockPosition());
+        String rnaPath = bis.readString();
+        bis.setPosition(currentPosition);
+        int arrayIndex = ((Number) structure.getFieldValue("array_index")).intValue();
+
+        // determining the curve type
+        if (rnaPath.endsWith("location")) {
+            return Ipo.AC_LOC_X + arrayIndex;
+        }
+        if (rnaPath.endsWith("rotation_quaternion")) {
+            return Ipo.AC_QUAT_W + arrayIndex;
+        }
+        if (rnaPath.endsWith("scale")) {
+            return Ipo.AC_SIZE_X + arrayIndex;
+        }
+        if (rnaPath.endsWith("rotation") || rnaPath.endsWith("rotation_euler")) {
+            return Ipo.OB_ROT_X + arrayIndex;
+        }
+        LOGGER.warning("Unknown curve rna path: " + rnaPath);
+        return -1;
+    }
+
+    /**
+     * An abstract representation of animation. The data stored here is mainly a raw action data loaded from blender.
+     * It can later be transformed into bone or spatial animation and applied to the specified node.
+     * 
+     * @author Marcin Roguski (Kaelthas)
+     */
+    private static class BlenderAction {
+        /** Animation speed - frames per second. */
+        private int              fps;
+        /** The last frame of the animation (the last ipo curve node position is used as a last frame). */
+        private int              stopFrame;
+        /**
+         * Tracks of the features. In case of bone animation the keys are the names of the bones. In case of spatial animation - the node's name
+         * is used. A single ipo contains all tracks for location, rotation and scales.
+         */
+        private Map<String, Ipo> featuresTracks = new HashMap<String, Ipo>();
+
+        public BlenderAction(int fps) {
+            this.fps = fps;
+        }
+
+        /**
+         * Converts the action into JME spatial animation tracks.
+         * @param node
+         *            the node that will be animated
+         * @return the spatial tracks for the node
+         */
+        public SpatialTrack[] toTracks(Node node) {
+            List<SpatialTrack> tracks = new ArrayList<SpatialTrack>(featuresTracks.size());
+            for (Entry<String, Ipo> entry : featuresTracks.entrySet()) {
+                tracks.add((SpatialTrack) entry.getValue().calculateTrack(0, node.getLocalTranslation(), node.getLocalRotation(), node.getLocalScale(), 1, stopFrame, fps, true));
+            }
+            return tracks.toArray(new SpatialTrack[tracks.size()]);
+        }
+
+        /**
+         * Converts the action into JME bone animation tracks.
+         * @param skeleton
+         *            the skeleton that will be animated
+         * @return the bone tracks for the node
+         */
+        public BoneTrack[] toTracks(Skeleton skeleton) {
+            List<BoneTrack> tracks = new ArrayList<BoneTrack>(featuresTracks.size());
+            for (Entry<String, Ipo> entry : featuresTracks.entrySet()) {
+                Bone bone = skeleton.getBone(entry.getKey());
+                int boneIndex = skeleton.getBoneIndex(entry.getKey());
+                tracks.add((BoneTrack) entry.getValue().calculateTrack(boneIndex, bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale(), 1, stopFrame, fps, false));
+            }
+            return tracks.toArray(new BoneTrack[tracks.size()]);
+        }
+
+        /**
+         * @return the time of animations (in seconds)
+         */
+        public float getAnimationTime() {
+            return (stopFrame - 1) / (float) fps;
+        }
+    }
+
+    /**
+     * Ipo constant curve. This is a curve with only one value and no specified
+     * type. This type of ipo cannot be used to calculate tracks. It should only
+     * be used to calculate single value for a given frame.
+     * 
+     * @author Marcin Roguski (Kaelthas)
+     */
+    private class ConstIpo extends Ipo {
+
+        /** The constant value of this ipo. */
+        private float constValue;
+
+        /**
+         * Constructor. Stores the constant value of this ipo.
+         * 
+         * @param constValue
+         *            the constant value of this ipo
+         */
+        public ConstIpo(float constValue) {
+            super(null, false, 0);// the version is not important here
+            this.constValue = constValue;
+        }
+
+        @Override
+        public float calculateValue(int frame) {
+            return constValue;
+        }
+
+        @Override
+        public float calculateValue(int frame, int curveIndex) {
+            return constValue;
+        }
+
+        @Override
+        public BoneTrack calculateTrack(int boneIndex, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean boneTrack) {
+            throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");
+        }
+    }
+}

+ 0 - 276
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java

@@ -1,276 +0,0 @@
-/*
- * Copyright (c) 2009-2012 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.scene.plugins.blender.animations;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.jme3.animation.Bone;
-import com.jme3.animation.BoneTrack;
-import com.jme3.animation.Skeleton;
-import com.jme3.math.Quaternion;
-import com.jme3.math.Vector3f;
-import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
-import com.jme3.scene.plugins.blender.BlenderContext;
-import com.jme3.scene.plugins.blender.curves.BezierCurve;
-import com.jme3.scene.plugins.blender.file.BlenderFileException;
-import com.jme3.scene.plugins.blender.file.Pointer;
-import com.jme3.scene.plugins.blender.file.Structure;
-
-/**
- * This class defines the methods to calculate certain aspects of animation and
- * armature functionalities.
- * 
- * @author Marcin Roguski (Kaelthas)
- */
-public class ArmatureHelper extends AbstractBlenderHelper {
-    private static final Logger LOGGER               = Logger.getLogger(ArmatureHelper.class.getName());
-
-    public static final String  ARMATURE_NODE_MARKER = "armature-node";
-
-    /**
-     * This 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
-     * @param blenderContext
-     *            the blender context
-     */
-    public ArmatureHelper(String blenderVersion, BlenderContext blenderContext) {
-        super(blenderVersion, blenderContext);
-    }
-
-    /**
-     * This method builds the object's bones structure.
-     * 
-     * @param armatureObjectOMA
-     *            the OMa of the armature node
-     * @param boneStructure
-     *            the structure containing the bones' data
-     * @param parent
-     *            the parent bone
-     * @param result
-     *            the list where the newly created bone will be added
-     * @param spatialOMA
-     *            the OMA of the spatial that will own the skeleton
-     * @param blenderContext
-     *            the blender context
-     * @throws BlenderFileException
-     *             an exception is thrown when there is problem with the blender
-     *             file
-     */
-    public void buildBones(Long armatureObjectOMA, Structure boneStructure, Bone parent, List<Bone> result, Long spatialOMA, BlenderContext blenderContext) throws BlenderFileException {
-        BoneContext bc = new BoneContext(armatureObjectOMA, boneStructure, blenderContext);
-        bc.buildBone(result, spatialOMA, blenderContext);
-    }
-
-    /**
-     * This method returns a map where the key is the object's group index that
-     * is used by a bone and the key is the bone index in the armature.
-     * 
-     * @param defBaseStructure
-     *            a bPose structure of the object
-     * @return bone group-to-index map
-     * @throws BlenderFileException
-     *             this exception is thrown when the blender file is somehow
-     *             corrupted
-     */
-    public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton) throws BlenderFileException {
-        Map<Integer, Integer> result = null;
-        if (skeleton.getBoneCount() != 0) {
-            result = new HashMap<Integer, Integer>();
-            List<Structure> deformGroups = defBaseStructure.evaluateListBase();// bDeformGroup
-            int groupIndex = 0;
-            for (Structure deformGroup : deformGroups) {
-                String deformGroupName = deformGroup.getFieldValue("name").toString();
-                int boneIndex = skeleton.getBoneIndex(deformGroupName);
-                if (boneIndex >= 0) {
-                    result.put(groupIndex, boneIndex);
-                }
-                ++groupIndex;
-            }
-        }
-        return result;
-    }
-
-    /**
-     * This method retuns the bone tracks for animation.
-     * 
-     * @param actionStructure
-     *            the structure containing the tracks
-     * @param blenderContext
-     *            the blender context
-     * @return a list of tracks for the specified animation
-     * @throws BlenderFileException
-     *             an exception is thrown when there are problems with the blend
-     *             file
-     */
-    public BoneTrack[] getTracks(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
-        if (blenderVersion < 250) {
-            return this.getTracks249(actionStructure, skeleton, blenderContext);
-        } else {
-            return this.getTracks250(actionStructure, skeleton, blenderContext);
-        }
-    }
-
-    /**
-     * This method retuns the bone tracks for animation for blender version 2.50
-     * and higher.
-     * 
-     * @param actionStructure
-     *            the structure containing the tracks
-     * @param blenderContext
-     *            the blender context
-     * @return a list of tracks for the specified animation
-     * @throws BlenderFileException
-     *             an exception is thrown when there are problems with the blend
-     *             file
-     */
-    private BoneTrack[] getTracks250(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
-        LOGGER.log(Level.FINE, "Getting tracks!");
-        IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
-        int fps = blenderContext.getBlenderKey().getFps();
-        Structure groups = (Structure) actionStructure.getFieldValue("groups");
-        List<Structure> actionGroups = groups.evaluateListBase();// bActionGroup
-        List<BoneTrack> tracks = new ArrayList<BoneTrack>();
-        for (Structure actionGroup : actionGroups) {
-            String name = actionGroup.getFieldValue("name").toString();
-            int boneIndex = skeleton.getBoneIndex(name);
-            if (boneIndex >= 0) {
-                List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase();
-                BezierCurve[] bezierCurves = new BezierCurve[channels.size()];
-                int channelCounter = 0;
-                for (Structure c : channels) {
-                    int type = ipoHelper.getCurveType(c, blenderContext);
-                    Pointer pBezTriple = (Pointer) c.getFieldValue("bezt");
-                    List<Structure> bezTriples = pBezTriple.fetchData();
-                    bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);
-                }
-
-                Bone bone = skeleton.getBone(boneIndex);
-                Ipo ipo = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
-                tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale(), 0, ipo.getLastFrame(), fps, false));
-            }
-        }
-        this.equaliseBoneTracks(tracks);
-        return tracks.toArray(new BoneTrack[tracks.size()]);
-    }
-
-    /**
-     * This method retuns the bone tracks for animation for blender version 2.49
-     * (and probably several lower versions too).
-     * 
-     * @param actionStructure
-     *            the structure containing the tracks
-     * @param blenderContext
-     *            the blender context
-     * @return a list of tracks for the specified animation
-     * @throws BlenderFileException
-     *             an exception is thrown when there are problems with the blend
-     *             file
-     */
-    private BoneTrack[] getTracks249(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
-        LOGGER.log(Level.FINE, "Getting tracks!");
-        IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
-        int fps = blenderContext.getBlenderKey().getFps();
-        Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");
-        List<Structure> actionChannels = chanbase.evaluateListBase();// bActionChannel
-        List<BoneTrack> tracks = new ArrayList<BoneTrack>();
-        for (Structure bActionChannel : actionChannels) {
-            String name = bActionChannel.getFieldValue("name").toString();
-            int boneIndex = skeleton.getBoneIndex(name);
-            if (boneIndex >= 0) {
-                Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");
-                if (!p.isNull()) {
-                    Structure ipoStructure = p.fetchData().get(0);
-
-                    Bone bone = skeleton.getBone(boneIndex);
-                    Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
-                    if (ipo != null) {
-                        tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale(), 0, ipo.getLastFrame(), fps, false));
-                    }
-                }
-            }
-        }
-        this.equaliseBoneTracks(tracks);
-        return tracks.toArray(new BoneTrack[tracks.size()]);
-    }
-
-    /**
-     * The method makes all the tracks to have equal frame lengths.
-     * @param tracks
-     *            the tracks to be equalized
-     */
-    private void equaliseBoneTracks(List<BoneTrack> tracks) {
-        // first compute the maximum amount of frames
-        int maximumFrameCount = -1;
-        float[] maximumTrackTimes = null;
-        for (BoneTrack track : tracks) {
-            if (track.getTimes().length > maximumFrameCount) {
-                maximumTrackTimes = track.getTimes();
-                maximumFrameCount = maximumTrackTimes.length;
-            }
-        }
-
-        // now widen all the tracks that have less frames by repeating the last values in the frame
-        for (BoneTrack track : tracks) {
-            int currentTrackLength = track.getTimes().length;
-            if (currentTrackLength < maximumFrameCount) {
-                Vector3f[] translations = new Vector3f[maximumFrameCount];
-                Quaternion[] rotations = new Quaternion[maximumFrameCount];
-                Vector3f[] scales = new Vector3f[maximumFrameCount];
-
-                Vector3f[] currentTranslations = track.getTranslations();
-                Quaternion[] currentRotations = track.getRotations();
-                Vector3f[] currentScales = track.getScales();
-                for (int i = 0; i < currentTrackLength; ++i) {
-                    translations[i] = currentTranslations[i];
-                    rotations[i] = currentRotations[i];
-                    scales[i] = currentScales[i];
-                }
-
-                for (int i = currentTrackLength; i < maximumFrameCount; ++i) {
-                    translations[i] = currentTranslations[currentTranslations.length - 1];
-                    rotations[i] = currentRotations[currentRotations.length - 1];
-                    scales[i] = currentScales[currentScales.length - 1];
-                }
-
-                track.setKeyframes(maximumTrackTimes, translations, rotations, scales);
-            }
-        }
-    }
-}

+ 4 - 2
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java

@@ -99,8 +99,10 @@ public class BoneContext {
 
         // first get the bone matrix in its armature space
         globalBoneMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", blenderContext.getBlenderKey().isFixUpAxis());
-        // then make sure it is rotated in a proper way to fit the jme bone transformation conventions
-        globalBoneMatrix.multLocal(BONE_ARMATURE_TRANSFORMATION_MATRIX);
+        if(blenderContext.getBlenderKey().isFixUpAxis()) {
+            // then make sure it is rotated in a proper way to fit the jme bone transformation conventions
+            globalBoneMatrix.multLocal(BONE_ARMATURE_TRANSFORMATION_MATRIX);
+        }
 
         Spatial armature = (Spatial) objectHelper.toObject(blenderContext.getFileBlock(armatureObjectOMA).getStructure(blenderContext), blenderContext);
         ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);

+ 8 - 8
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java

@@ -156,7 +156,8 @@ public class Ipo {
                 degreeToRadiansFactor *= FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here
             }
             int yIndex = 1, zIndex = 2;
-            if (spatialTrack && fixUpAxis) {
+            boolean swapAxes = spatialTrack && fixUpAxis;
+            if (swapAxes) {
                 yIndex = 2;
                 zIndex = 1;
             }
@@ -164,8 +165,7 @@ public class Ipo {
             // calculating track data
             for (int frame = startFrame; frame <= stopFrame; ++frame) {
                 int index = frame - startFrame;
-                times[index] = index * timeBetweenFrames;// start + (frame - 1)
-                                                         // * timeBetweenFrames;
+                times[index] = index * timeBetweenFrames;// start + (frame - 1) * timeBetweenFrames;
                 for (int j = 0; j < bezierCurves.length; ++j) {
                     double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
                     switch (bezierCurves[j].getType()) {
@@ -174,7 +174,7 @@ public class Ipo {
                             translation[0] = (float) value;
                             break;
                         case AC_LOC_Y:
-                            if (fixUpAxis && value != 0) {
+                            if (swapAxes && value != 0) {
                                 value = -value;
                             }
                             translation[yIndex] = (float) value;
@@ -188,7 +188,7 @@ public class Ipo {
                             objectRotation[0] = (float) value * degreeToRadiansFactor;
                             break;
                         case OB_ROT_Y:
-                            if (fixUpAxis && value != 0) {
+                            if (swapAxes && value != 0) {
                                 value = -value;
                             }
                             objectRotation[yIndex] = (float) value * degreeToRadiansFactor;
@@ -202,10 +202,10 @@ public class Ipo {
                             scale[0] = (float) value;
                             break;
                         case AC_SIZE_Y:
-                            scale[fixUpAxis ? 2 : 1] = (float) value;
+                            scale[yIndex] = (float) value;
                             break;
                         case AC_SIZE_Z:
-                            scale[fixUpAxis ? 1 : 2] = (float) value;
+                            scale[zIndex] = (float) value;
                             break;
 
                         // QUATERNION ROTATION (used with bone animation)
@@ -216,7 +216,7 @@ public class Ipo {
                             quaternionRotation[0] = (float) value;
                             break;
                         case AC_QUAT_Y:
-                            if (fixUpAxis && value != 0) {
+                            if (swapAxes && value != 0) {
                                 value = -value;
                             }
                             quaternionRotation[yIndex] = (float) value;

+ 0 - 194
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/IpoHelper.java

@@ -1,194 +0,0 @@
-package com.jme3.scene.plugins.blender.animations;
-
-import java.util.List;
-import java.util.logging.Logger;
-
-import com.jme3.animation.BoneTrack;
-import com.jme3.math.Quaternion;
-import com.jme3.math.Vector3f;
-import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
-import com.jme3.scene.plugins.blender.BlenderContext;
-import com.jme3.scene.plugins.blender.curves.BezierCurve;
-import com.jme3.scene.plugins.blender.file.BlenderFileException;
-import com.jme3.scene.plugins.blender.file.BlenderInputStream;
-import com.jme3.scene.plugins.blender.file.FileBlockHeader;
-import com.jme3.scene.plugins.blender.file.Pointer;
-import com.jme3.scene.plugins.blender.file.Structure;
-
-/**
- * This class helps to compute values from interpolation curves for features
- * like animation or constraint influence. The curves are 3rd degree bezier
- * curves.
- * 
- * @author Marcin Roguski
- */
-public class IpoHelper extends AbstractBlenderHelper {
-    private static final Logger LOGGER = Logger.getLogger(IpoHelper.class.getName());
-
-    /**
-     * This 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
-     * @param blenderContext
-     *            the blender context
-     */
-    public IpoHelper(String blenderVersion, BlenderContext blenderContext) {
-        super(blenderVersion, blenderContext);
-    }
-
-    /**
-     * This method creates an ipo object used for interpolation calculations.
-     * 
-     * @param ipoStructure
-     *            the structure with ipo definition
-     * @param blenderContext
-     *            the blender context
-     * @return the ipo object
-     * @throws BlenderFileException
-     *             this exception is thrown when the blender file is somehow
-     *             corrupted
-     */
-    public Ipo fromIpoStructure(Structure ipoStructure, BlenderContext blenderContext) throws BlenderFileException {
-        Structure curvebase = (Structure) ipoStructure.getFieldValue("curve");
-
-        // preparing bezier curves
-        Ipo result = null;
-        List<Structure> curves = curvebase.evaluateListBase();// IpoCurve
-        if (curves.size() > 0) {
-            BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
-            int frame = 0;
-            for (Structure curve : curves) {
-                Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
-                List<Structure> bezTriples = pBezTriple.fetchData();
-                int type = ((Number) curve.getFieldValue("adrcode")).intValue();
-                bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
-            }
-            curves.clear();
-            result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
-            blenderContext.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);
-        }
-        return result;
-    }
-
-    /**
-     * This method creates an ipo object used for interpolation calculations. It
-     * should be called for blender version 2.50 and higher.
-     * 
-     * @param actionStructure
-     *            the structure with action definition
-     * @param blenderContext
-     *            the blender context
-     * @return the ipo object
-     * @throws BlenderFileException
-     *             this exception is thrown when the blender file is somehow
-     *             corrupted
-     */
-    public Ipo fromAction(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
-        Ipo result = null;
-        List<Structure> curves = ((Structure) actionStructure.getFieldValue("curves")).evaluateListBase();// FCurve
-        if (curves.size() > 0) {
-            BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
-            int frame = 0;
-            for (Structure curve : curves) {
-                Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
-                List<Structure> bezTriples = pBezTriple.fetchData();
-                int type = this.getCurveType(curve, blenderContext);
-                bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
-            }
-            curves.clear();
-            result = new Ipo(bezierCurves, fixUpAxis, blenderContext.getBlenderVersion());
-        }
-        return result;
-    }
-
-    /**
-     * This method returns the type of the ipo curve.
-     * 
-     * @param structure
-     *            the structure must contain the 'rna_path' field and
-     *            'array_index' field (the type is not important here)
-     * @param blenderContext
-     *            the blender context
-     * @return the type of the curve
-     */
-    public int getCurveType(Structure structure, BlenderContext blenderContext) {
-        // reading rna path first
-        BlenderInputStream bis = blenderContext.getInputStream();
-        int currentPosition = bis.getPosition();
-        Pointer pRnaPath = (Pointer) structure.getFieldValue("rna_path");
-        FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pRnaPath.getOldMemoryAddress());
-        bis.setPosition(dataFileBlock.getBlockPosition());
-        String rnaPath = bis.readString();
-        bis.setPosition(currentPosition);
-        int arrayIndex = ((Number) structure.getFieldValue("array_index")).intValue();
-
-        // determining the curve type
-        if (rnaPath.endsWith("location")) {
-            return Ipo.AC_LOC_X + arrayIndex;
-        }
-        if (rnaPath.endsWith("rotation_quaternion")) {
-            return Ipo.AC_QUAT_W + arrayIndex;
-        }
-        if (rnaPath.endsWith("scale")) {
-            return Ipo.AC_SIZE_X + arrayIndex;
-        }
-        if (rnaPath.endsWith("rotation") || rnaPath.endsWith("rotation_euler")) {
-            return Ipo.OB_ROT_X + arrayIndex;
-        }
-        LOGGER.warning("Unknown curve rna path: " + rnaPath);
-        return -1;
-    }
-
-    /**
-     * This method creates an ipo with only a single value. No track type is
-     * specified so do not use it for calculating tracks.
-     * 
-     * @param constValue
-     *            the value of this ipo
-     * @return constant ipo
-     */
-    public Ipo fromValue(float constValue) {
-        return new ConstIpo(constValue);
-    }
-
-    /**
-     * Ipo constant curve. This is a curve with only one value and no specified
-     * type. This type of ipo cannot be used to calculate tracks. It should only
-     * be used to calculate single value for a given frame.
-     * 
-     * @author Marcin Roguski (Kaelthas)
-     */
-    private class ConstIpo extends Ipo {
-
-        /** The constant value of this ipo. */
-        private float constValue;
-
-        /**
-         * Constructor. Stores the constant value of this ipo.
-         * 
-         * @param constValue
-         *            the constant value of this ipo
-         */
-        public ConstIpo(float constValue) {
-            super(null, false, 0);// the version is not important here
-            this.constValue = constValue;
-        }
-
-        @Override
-        public float calculateValue(int frame) {
-            return constValue;
-        }
-
-        @Override
-        public float calculateValue(int frame, int curveIndex) {
-            return constValue;
-        }
-
-        @Override
-        public BoneTrack calculateTrack(int boneIndex, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean boneTrack) {
-            throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");
-        }
-    }
-}

+ 3 - 3
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java

@@ -6,11 +6,11 @@ import java.util.logging.Logger;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
-import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
 import com.jme3.scene.plugins.blender.animations.BoneContext;
 import com.jme3.scene.plugins.blender.animations.Ipo;
 import com.jme3.scene.plugins.blender.file.BlenderFileException;
 import com.jme3.scene.plugins.blender.file.Structure;
+import com.jme3.scene.plugins.blender.objects.ObjectHelper;
 
 /**
  * Constraint applied on the bone.
@@ -48,7 +48,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
             }
             // the second part of the if expression verifies if the found node
             // (if any) is an armature node
-            if (blenderContext.getMarkerValue(ArmatureHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) {
+            if (blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) {
                 if (subtargetName.trim().isEmpty()) {
                     LOGGER.log(Level.WARNING, "No bone target specified for constraint: {0}.", name);
                     return false;
@@ -64,7 +64,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
         }
         return true;
     }
-    
+
     @Override
     public void apply(int frame) {
         super.apply(frame);

+ 7 - 8
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java

@@ -17,10 +17,9 @@ import com.jme3.scene.Spatial;
 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
-import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
+import com.jme3.scene.plugins.blender.animations.AnimationHelper;
 import com.jme3.scene.plugins.blender.animations.BoneContext;
 import com.jme3.scene.plugins.blender.animations.Ipo;
-import com.jme3.scene.plugins.blender.animations.IpoHelper;
 import com.jme3.scene.plugins.blender.file.BlenderFileException;
 import com.jme3.scene.plugins.blender.file.Pointer;
 import com.jme3.scene.plugins.blender.file.Structure;
@@ -63,7 +62,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
     public void loadConstraints(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
         LOGGER.fine("Loading constraints.");
         // reading influence ipos for the constraints
-        IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
+        AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
         Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();
         Pointer pActions = (Pointer) objectStructure.getFieldValue("action");
         if (pActions.isNotNull()) {
@@ -79,7 +78,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
                         Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");
                         if (pIpo.isNotNull()) {
                             String constraintName = constraintChannel.getFieldValue("name").toString();
-                            Ipo ipo = ipoHelper.fromIpoStructure(pIpo.fetchData().get(0), blenderContext);
+                            Ipo ipo = animationHelper.fromIpoStructure(pIpo.fetchData().get(0), blenderContext);
                             ipos.put(constraintName, ipo);
                         }
                     }
@@ -107,7 +106,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
                     Ipo ipo = ipoMap == null ? null : ipoMap.get(constraintName);
                     if (ipo == null) {
                         float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
-                        ipo = ipoHelper.fromValue(enforce);
+                        ipo = animationHelper.fromValue(enforce);
                     }
                     constraintsList.add(new BoneConstraint(constraint, boneOMA, ipo, blenderContext));
                 }
@@ -130,7 +129,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
                 Ipo ipo = objectConstraintsIpos != null ? objectConstraintsIpos.get(constraintName) : null;
                 if (ipo == null) {
                     float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
-                    ipo = ipoHelper.fromValue(enforce);
+                    ipo = animationHelper.fromValue(enforce);
                 }
 
                 constraintsList.add(this.createConstraint(dataType, constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext));
@@ -219,7 +218,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
      */
     public Transform getTransform(Long oma, String subtargetName, Space space) {
         Spatial feature = (Spatial) blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_FEATURE);
-        boolean isArmature = blenderContext.getMarkerValue(ArmatureHelper.ARMATURE_NODE_MARKER, feature) != null;
+        boolean isArmature = blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, feature) != null;
         if (isArmature) {
             blenderContext.getSkeleton(oma).updateWorldVectors();
             BoneContext targetBoneContext = blenderContext.getBoneByName(oma, subtargetName);
@@ -301,7 +300,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
      */
     public void applyTransform(Long oma, String subtargetName, Space space, Transform transform) {
         Spatial feature = (Spatial) blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_FEATURE);
-        boolean isArmature = blenderContext.getMarkerValue(ArmatureHelper.ARMATURE_NODE_MARKER, feature) != null;
+        boolean isArmature = blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, feature) != null;
         if (isArmature) {
             Skeleton skeleton = blenderContext.getSkeleton(oma);
             BoneContext targetBoneContext = blenderContext.getBoneByName(oma, subtargetName);

+ 6 - 8
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java

@@ -25,7 +25,6 @@ import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
-import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
 import com.jme3.scene.plugins.blender.animations.BoneContext;
 import com.jme3.scene.plugins.blender.objects.ObjectHelper;
 import com.jme3.util.TempVars;
@@ -95,7 +94,7 @@ public class SimulationNode {
     private SimulationNode(Long featureOMA, BlenderContext blenderContext, boolean rootNode) {
         this.blenderContext = blenderContext;
         Node spatial = (Node) blenderContext.getLoadedFeature(featureOMA, LoadedFeatureDataType.LOADED_FEATURE);
-        if (blenderContext.getMarkerValue(ArmatureHelper.ARMATURE_NODE_MARKER, spatial) != null) {
+        if (blenderContext.getMarkerValue(ObjectHelper.ARMATURE_NODE_MARKER, spatial) != null) {
             skeleton = blenderContext.getSkeleton(featureOMA);
 
             Node nodeWithAnimationControl = blenderContext.getControlledNode(skeleton);
@@ -136,9 +135,9 @@ public class SimulationNode {
             // each bone of the skeleton has the same anim data applied
             BoneContext boneContext = blenderContext.getBoneContext(skeleton.getBone(1));
             Long boneOma = boneContext.getBoneOma();
-            animations = blenderContext.getAnimData(boneOma) == null ? null : blenderContext.getAnimData(boneOma).anims;
+            animations = blenderContext.getAnimations(boneOma);
         } else {
-            animations = blenderContext.getAnimData(featureOMA) == null ? null : blenderContext.getAnimData(featureOMA).anims;
+            animations = blenderContext.getAnimations(featureOMA);
             for (Spatial child : spatial.getChildren()) {
                 if (child instanceof Node) {
                     children.add(new SimulationNode((Long) blenderContext.getMarkerValue(ObjectHelper.OMA_MARKER, child), blenderContext, false));
@@ -273,12 +272,11 @@ public class SimulationNode {
                             track.setTime(time, 1, animControl, animChannel, vars);
                             skeleton.updateWorldVectors();
                         }
-                        
 
                         // ... 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
+                            if (skeleton.getBoneIndex(rootBone) > 0) {
+                                // ommit the 0 - indexed root bone as it is the bone added by importer
                                 this.applyConstraints(rootBone, alteredOmas, frame);
                             }
                         }
@@ -419,7 +417,7 @@ public class SimulationNode {
     private List<Constraint> findConstraints(Long ownerOMA, BlenderContext blenderContext) {
         List<Constraint> result = new ArrayList<Constraint>();
         List<Constraint> constraints = blenderContext.getConstraints(ownerOMA);
-        if(constraints != null) {
+        if (constraints != null) {
             for (Constraint constraint : constraints) {
                 if (constraint.isImplemented() && constraint.validate()) {
                     result.add(constraint);

+ 107 - 143
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java

@@ -11,14 +11,8 @@ import java.util.TreeMap;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import com.jme3.animation.AnimControl;
-import com.jme3.animation.Animation;
 import com.jme3.animation.Bone;
-import com.jme3.animation.BoneTrack;
 import com.jme3.animation.Skeleton;
-import com.jme3.animation.SkeletonControl;
-import com.jme3.math.Matrix4f;
-import com.jme3.math.Transform;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.Node;
@@ -28,19 +22,13 @@ import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.VertexBuffer.Usage;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
-import com.jme3.scene.plugins.blender.animations.AnimationData;
-import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
+import com.jme3.scene.plugins.blender.animations.AnimationHelper;
 import com.jme3.scene.plugins.blender.animations.BoneContext;
-import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
-import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
 import com.jme3.scene.plugins.blender.file.BlenderFileException;
-import com.jme3.scene.plugins.blender.file.FileBlockHeader;
 import com.jme3.scene.plugins.blender.file.Pointer;
 import com.jme3.scene.plugins.blender.file.Structure;
 import com.jme3.scene.plugins.blender.meshes.MeshContext;
-import com.jme3.scene.plugins.blender.objects.ObjectHelper;
 import com.jme3.util.BufferUtils;
-import com.jme3.util.TempVars;
 
 /**
  * This modifier allows to add bone animation to the object.
@@ -56,8 +44,6 @@ import com.jme3.util.TempVars;
     private Structure           objectStructure;
     private Structure           meshStructure;
 
-    /** Loaded animation data. */
-    private AnimationData       animationData;
     /** Old memory address of the mesh that will have the skeleton applied. */
     private Long                meshOMA;
 
@@ -80,8 +66,6 @@ import com.jme3.util.TempVars;
         if (this.validate(modifierStructure, blenderContext)) {
             Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object");
             if (pArmatureObject.isNotNull()) {
-                ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
-
                 armatureObject = pArmatureObject.fetchData().get(0);
 
                 // load skeleton
@@ -89,7 +73,7 @@ import com.jme3.util.TempVars;
                 List<Structure> bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase();
                 List<Bone> bonesList = new ArrayList<Bone>();
                 for (int i = 0; i < bonebase.size(); ++i) {
-                    armatureHelper.buildBones(armatureObject.getOldMemoryAddress(), bonebase.get(i), null, bonesList, objectStructure.getOldMemoryAddress(), blenderContext);
+                    this.buildBones(armatureObject.getOldMemoryAddress(), bonebase.get(i), null, bonesList, objectStructure.getOldMemoryAddress(), blenderContext);
                 }
                 bonesList.add(0, new Bone(""));
                 Bone[] bones = bonesList.toArray(new Bone[bonesList.size()]);
@@ -100,72 +84,63 @@ import com.jme3.util.TempVars;
 
                 // read mesh indexes
                 meshOMA = meshStructure.getOldMemoryAddress();
+            } else {
+                modifying = false;
+            }
+        }
+    }
 
-                // read animations
-                ArrayList<Animation> animations = new ArrayList<Animation>();
-                List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
-                if (actionHeaders != null) {// it may happen that the model has
-                                            // armature with no actions
-                    for (FileBlockHeader header : actionHeaders) {
-                        Structure actionStructure = header.getStructure(blenderContext);
-                        String actionName = actionStructure.getName();
-
-                        BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, skeleton, blenderContext);
-                        if (tracks != null && tracks.length > 0) {
-                            // determining the animation time
-                            float maximumTrackLength = 0;
-                            for (BoneTrack track : tracks) {
-                                float length = track.getLength();
-                                if (length > maximumTrackLength) {
-                                    maximumTrackLength = length;
-                                }
-                            }
-
-                            Animation boneAnimation = new Animation(actionName, maximumTrackLength);
-                            boneAnimation.setTracks(tracks);
-                            animations.add(boneAnimation);
-                        }
-                    }
-                }
-                // fetching action defined in object
-                Pointer pAction = (Pointer) objectStructure.getFieldValue("action");
-                if (pAction.isNotNull()) {
-                    Structure actionStructure = pAction.fetchData().get(0);
-                    String actionName = actionStructure.getName();
-
-                    BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, skeleton, blenderContext);
-                    if (tracks != null && tracks.length > 0) {
-                        // determining the animation time
-                        float maximumTrackLength = 0;
-                        for (BoneTrack track : tracks) {
-                            float length = track.getLength();
-                            if (length > maximumTrackLength) {
-                                maximumTrackLength = length;
-                            }
-                        }
-
-                        Animation boneAnimation = new Animation(actionName, maximumTrackLength);
-                        boneAnimation.setTracks(tracks);
-                        animations.add(boneAnimation);
-                    }
-                }
-
-                animationData = new AnimationData(skeleton, animations);
+    /**
+     * This method builds the object's bones structure.
+     * 
+     * @param armatureObjectOMA
+     *            the OMa of the armature node
+     * @param boneStructure
+     *            the structure containing the bones' data
+     * @param parent
+     *            the parent bone
+     * @param result
+     *            the list where the newly created bone will be added
+     * @param spatialOMA
+     *            the OMA of the spatial that will own the skeleton
+     * @param blenderContext
+     *            the blender context
+     * @throws BlenderFileException
+     *             an exception is thrown when there is problem with the blender
+     *             file
+     */
+    private void buildBones(Long armatureObjectOMA, Structure boneStructure, Bone parent, List<Bone> result, Long spatialOMA, BlenderContext blenderContext) throws BlenderFileException {
+        BoneContext bc = new BoneContext(armatureObjectOMA, boneStructure, blenderContext);
+        bc.buildBone(result, spatialOMA, blenderContext);
+    }
 
-                // store the animation data for each bone
-                for (Bone bone : bones) {
-                    if (bone.getName().length() > 0) {
-                        BoneContext boneContext = blenderContext.getBoneContext(bone);
-                        Long boneOma = boneContext.getBoneOma();
-                        if (boneOma != null) {
-                            blenderContext.setAnimData(boneOma, animationData);
-                        }
-                    }
+    /**
+     * This method returns a map where the key is the object's group index that
+     * is used by a bone and the key is the bone index in the armature.
+     * 
+     * @param defBaseStructure
+     *            a bPose structure of the object
+     * @return bone group-to-index map
+     * @throws BlenderFileException
+     *             this exception is thrown when the blender file is somehow
+     *             corrupted
+     */
+    public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton) throws BlenderFileException {
+        Map<Integer, Integer> result = null;
+        if (skeleton.getBoneCount() != 0) {
+            result = new HashMap<Integer, Integer>();
+            List<Structure> deformGroups = defBaseStructure.evaluateListBase();// bDeformGroup
+            int groupIndex = 0;
+            for (Structure deformGroup : deformGroups) {
+                String deformGroupName = deformGroup.getFieldValue("name").toString();
+                int boneIndex = skeleton.getBoneIndex(deformGroupName);
+                if (boneIndex >= 0) {
+                    result.put(groupIndex, boneIndex);
                 }
-            } else {
-                modifying = false;
+                ++groupIndex;
             }
         }
+        return result;
     }
 
     @Override
@@ -174,7 +149,7 @@ import com.jme3.util.TempVars;
         if (invalid) {
             LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName());
         }// if invalid, animData will be null
-        if (animationData != null && skeleton != null) {
+        if (skeleton != null) {
             // setting weights for bones
             List<Geometry> geomList = (List<Geometry>) blenderContext.getLoadedFeature(meshOMA, LoadedFeatureDataType.LOADED_FEATURE);
             MeshContext meshContext = blenderContext.getMeshContext(meshOMA);
@@ -209,65 +184,9 @@ import com.jme3.util.TempVars;
                     LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
                     invalid = true;
                 }
-            }
-
-            if (!invalid) {
-                // applying animations
-                AnimControl control = new AnimControl(animationData.skeleton);
-                List<Animation> animList = animationData.anims;
-                if (animList != null && animList.size() > 0) {
-                    HashMap<String, Animation> anims = new HashMap<String, Animation>(animList.size());
-                    for (int i = 0; i < animList.size(); ++i) {
-                        Animation animation = animList.get(i);
-                        anims.put(animation.getName(), animation);
-                    }
-                    control.setAnimations(anims);
-                }
-                node.addControl(control);
-                node.addControl(new SkeletonControl(animationData.skeleton));
-                blenderContext.setNodeForSkeleton(skeleton, node);
-
-                TempVars tempVars = TempVars.get();
-                try {
-                    Pointer pPose = (Pointer) armatureObject.getFieldValue("pose");
-                    if (pPose.isNotNull()) {
-                        LOGGER.fine("Loading the pose of the armature.");
-                        ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
-                        ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
-
-                        Structure pose = pPose.fetchData().get(0);
-                        Structure chanbase = (Structure) pose.getFieldValue("chanbase");
-                        List<Structure> chans = chanbase.evaluateListBase();
-                        Transform transform = new Transform();
-                        for (Structure poseChannel : chans) {
-                            Pointer pBone = (Pointer) poseChannel.getFieldValue("bone");
-                            if (pBone.isNull()) {
-                                throw new BlenderFileException("Cannot find bone for pose channel named: " + poseChannel.getName());
-                            }
-                            BoneContext boneContext = blenderContext.getBoneContext(pBone.getOldMemoryAddress());
-
-                            LOGGER.log(Level.FINEST, "Getting the global pose transformation for bone: {0}", boneContext);
-                            Matrix4f poseMat = objectHelper.getMatrix(poseChannel, "pose_mat", blenderContext.getBlenderKey().isFixUpAxis());
-                            poseMat.multLocal(BoneContext.BONE_ARMATURE_TRANSFORMATION_MATRIX);
 
-                            Matrix4f armatureWorldMat = objectHelper.getMatrix(armatureObject, "obmat", blenderContext.getBlenderKey().isFixUpAxis());
-                            Matrix4f boneWorldMat = armatureWorldMat.multLocal(poseMat);
-
-                            boneWorldMat.toTranslationVector(tempVars.vect1);
-                            boneWorldMat.toRotationQuat(tempVars.quat1);
-                            boneWorldMat.toScaleVector(tempVars.vect2);
-                            transform.setTranslation(tempVars.vect1);
-                            transform.setRotation(tempVars.quat1);
-                            transform.setScale(tempVars.vect2);
-
-                            constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD, transform);
-                        }
-                    }
-                } catch (BlenderFileException e) {
-                    LOGGER.log(Level.WARNING, "Problems occured during pose loading: {0}.", e.getLocalizedMessage());
-                } finally {
-                    tempVars.release();
-                }
+                AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
+                animationHelper.applyAnimations(node, skeleton, blenderContext.getBlenderKey().getSkeletonAnimationNames(node.getName()));
 
                 node.updateModelBound();
             }
@@ -288,9 +207,8 @@ import com.jme3.util.TempVars;
      *             somehow invalid or corrupted
      */
     private VertexBuffer[] readVerticesWeightsData(Structure objectStructure, Structure meshStructure, Skeleton skeleton, int materialIndex, int[] bonesGroups, BlenderContext blenderContext) throws BlenderFileException {
-        ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
         Structure defBase = (Structure) objectStructure.getFieldValue("defbase");
-        Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, skeleton);
+        Map<Integer, Integer> groupToBoneIndexMap = this.getGroupToBoneIndexMap(defBase, skeleton);
 
         MeshContext meshContext = blenderContext.getMeshContext(meshStructure.getOldMemoryAddress());
 
@@ -326,9 +244,7 @@ import com.jme3.util.TempVars;
      */
     private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups, Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap) throws BlenderFileException {
         bonesGroups[0] = 0;
-        Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert
-                                                                        // =
-                                                                        // DeformVERTices
+        Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
         FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
         ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
 
@@ -461,4 +377,52 @@ import com.jme3.util.TempVars;
         }
         weightsFloatData.rewind();
     }
+    
+//    This method is now not used because it broke animations.
+//    Perhaps in the future I will find a solution to this problem.
+//    I store it here for future use.
+//    
+//    private void loadBonePoses() {
+//        TempVars tempVars = TempVars.get();
+//        try {
+//            Pointer pPose = (Pointer) armatureObject.getFieldValue("pose");
+//            if (pPose.isNotNull()) {
+//                LOGGER.fine("Loading the pose of the armature.");
+//                ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
+//                ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+//
+//                Structure pose = pPose.fetchData().get(0);
+//                Structure chanbase = (Structure) pose.getFieldValue("chanbase");
+//                List<Structure> chans = chanbase.evaluateListBase();
+//                Transform transform = new Transform();
+//                for (Structure poseChannel : chans) {
+//                    Pointer pBone = (Pointer) poseChannel.getFieldValue("bone");
+//                    if (pBone.isNull()) {
+//                        throw new BlenderFileException("Cannot find bone for pose channel named: " + poseChannel.getName());
+//                    }
+//                    BoneContext boneContext = blenderContext.getBoneContext(pBone.getOldMemoryAddress());
+//
+//                    LOGGER.log(Level.FINEST, "Getting the global pose transformation for bone: {0}", boneContext);
+//                    Matrix4f poseMat = objectHelper.getMatrix(poseChannel, "pose_mat", blenderContext.getBlenderKey().isFixUpAxis());
+//                    poseMat.multLocal(BoneContext.BONE_ARMATURE_TRANSFORMATION_MATRIX);
+//
+//                    Matrix4f armatureWorldMat = objectHelper.getMatrix(armatureObject, "obmat", blenderContext.getBlenderKey().isFixUpAxis());
+//                    Matrix4f boneWorldMat = armatureWorldMat.multLocal(poseMat);
+//
+//                    boneWorldMat.toTranslationVector(tempVars.vect1);
+//                    boneWorldMat.toRotationQuat(tempVars.quat1);
+//                    boneWorldMat.toScaleVector(tempVars.vect2);
+//                    transform.setTranslation(tempVars.vect1);
+//                    transform.setRotation(tempVars.quat1);
+//                    transform.setScale(tempVars.vect2);
+//
+//                    constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD, transform);
+//                }
+//            }
+//        } catch (BlenderFileException e) {
+//            LOGGER.log(Level.WARNING, "Problems occured during pose loading: {0}.", e.getLocalizedMessage());
+//        } finally {
+//            tempVars.release();
+//        }
+//    }
 }

+ 5 - 78
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java

@@ -31,14 +31,6 @@
  */
 package com.jme3.scene.plugins.blender.modifiers;
 
-import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
-import com.jme3.scene.plugins.blender.BlenderContext;
-import com.jme3.scene.plugins.blender.animations.Ipo;
-import com.jme3.scene.plugins.blender.animations.IpoHelper;
-import com.jme3.scene.plugins.blender.file.BlenderFileException;
-import com.jme3.scene.plugins.blender.file.Pointer;
-import com.jme3.scene.plugins.blender.file.Structure;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
@@ -47,6 +39,11 @@ import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.file.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.Structure;
+
 /**
  * A class that is used in modifiers calculations.
  * 
@@ -113,76 +110,6 @@ public class ModifierHelper extends AbstractBlenderHelper {
                 }
             }
         }
-
-        // at the end read object's animation modifier (object animation is
-        // either described by action or by ipo of the object)
-        Modifier modifier;
-        if (blenderVersion <= 249) {
-            modifier = this.readAnimationModifier249(objectStructure, blenderContext);
-        } else {
-            modifier = this.readAnimationModifier250(objectStructure, blenderContext);
-        }
-        if (modifier != null) {
-            result.add(modifier);
-        }
-        return result;
-    }
-
-    /**
-     * This method reads the object's animation modifier for blender version
-     * 2.49 and lower.
-     * 
-     * @param objectStructure
-     *            the object's structure
-     * @param blenderContext
-     *            the blender context
-     * @return loaded modifier
-     * @throws BlenderFileException
-     *             this exception is thrown when the blender file is somehow
-     *             corrupted
-     */
-    private Modifier readAnimationModifier249(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
-        Modifier result = null;
-        Pointer pIpo = (Pointer) objectStructure.getFieldValue("ipo");
-        if (pIpo.isNotNull()) {
-            IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
-            Structure ipoStructure = pIpo.fetchData().get(0);
-            Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
-            if (ipo != null) {
-                result = new ObjectAnimationModifier(ipo, objectStructure.getName(), objectStructure.getOldMemoryAddress(), blenderContext);
-            }
-        }
-        return result;
-    }
-
-    /**
-     * This method reads the object's animation modifier for blender version
-     * 2.50 and higher.
-     * 
-     * @param objectStructure
-     *            the object's structure
-     * @param blenderContext
-     *            the blender context
-     * @return loaded modifier
-     * @throws BlenderFileException
-     *             this exception is thrown when the blender file is somehow
-     *             corrupted
-     */
-    private Modifier readAnimationModifier250(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
-        Modifier result = null;
-        Pointer pAnimData = (Pointer) objectStructure.getFieldValue("adt");
-        if (pAnimData.isNotNull()) {
-            Structure animData = pAnimData.fetchData().get(0);
-            Pointer pAction = (Pointer) animData.getFieldValue("action");
-            if (pAction.isNotNull()) {
-                Structure actionStructure = pAction.fetchData().get(0);
-                IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
-                Ipo ipo = ipoHelper.fromAction(actionStructure, blenderContext);
-                if (ipo != null) {// ipo can be null if it has no curves applied, ommit such modifier then
-                    result = new ObjectAnimationModifier(ipo, actionStructure.getName(), objectStructure.getOldMemoryAddress(), blenderContext);
-                }
-            }
-        }
         return result;
     }
 }

+ 0 - 89
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java

@@ -1,89 +0,0 @@
-package com.jme3.scene.plugins.blender.modifiers;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.jme3.animation.AnimControl;
-import com.jme3.animation.Animation;
-import com.jme3.animation.SpatialTrack;
-import com.jme3.scene.Node;
-import com.jme3.scene.Spatial;
-import com.jme3.scene.plugins.blender.BlenderContext;
-import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
-import com.jme3.scene.plugins.blender.animations.AnimationData;
-import com.jme3.scene.plugins.blender.animations.Ipo;
-import com.jme3.scene.plugins.blender.file.BlenderFileException;
-
-/**
- * This modifier allows to add animation to the object.
- * 
- * @author Marcin Roguski (Kaelthas)
- */
-/* package */class ObjectAnimationModifier extends Modifier {
-    private static final Logger LOGGER = Logger.getLogger(ObjectAnimationModifier.class.getName());
-
-    /** Loaded animation data. */
-    private AnimationData       animationData;
-
-    /**
-     * This constructor reads animation of the object itself (without bones) and
-     * stores it as an ArmatureModifierData modifier. The animation is returned
-     * as a modifier. It should be later applied regardless other modifiers. The
-     * reason for this is that object may not have modifiers added but it's
-     * animation should be working. The stored modifier is an anim data and
-     * additional data is given object's OMA.
-     * 
-     * @param ipo
-     *            the object's interpolation curves
-     * @param objectAnimationName
-     *            the name of object's animation
-     * @param objectOMA
-     *            the OMA of the object
-     * @param blenderContext
-     *            the blender context
-     * @throws BlenderFileException
-     *             this exception is thrown when the blender file is somehow
-     *             corrupted
-     */
-    public ObjectAnimationModifier(Ipo ipo, String objectAnimationName, Long objectOMA, BlenderContext blenderContext) throws BlenderFileException {
-        int fps = blenderContext.getBlenderKey().getFps();
-
-        Spatial object = (Spatial) blenderContext.getLoadedFeature(objectOMA, LoadedFeatureDataType.LOADED_FEATURE);
-        // calculating track
-        SpatialTrack track = (SpatialTrack) ipo.calculateTrack(-1, object.getLocalTranslation(), object.getLocalRotation(), object.getLocalScale(), 0, ipo.getLastFrame(), fps, true);
-
-        Animation animation = new Animation(objectAnimationName, ipo.getLastFrame() / (float) fps);
-        animation.setTracks(new SpatialTrack[] { track });
-        ArrayList<Animation> animations = new ArrayList<Animation>(1);
-        animations.add(animation);
-
-        animationData = new AnimationData(animations);
-        blenderContext.setAnimData(objectOMA, animationData);
-    }
-
-    @Override
-    public void apply(Node node, BlenderContext blenderContext) {
-        if (invalid) {
-            LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName());
-        }// if invalid, animData will be null
-        if (animationData != null) {
-            // INFO: constraints for this modifier are applied in the
-            // ObjectHelper when the whole object is loaded
-            List<Animation> animList = animationData.anims;
-            if (animList != null && animList.size() > 0) {
-                HashMap<String, Animation> anims = new HashMap<String, Animation>();
-                for (int i = 0; i < animList.size(); ++i) {
-                    Animation animation = animList.get(i);
-                    anims.put(animation.getName(), animation);
-                }
-
-                AnimControl control = new AnimControl(null);
-                control.setAnimations(anims);
-                node.addControl(control);
-            }
-        }
-    }
-}

+ 9 - 4
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java

@@ -54,7 +54,7 @@ import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
-import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
+import com.jme3.scene.plugins.blender.animations.AnimationHelper;
 import com.jme3.scene.plugins.blender.cameras.CameraHelper;
 import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
 import com.jme3.scene.plugins.blender.curves.CurvesHelper;
@@ -74,9 +74,10 @@ import com.jme3.util.TempVars;
  * @author Marcin Roguski (Kaelthas)
  */
 public class ObjectHelper extends AbstractBlenderHelper {
-    private static final Logger LOGGER     = Logger.getLogger(ObjectHelper.class.getName());
+    private static final Logger LOGGER               = Logger.getLogger(ObjectHelper.class.getName());
 
-    public static final String  OMA_MARKER = "oma";
+    public static final String  OMA_MARKER           = "oma";
+    public static final String  ARMATURE_NODE_MARKER = "armature-node";
 
     /**
      * This constructor parses the given blender version and stores the result.
@@ -236,9 +237,13 @@ public class ObjectHelper extends AbstractBlenderHelper {
             LOGGER.fine("Applying markers (those will be removed before the final result is released).");
             blenderContext.addMarker(OMA_MARKER, result, objectStructure.getOldMemoryAddress());
             if (objectType == ObjectType.ARMATURE) {
-                blenderContext.addMarker(ArmatureHelper.ARMATURE_NODE_MARKER, result, Boolean.TRUE);
+                blenderContext.addMarker(ARMATURE_NODE_MARKER, result, Boolean.TRUE);
             }
 
+            LOGGER.fine("Applying animations to the object if such are defined.");
+            AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
+            animationHelper.applyAnimations(result, blenderContext.getBlenderKey().getNodeAnimationNames(name));
+
             LOGGER.fine("Loading constraints connected with this object.");
             ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
             constraintHelper.loadConstraints(objectStructure, blenderContext);