Automatic loading of both bone and object animations (no need to specify animations in blender key any more).

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8305 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
Kae..pl 13 years ago
parent ce259299e2
commit 0b274b3c1b
  1. 127
      engine/src/blender/com/jme3/asset/BlenderKey.java
  2. 38
      engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java
  3. 24
      engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java
  4. 27
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java
  5. 70
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java

@ -33,12 +33,8 @@ package com.jme3.asset;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue; import java.util.Queue;
import java.util.Set;
import com.jme3.bounding.BoundingVolume; import com.jme3.bounding.BoundingVolume;
import com.jme3.collision.Collidable; import com.jme3.collision.Collidable;
@ -66,13 +62,6 @@ import com.jme3.texture.Texture;
public class BlenderKey extends ModelKey { public class BlenderKey extends ModelKey {
protected static final int DEFAULT_FPS = 25; protected static final int DEFAULT_FPS = 25;
/**
* Animation definitions. The key is the object name that owns the animation. The value is a map between animation
* name and its start and stop frames. Blender stores a pointer for animation within object. Therefore one object
* can only have one animation at the time. We want to be able to switch between animations for one object so we
* need to map the object name to animation names the object will use.
*/
protected Map<String, Map<String, int[]>> animations;
/** /**
* FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time
* between the frames. * between the frames.
@ -126,76 +115,6 @@ public class BlenderKey extends ModelKey {
super(name); super(name);
} }
/**
* This method adds an animation definition. If a definition already eixists in the key then it is replaced.
* @param objectName
* the name of animation's owner
* @param name
* the name of the animation
* @param start
* the start frame of the animation
* @param stop
* the stop frame of the animation
*/
public synchronized void addAnimation(String objectName, String name, int start, int stop) {
if (objectName == null) {
throw new IllegalArgumentException("Object name cannot be null!");
}
if (name == null) {
throw new IllegalArgumentException("Animation name cannot be null!");
}
if (start > stop) {
throw new IllegalArgumentException("Start frame cannot be greater than stop frame!");
}
if (animations == null) {
animations = new HashMap<String, Map<String, int[]>>();
animations.put(objectName, new HashMap<String, int[]>());
}
Map<String, int[]> objectAnimations = animations.get(objectName);
if (objectAnimations == null) {
objectAnimations = new HashMap<String, int[]>();
animations.put(objectName, objectAnimations);
}
objectAnimations.put(name, new int[] { start, stop });
}
/**
* This method returns the animation frames boundaries.
* @param objectName
* the name of animation's owner
* @param name
* animation name
* @return animation frame boundaries in a table [start, stop] or null if animation of the given name does not
* exists
*/
public int[] getAnimationFrames(String objectName, String name) {
Map<String, int[]> objectAnimations = animations == null ? null : animations.get(objectName);
int[] frames = objectAnimations == null ? null : objectAnimations.get(name);
return frames == null ? null : frames.clone();
}
/**
* This method returns the animation names for the given object name.
* @param objectName
* the name of the object
* @return an array of animations for this object
*/
public Set<String> getAnimationNames(String objectName) {
Map<String, int[]> objectAnimations = animations == null ? null : animations.get(objectName);
return objectAnimations == null ? null : objectAnimations.keySet();
}
/**
* This method returns the animations map.
* The key is the animated spatial name. The value is a map where the key
* is the animation name and the value is 2-element array of int that has
* start and stop frame of the animation.
* @return the animations map
*/
public Map<String, Map<String, int[]>> getAnimations() {
return animations;
}
/** /**
* This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25. * This method returns frames per second amount. The default value is BlenderKey.DEFAULT_FPS = 25.
* @return the frames per second amount * @return the frames per second amount
@ -424,22 +343,6 @@ public class BlenderKey extends ModelKey {
public void write(JmeExporter e) throws IOException { public void write(JmeExporter e) throws IOException {
super.write(e); super.write(e);
OutputCapsule oc = e.getCapsule(this); OutputCapsule oc = e.getCapsule(this);
// saving animations
oc.write(animations == null ? 0 : animations.size(), "anim-size", 0);
if (animations != null) {
int objectCounter = 0;
for (Entry<String, Map<String, int[]>> animEntry : animations.entrySet()) {
oc.write(animEntry.getKey(), "animated-object-" + objectCounter, null);
int animsAmount = animEntry.getValue().size();
oc.write(animsAmount, "anims-amount-" + objectCounter, 0);
for (Entry<String, int[]> animsEntry : animEntry.getValue().entrySet()) {
oc.write(animsEntry.getKey(), "anim-name-" + objectCounter, null);
oc.write(animsEntry.getValue(), "anim-frames-" + objectCounter, null);
}
++objectCounter;
}
}
// saving the rest of the data
oc.write(fps, "fps", DEFAULT_FPS); oc.write(fps, "fps", DEFAULT_FPS);
oc.write(generatedTextureWidth, "generated-texture-width", 20); oc.write(generatedTextureWidth, "generated-texture-width", 20);
oc.write(generatedTextureHeight, "generated-texture-height", 20); oc.write(generatedTextureHeight, "generated-texture-height", 20);
@ -458,28 +361,6 @@ public class BlenderKey extends ModelKey {
public void read(JmeImporter e) throws IOException { public void read(JmeImporter e) throws IOException {
super.read(e); super.read(e);
InputCapsule ic = e.getCapsule(this); InputCapsule ic = e.getCapsule(this);
// reading animations
int animSize = ic.readInt("anim-size", 0);
if (animSize > 0) {
if (animations == null) {
animations = new HashMap<String, Map<String, int[]>>(animSize);
} else {
animations.clear();
}
for (int i = 0; i < animSize; ++i) {
String objectName = ic.readString("animated-object-" + i, null);
int animationsAmount = ic.readInt("anims-amount-" + i, 0);
Map<String, int[]> objectAnimations = new HashMap<String, int[]>(animationsAmount);
for (int j = 0; j < animationsAmount; ++j) {
String animName = ic.readString("anim-name-" + i, null);
int[] animFrames = ic.readIntArray("anim-frames-" + i, null);
objectAnimations.put(animName, animFrames);
}
animations.put(objectName, objectAnimations);
}
}
// reading the rest of the data
fps = ic.readInt("fps", DEFAULT_FPS); fps = ic.readInt("fps", DEFAULT_FPS);
generatedTextureWidth = ic.readInt("generated-texture-width", 20); generatedTextureWidth = ic.readInt("generated-texture-width", 20);
generatedTextureHeight = ic.readInt("generated-texture-height", 20); generatedTextureHeight = ic.readInt("generated-texture-height", 20);
@ -498,7 +379,6 @@ public class BlenderKey extends ModelKey {
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
int result = super.hashCode(); int result = super.hashCode();
result = prime * result + (animations == null ? 0 : animations.hashCode());
result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode()); result = prime * result + (assetRootPath == null ? 0 : assetRootPath.hashCode());
result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode()); result = prime * result + (defaultMaterial == null ? 0 : defaultMaterial.hashCode());
result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode()); result = prime * result + (faceCullMode == null ? 0 : faceCullMode.hashCode());
@ -526,13 +406,6 @@ public class BlenderKey extends ModelKey {
return false; return false;
} }
BlenderKey other = (BlenderKey) obj; BlenderKey other = (BlenderKey) obj;
if (animations == null) {
if (other.animations != null) {
return false;
}
} else if (!animations.equals(other.animations)) {
return false;
}
if (assetRootPath == null) { if (assetRootPath == null) {
if (other.assetRootPath != null) { if (other.assetRootPath != null) {
return false; return false;

@ -39,7 +39,7 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jme3.animation.Bone; import com.jme3.animation.Bone;
import com.jme3.animation.BoneTrack; import com.jme3.animation.Track;
import com.jme3.math.Matrix4f; import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
@ -320,20 +320,16 @@ public class ArmatureHelper extends AbstractBlenderHelper {
* the structure containing the tracks * the structure containing the tracks
* @param blenderContext * @param blenderContext
* the blender context * the blender context
* @param objectName
* the name of the object that will use these tracks
* @param animationName
* the animation name
* @return a list of tracks for the specified animation * @return a list of tracks for the specified animation
* @throws BlenderFileException * @throws BlenderFileException
* an exception is thrown when there are problems with the blend * an exception is thrown when there are problems with the blend
* file * file
*/ */
public BoneTrack[] getTracks(Structure actionStructure, BlenderContext blenderContext, String objectName, String animationName) throws BlenderFileException { public Track<?>[] getTracks(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
if (blenderVersion < 250) { if (blenderVersion < 250) {
return this.getTracks249(actionStructure, blenderContext, objectName, animationName); return this.getTracks249(actionStructure, blenderContext);
} else { } else {
return this.getTracks250(actionStructure, blenderContext, objectName, animationName); return this.getTracks250(actionStructure, blenderContext);
} }
} }
@ -344,26 +340,21 @@ public class ArmatureHelper extends AbstractBlenderHelper {
* the structure containing the tracks * the structure containing the tracks
* @param blenderContext * @param blenderContext
* the blender context * the blender context
* @param objectName
* the name of the object that will use these tracks
* @param animationName
* the animation name
* @return a list of tracks for the specified animation * @return a list of tracks for the specified animation
* @throws BlenderFileException * @throws BlenderFileException
* an exception is thrown when there are problems with the blend * an exception is thrown when there are problems with the blend
* file * file
*/ */
private BoneTrack[] getTracks250(Structure actionStructure, BlenderContext blenderContext, String objectName, String animationName) throws BlenderFileException { private Track<?>[] getTracks250(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.log(Level.INFO, "Getting tracks!"); LOGGER.log(Level.INFO, "Getting tracks!");
int fps = blenderContext.getBlenderKey().getFps(); int fps = blenderContext.getBlenderKey().getFps();
int[] animationFrames = blenderContext.getBlenderKey().getAnimationFrames(objectName, animationName);
Structure groups = (Structure) actionStructure.getFieldValue("groups"); Structure groups = (Structure) actionStructure.getFieldValue("groups");
List<Structure> actionGroups = groups.evaluateListBase(blenderContext);//bActionGroup List<Structure> actionGroups = groups.evaluateListBase(blenderContext);//bActionGroup
if (actionGroups != null && actionGroups.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) { if (actionGroups != null && actionGroups.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) {
throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!"); throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!");
} }
List<BoneTrack> tracks = new ArrayList<BoneTrack>(); List<Track<?>> tracks = new ArrayList<Track<?>>();
for (Structure actionGroup : actionGroups) { for (Structure actionGroup : actionGroups) {
String name = actionGroup.getFieldValue("name").toString(); String name = actionGroup.getFieldValue("name").toString();
Integer boneIndex = bonesMap.get(name); Integer boneIndex = bonesMap.get(name);
@ -389,10 +380,10 @@ public class ArmatureHelper extends AbstractBlenderHelper {
} }
Ipo ipo = new Ipo(bezierCurves); Ipo ipo = new Ipo(bezierCurves);
tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps)); tracks.add(ipo.calculateTrack(boneIndex.intValue(), 0, ipo.getLastFrame(), fps));
} }
} }
return tracks.toArray(new BoneTrack[tracks.size()]); return tracks.toArray(new Track<?>[tracks.size()]);
} }
/** /**
@ -402,26 +393,21 @@ public class ArmatureHelper extends AbstractBlenderHelper {
* the structure containing the tracks * the structure containing the tracks
* @param blenderContext * @param blenderContext
* the blender context * the blender context
* @param objectName
* the name of the object that will use these tracks
* @param animationName
* the animation name
* @return a list of tracks for the specified animation * @return a list of tracks for the specified animation
* @throws BlenderFileException * @throws BlenderFileException
* an exception is thrown when there are problems with the blend * an exception is thrown when there are problems with the blend
* file * file
*/ */
private BoneTrack[] getTracks249(Structure actionStructure, BlenderContext blenderContext, String objectName, String animationName) throws BlenderFileException { private Track<?>[] getTracks249(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.log(Level.INFO, "Getting tracks!"); LOGGER.log(Level.INFO, "Getting tracks!");
IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class); IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
int fps = blenderContext.getBlenderKey().getFps(); int fps = blenderContext.getBlenderKey().getFps();
int[] animationFrames = blenderContext.getBlenderKey().getAnimationFrames(objectName, animationName);
Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase"); Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");
List<Structure> actionChannels = chanbase.evaluateListBase(blenderContext);//bActionChannel List<Structure> actionChannels = chanbase.evaluateListBase(blenderContext);//bActionChannel
if (actionChannels != null && actionChannels.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) { if (actionChannels != null && actionChannels.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) {
throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!"); throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!");
} }
List<BoneTrack> tracks = new ArrayList<BoneTrack>(); List<Track<?>> tracks = new ArrayList<Track<?>>();
for (Structure bActionChannel : actionChannels) { for (Structure bActionChannel : actionChannels) {
String name = bActionChannel.getFieldValue("name").toString(); String name = bActionChannel.getFieldValue("name").toString();
Integer boneIndex = bonesMap.get(name); Integer boneIndex = bonesMap.get(name);
@ -430,11 +416,11 @@ public class ArmatureHelper extends AbstractBlenderHelper {
if (!p.isNull()) { if (!p.isNull()) {
Structure ipoStructure = p.fetchData(blenderContext.getInputStream()).get(0); Structure ipoStructure = p.fetchData(blenderContext.getInputStream()).get(0);
Ipo ipo = ipoHelper.createIpo(ipoStructure, blenderContext); Ipo ipo = ipoHelper.createIpo(ipoStructure, blenderContext);
tracks.add(ipo.calculateTrack(boneIndex.intValue(), animationFrames[0], animationFrames[1], fps)); tracks.add(ipo.calculateTrack(boneIndex.intValue(), 0, ipo.getLastFrame(), fps));
} }
} }
} }
return tracks.toArray(new BoneTrack[tracks.size()]); return tracks.toArray(new Track<?>[tracks.size()]);
} }
/** /**

@ -1,6 +1,8 @@
package com.jme3.scene.plugins.blender.animations; package com.jme3.scene.plugins.blender.animations;
import com.jme3.animation.BoneTrack; import com.jme3.animation.BoneTrack;
import com.jme3.animation.SpatialTrack;
import com.jme3.animation.Track;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.curves.BezierCurve; import com.jme3.scene.plugins.blender.curves.BezierCurve;
@ -28,7 +30,7 @@ public class Ipo {
/** A list of bezier curves for this interpolation object. */ /** A list of bezier curves for this interpolation object. */
private BezierCurve[] bezierCurves; private BezierCurve[] bezierCurves;
/** Each ipo contains one bone track. */ /** Each ipo contains one bone track. */
private BoneTrack calculatedTrack; private Track<?> calculatedTrack;
/** /**
* Constructor. Stores the bezier curves. * Constructor. Stores the bezier curves.
@ -105,8 +107,9 @@ public class Ipo {
/** /**
* This method calculates the value of the curves as a bone track between the specified frames. * This method calculates the value of the curves as a bone track between the specified frames.
* @param boneIndex * @param targetIndex
* the index of the bone for which the method calculates the tracks * the index of the target for which the method calculates the tracks
* IMPORTANT! Aet to -1 (or any negative number) if you want to load spatial animation.
* @param startFrame * @param startFrame
* the firs frame of tracks (inclusive) * the firs frame of tracks (inclusive)
* @param stopFrame * @param stopFrame
@ -115,7 +118,8 @@ public class Ipo {
* frame rate (frames per second) * frame rate (frames per second)
* @return bone track for the specified bone * @return bone track for the specified bone
*/ */
public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) { public Track<?> calculateTrack(int targetIndex, int startFrame, int stopFrame, int fps) {
if(calculatedTrack == null) {
//preparing data for track //preparing data for track
int framesAmount = stopFrame - startFrame; int framesAmount = stopFrame - startFrame;
float start = (startFrame - 1.0f) / fps; float start = (startFrame - 1.0f) / fps;
@ -127,7 +131,7 @@ public class Ipo {
Quaternion[] rotations = new Quaternion[framesAmount + 1]; Quaternion[] rotations = new Quaternion[framesAmount + 1];
float[] quaternionRotation = new float[4]; float[] quaternionRotation = new float[4];
float[] objectRotation = new float[3]; float[] objectRotation = new float[3];
boolean bObjectRotation = false; boolean bSpatialTrack = targetIndex < 0;
Vector3f[] scales = new Vector3f[framesAmount + 1]; Vector3f[] scales = new Vector3f[framesAmount + 1];
float[] scale = new float[3]; float[] scale = new float[3];
@ -147,7 +151,6 @@ public class Ipo {
case OB_ROT_Y: case OB_ROT_Y:
case OB_ROT_Z: case OB_ROT_Z:
objectRotation[bezierCurves[j].getType() - 7] = (float) value; objectRotation[bezierCurves[j].getType() - 7] = (float) value;
bObjectRotation = true;
break; break;
case AC_SIZE_X: case AC_SIZE_X:
case AC_SIZE_Y: case AC_SIZE_Y:
@ -167,11 +170,16 @@ public class Ipo {
} }
} }
translations[index] = new Vector3f(translation[0], translation[1], translation[2]); translations[index] = new Vector3f(translation[0], translation[1], translation[2]);
rotations[index] = bObjectRotation ? new Quaternion().fromAngles(objectRotation) rotations[index] = bSpatialTrack ? new Quaternion().fromAngles(objectRotation)
: new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]); : new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);
scales[index] = new Vector3f(scale[0], scale[1], scale[2]); scales[index] = new Vector3f(scale[0], scale[1], scale[2]);
} }
calculatedTrack = new BoneTrack(boneIndex, times, translations, rotations, scales); if(bSpatialTrack) {
calculatedTrack = new SpatialTrack(times, translations, rotations, scales);
} else {
calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales);
}
}
return calculatedTrack; return calculatedTrack;
} }
} }

@ -6,7 +6,6 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -15,6 +14,7 @@ import com.jme3.animation.Animation;
import com.jme3.animation.Bone; import com.jme3.animation.Bone;
import com.jme3.animation.Skeleton; import com.jme3.animation.Skeleton;
import com.jme3.animation.SkeletonControl; import com.jme3.animation.SkeletonControl;
import com.jme3.animation.Track;
import com.jme3.math.Matrix4f; import com.jme3.math.Matrix4f;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
@ -116,28 +116,27 @@ import com.jme3.util.BufferUtils;
this.readVerticesWeightsData(objectStructure, meshStructure, blenderContext); this.readVerticesWeightsData(objectStructure, meshStructure, blenderContext);
//read animations //read animations
String objectName = objectStructure.getName();
Set<String> animationNames = blenderContext.getBlenderKey().getAnimationNames(objectName);
System.out.println("Loaded animation " + objectName);
if (animationNames != null && animationNames.size() > 0) {
ArrayList<Animation> animations = new ArrayList<Animation>(); ArrayList<Animation> animations = new ArrayList<Animation>();
List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
for (FileBlockHeader header : actionHeaders) { for (FileBlockHeader header : actionHeaders) {
Structure actionStructure = header.getStructure(blenderContext); Structure actionStructure = header.getStructure(blenderContext);
String actionName = actionStructure.getName(); String actionName = actionStructure.getName();
if (animationNames.contains(actionName)) {
int[] animationFrames = blenderContext.getBlenderKey().getAnimationFrames(objectName, actionName);
int fps = blenderContext.getBlenderKey().getFps();
float start = (float) animationFrames[0] / (float) fps;
float stop = (float) animationFrames[1] / (float) fps;
Animation boneAnimation = new Animation(actionName, stop - start);
boneAnimation.setTracks(armatureHelper.getTracks(actionStructure, blenderContext, objectName, actionName));
animations.add(boneAnimation);
Track<?>[] tracks = armatureHelper.getTracks(actionStructure, blenderContext);
//determining the animation time
float maximumTrackLength = 0;
for(Track<?> track : tracks) {
float length = track.getLength();
if(length > maximumTrackLength) {
maximumTrackLength = length;
} }
} }
animData = new AnimData(new Skeleton(bones), animations);
Animation boneAnimation = new Animation(actionName, maximumTrackLength);
boneAnimation.setTracks(tracks);
animations.add(boneAnimation);
} }
animData = new AnimData(new Skeleton(bones), animations);
} }
} }
} }

@ -1,10 +1,22 @@
package com.jme3.scene.plugins.blender.modifiers; 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 java.util.logging.Logger;
import com.jme3.animation.AnimControl;
import com.jme3.animation.Animation;
import com.jme3.animation.Track;
import com.jme3.scene.Node; import com.jme3.scene.Node;
import com.jme3.scene.plugins.blender.BlenderContext; 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.constraints.Constraint;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; import com.jme3.scene.plugins.blender.exceptions.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.file.Structure;
import com.jme3.scene.plugins.ogre.AnimData; import com.jme3.scene.plugins.ogre.AnimData;
@ -41,13 +53,12 @@ import com.jme3.scene.plugins.ogre.AnimData;
*/ */
public ObjectAnimationModifier(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException { public ObjectAnimationModifier(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.warning("Object animation modifier not yet implemented!"); LOGGER.warning("Object animation modifier not yet implemented!");
/*
Pointer pIpo = (Pointer) objectStructure.getFieldValue("ipo"); Pointer pIpo = (Pointer) objectStructure.getFieldValue("ipo");
if (pIpo.isNotNull()) { if (pIpo.isNotNull()) {
// check if there is an action name connected with this ipo // check if there is an action name connected with this ipo
String objectAnimationName = null; String objectAnimationName = null;
List<FileBlockHeader> actionBlocks = blenderContext List<FileBlockHeader> actionBlocks = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
for (FileBlockHeader actionBlock : actionBlocks) { for (FileBlockHeader actionBlock : actionBlocks) {
Structure action = actionBlock.getStructure(blenderContext); Structure action = actionBlock.getStructure(blenderContext);
List<Structure> actionChannels = ((Structure) action.getFieldValue("chanbase")).evaluateListBase(blenderContext); List<Structure> actionChannels = ((Structure) action.getFieldValue("chanbase")).evaluateListBase(blenderContext);
@ -68,38 +79,51 @@ import com.jme3.scene.plugins.ogre.AnimData;
IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class); IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
Structure ipoStructure = pIpo.fetchData(blenderContext.getInputStream()).get(0); Structure ipoStructure = pIpo.fetchData(blenderContext.getInputStream()).get(0);
Ipo ipo = ipoHelper.createIpo(ipoStructure, blenderContext); Ipo ipo = ipoHelper.createIpo(ipoStructure, blenderContext);
int[] animationFrames = blenderContext.getBlenderKey().getAnimationFrames(objectName, objectAnimationName);
if (animationFrames == null) {// if the name was created here there are no frames set for the animation
animationFrames = new int[] { 1, ipo.getLastFrame() };
}
int fps = blenderContext.getBlenderKey().getFps(); int fps = blenderContext.getBlenderKey().getFps();
float start = (float) animationFrames[0] / (float) fps;
float stop = (float) animationFrames[1] / (float) fps;
// calculating track for the only bone in this skeleton // calculating track for the only bone in this skeleton
BoneTrack[] tracks = new BoneTrack[1]; Track<?> track = ipo.calculateTrack(-1, 0, ipo.getLastFrame(), fps);
tracks[0] = ipo.calculateTrack(0, animationFrames[0], animationFrames[1], fps);
BoneAnimation boneAnimation = new BoneAnimation(objectAnimationName, stop - start); Animation animation = new Animation(objectAnimationName, ipo.getLastFrame() / fps);
boneAnimation.setTracks(tracks); animation.setTracks(new Track<?>[] { track });
ArrayList<Animation> animations = new ArrayList<Animation>(1); ArrayList<Animation> animations = new ArrayList<Animation>(1);
animations.add(boneAnimation); animations.add(animation);
// preparing the object's bone animData = new AnimData(null, animations);
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Transform t = objectHelper.getTransformation(objectStructure, blenderContext);
Bone bone = new Bone(null);
bone.setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale());
animData = new AnimData(new Skeleton(new Bone[] { bone }), animations);
objectOMA = objectStructure.getOldMemoryAddress(); objectOMA = objectStructure.getOldMemoryAddress();
} }
*/
} }
@Override @Override
public Node apply(Node node, BlenderContext blenderContext) { public Node apply(Node node, BlenderContext blenderContext) {
LOGGER.warning("Object animation modifier not yet implemented!"); if(invalid) {
LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName());
}//if invalid, animData will be null
if(animData == null) {
return node;
}
ArrayList<Animation> animList = animData.anims;
if (animList != null && animList.size() > 0) {
List<Constraint> constraints = blenderContext.getConstraints(this.objectOMA);
HashMap<String, Animation> anims = new HashMap<String, Animation>();
for (int i = 0; i < animList.size(); ++i) {
Animation animation = (Animation) animList.get(i).clone();
// baking constraints into animations
if (constraints != null && constraints.size() > 0) {
for (Constraint constraint : constraints) {
constraint.affectAnimation(animation, 0);
}
}
anims.put(animation.getName(), animation);
}
AnimControl control = new AnimControl(null);
control.setAnimations(anims);
node.addControl(control);
}
return node; return node;
} }

Loading…
Cancel
Save