diff --git a/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java b/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
index dd153e393..30bbd212b 100644
--- a/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
+++ b/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 org.lwjgl.opengl.GL11.GL_MAX_TEXTURE_SIZE.
*/
- 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> nodeAnimationMap = new HashMap>();
+ /** A map between node name and its skeleton animation names. */
+ protected Map> skeletonAnimationMap = new HashMap>();
/**
* 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 animations = nodeAnimationMap.get(nodeName);
+ if (animations == null) {
+ animations = new ArrayList();
+ 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 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 animations = skeletonAnimationMap.get(nodeName);
+ if (animations == null) {
+ animations = new ArrayList();
+ 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 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> 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> 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>(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(Arrays.asList(anims)));// must create ArrayList because 'asList' method returns unmodifiable list
+ }
+ }
+
+ animsSize = ic.readInt("skeleton-anims-map-size", 0);
+ skeletonAnimationMap = new HashMap>(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(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 scenes;
+ private List scenes;
/** Objects from all scenes. */
- private List objects;
+ private List objects;
/** Materials from all objects. */
- private List materials;
+ private List materials;
/** Textures from all objects. */
- private List textures;
+ private List textures;
/** Animations of all objects. */
- private List animations;
+ private List animations;
/** All cameras from the file. */
- private List cameras;
+ private List cameras;
/** All lights from the file. */
- private List lights;
+ private List 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();
+ animations = new ArrayList();
}
}
if ((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) {
@@ -839,7 +948,7 @@ public class BlenderKey extends ModelKey {
/**
* @return all loaded animations
*/
- public List getAnimations() {
+ public List getAnimations() {
return animations;
}
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java
index 290a578b9..17b312a37 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java
+++ b/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 parentStack = new Stack();
/** A list of constraints for the specified object. */
protected Map> constraints = new HashMap>();
- /** Anim data loaded for features. */
- private Map animData = new HashMap();
+ /** Animations loaded for features. */
+ private Map> animations = new HashMap>();
/** Loaded skeletons. */
private Map skeletons = new HashMap();
/** 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 animList = animations.get(ownerOMA);
+ if(animList == null) {
+ animList = new ArrayList();
+ 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 getAnimations(Long ownerOMA) {
+ return animations.get(ownerOMA);
}
/**
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java
index 4b398d166..fa5566560 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java
+++ b/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 sceneBlocks = new ArrayList();
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 {
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java
index 1a856df6e..021a6082f 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java
+++ b/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 rootObjects = new ArrayList();
for (FileBlockHeader block : blocks) {
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationData.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationData.java
deleted file mode 100644
index 05f405fc1..000000000
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationData.java
+++ /dev/null
@@ -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 anims;
-
- public AnimationData(List anims) {
- this.anims = anims;
- skeleton = null;
- }
-
- public AnimationData(Skeleton skeleton, List anims) {
- this.skeleton = skeleton;
- this.anims = anims;
- }
-}
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java
new file mode 100644
index 000000000..19c51b422
--- /dev/null
+++ b/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 actions = new HashMap();
+
+ 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 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 animationNames) {
+ if (animationNames != null && animationNames.size() > 0) {
+ List animations = new ArrayList();
+ 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 anims = new HashMap(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 animationNames) {
+ node.addControl(new SkeletonControl(skeleton));
+ blenderContext.setNodeForSkeleton(skeleton, node);
+
+ if (animationNames != null && animationNames.size() > 0) {
+ List animations = new ArrayList();
+ 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 anims = new HashMap(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 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 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 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 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 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 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 featuresTracks = new HashMap();
+
+ 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 tracks = new ArrayList(featuresTracks.size());
+ for (Entry 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 tracks = new ArrayList(featuresTracks.size());
+ for (Entry 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!");
+ }
+ }
+}
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java
deleted file mode 100644
index 2c87d9c0f..000000000
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java
+++ /dev/null
@@ -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 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 getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton) throws BlenderFileException {
- Map result = null;
- if (skeleton.getBoneCount() != 0) {
- result = new HashMap();
- List 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 actionGroups = groups.evaluateListBase();// bActionGroup
- List tracks = new ArrayList();
- for (Structure actionGroup : actionGroups) {
- String name = actionGroup.getFieldValue("name").toString();
- int boneIndex = skeleton.getBoneIndex(name);
- if (boneIndex >= 0) {
- List 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 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 actionChannels = chanbase.evaluateListBase();// bActionChannel
- List tracks = new ArrayList();
- 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 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);
- }
- }
- }
-}
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java
index 6b14d8097..6fd91f143 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java
+++ b/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);
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java
index a2b5e3708..8a0bf9176 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java
+++ b/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;
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/IpoHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/IpoHelper.java
deleted file mode 100644
index 260a59575..000000000
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/IpoHelper.java
+++ /dev/null
@@ -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 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 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 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 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!");
- }
- }
-}
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java
index 284cb7da4..232f30508 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java
+++ b/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);
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
index 97b6889ff..68f723183 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
+++ b/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> constraintsIpos = new HashMap>();
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);
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java
index f656150fa..088e9c889 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java
+++ b/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 findConstraints(Long ownerOMA, BlenderContext blenderContext) {
List result = new ArrayList();
List constraints = blenderContext.getConstraints(ownerOMA);
- if(constraints != null) {
+ if (constraints != null) {
for (Constraint constraint : constraints) {
if (constraint.isImplemented() && constraint.validate()) {
result.add(constraint);
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java
index 258a2c8aa..395097a96 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java
+++ b/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 bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase();
List bonesList = new ArrayList();
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 animations = new ArrayList();
- List 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 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 getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton) throws BlenderFileException {
+ Map result = null;
+ if (skeleton.getBoneCount() != 0) {
+ result = new HashMap();
+ List 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 geomList = (List) 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 animList = animationData.anims;
- if (animList != null && animList.size() > 0) {
- HashMap anims = new HashMap(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 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 groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, skeleton);
+ Map 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> vertexReferenceMap, Map 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 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();
+// }
+// }
}
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java
index a0ad5c8ef..6a28e433d 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java
+++ b/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;
}
}
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java
deleted file mode 100644
index caf1e206e..000000000
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java
+++ /dev/null
@@ -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 animations = new ArrayList(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 animList = animationData.anims;
- if (animList != null && animList.size() > 0) {
- HashMap anims = new HashMap();
- 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);
- }
- }
- }
-}
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java
index cf1835095..4bc111d8e 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java
+++ b/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);