Many changes:

- simplified implementation for bones loading
- static bones poses discarded
- loading of object animation for blender 2.50+ added (didn't work for these versions before)
- several small bugfixes

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9115 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
Kae..pl 13 years ago
parent fb68a176b5
commit 3a84693f68
  1. 28
      engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java
  2. 385
      engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java
  3. 204
      engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java
  4. 409
      engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java
  5. 281
      engine/src/blender/com/jme3/scene/plugins/blender/animations/IpoHelper.java
  6. 22
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java
  7. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintAction.java
  8. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintChildOf.java
  9. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintClampTo.java
  10. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDampTrack.java
  11. 28
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDistLimit.java
  12. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintFollowPath.java
  13. 6
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
  14. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintInverseKinematics.java
  15. 24
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLike.java
  16. 22
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLimit.java
  17. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLockTrack.java
  18. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintMinMax.java
  19. 5
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintNull.java
  20. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPivot.java
  21. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPython.java
  22. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRigidBodyJoint.java
  23. 22
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLike.java
  24. 34
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLimit.java
  25. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintShrinkWrap.java
  26. 20
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLike.java
  27. 18
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLimit.java
  28. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSplineInverseKinematic.java
  29. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintStretchTo.java
  30. 8
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintTransform.java
  31. 16
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/Feature.java
  32. 80
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java
  33. 207
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java
  34. 83
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java
  35. 2
      engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java

@ -46,6 +46,7 @@ import com.jme3.asset.AssetManager;
import com.jme3.asset.BlenderKey; import com.jme3.asset.BlenderKey;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.animations.Ipo; import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.constraints.Constraint; import com.jme3.scene.plugins.blender.constraints.Constraint;
import com.jme3.scene.plugins.blender.file.BlenderInputStream; import com.jme3.scene.plugins.blender.file.BlenderInputStream;
@ -112,6 +113,8 @@ public class BlenderContext {
private Map<Long, Skeleton> skeletons = new HashMap<Long, Skeleton>(); private Map<Long, Skeleton> skeletons = new HashMap<Long, Skeleton>();
/** A map of mesh contexts. */ /** A map of mesh contexts. */
protected Map<Long, MeshContext> meshContexts = new HashMap<Long, MeshContext>(); protected Map<Long, MeshContext> meshContexts = new HashMap<Long, MeshContext>();
/** A map of bone contexts. */
protected Map<Long, BoneContext> boneContexts = new HashMap<Long, BoneContext>();
/** A map of material contexts. */ /** A map of material contexts. */
protected Map<Material, MaterialContext> materialContexts = new HashMap<Material, MaterialContext>(); protected Map<Material, MaterialContext> materialContexts = new HashMap<Material, MaterialContext>();
/** A map og helpers that perform loading. */ /** A map og helpers that perform loading. */
@ -541,7 +544,32 @@ public class BlenderContext {
public MeshContext getMeshContext(Long meshOMA) { public MeshContext getMeshContext(Long meshOMA) {
return this.meshContexts.get(meshOMA); return this.meshContexts.get(meshOMA);
} }
/**
* This method sets the bone context for the given bone old memory address.
* If the context is already set it will be replaced.
*
* @param boneOMA
* the bone's old memory address
* @param boneContext
* the bones's context
*/
public void setBoneContext(Long boneOMA, BoneContext boneContext) {
this.boneContexts.put(boneOMA, boneContext);
}
/**
* This method returns the bone context for the given bone old memory
* address. If no context exists then <b>null</b> is returned.
*
* @param boneOMA
* the bone's old memory address
* @return bone's context
*/
public BoneContext getBoneContext(Long boneOMA) {
return boneContexts.get(boneOMA);
}
/** /**
* This method sets the material context for the given material. If the * This method sets the material context for the given material. If the
* context is already set it will be replaced. * context is already set it will be replaced.

@ -42,44 +42,39 @@ import com.jme3.animation.Bone;
import com.jme3.animation.BoneTrack; import com.jme3.animation.BoneTrack;
import com.jme3.animation.Skeleton; import com.jme3.animation.Skeleton;
import com.jme3.math.Matrix4f; import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.scene.plugins.blender.AbstractBlenderHelper; import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.curves.BezierCurve; import com.jme3.scene.plugins.blender.curves.BezierCurve;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.BlenderInputStream;
import com.jme3.scene.plugins.blender.file.DynamicArray;
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.Pointer; 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.blender.objects.ObjectHelper;
/** /**
* This class defines the methods to calculate certain aspects of animation and armature functionalities. * This class defines the methods to calculate certain aspects of animation and
* armature functionalities.
*
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
public class ArmatureHelper extends AbstractBlenderHelper { public class ArmatureHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName()); private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName());
/** A map of bones and their old memory addresses. */ /** A map of bones and their old memory addresses. */
private Map<Bone, Long> bonesOMAs = new HashMap<Bone, Long>(); private Map<Bone, Long> bonesOMAs = new HashMap<Bone, Long>();
/** Bone transforms need to be applied after the model is attached to the skeleton. Otherwise it will have no effect. */
private Map<Bone, Transform> boneBindTransforms = new HashMap<Bone, Transform>(); /**
* This constructor parses the given blender version and stores the result.
/** * Some functionalities may differ in different blender versions.
* This constructor parses the given blender version and stores the result. Some functionalities may differ in *
* different blender versions. * @param blenderVersion
* @param blenderVersion * the version read from the blend file
* the version read from the blend file * @param fixUpAxis
* @param fixUpAxis * a variable that indicates if the Y asxis is the UP axis or not
* a variable that indicates if the Y asxis is the UP axis or not */
*/ public ArmatureHelper(String blenderVersion, boolean fixUpAxis) {
public ArmatureHelper(String blenderVersion, boolean fixUpAxis) { super(blenderVersion, fixUpAxis);
super(blenderVersion, fixUpAxis); }
}
/**
/**
* This method builds the object's bones structure. * This method builds the object's bones structure.
* *
* @param boneStructure * @param boneStructure
@ -96,119 +91,61 @@ public class ArmatureHelper extends AbstractBlenderHelper {
* an exception is thrown when there is problem with the blender * an exception is thrown when there is problem with the blender
* file * file
*/ */
@SuppressWarnings("unchecked") public void buildBones(Structure boneStructure, Bone parent, List<Bone> result, Matrix4f arbt, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
public void buildBones(Structure boneStructure, Bone parent, List<Bone> result, Matrix4f arbt, BoneContext bc = new BoneContext(boneStructure, arbt, bonesPoseChannels, blenderContext);
final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException { bc.buildBone(result, bonesOMAs, blenderContext);
String boneName = boneStructure.getFieldValue("name").toString();
Long boneOMA = boneStructure.getOldMemoryAddress();
Bone bone = new Bone(boneName);
this.bonesOMAs.put(bone, boneOMA);
blenderContext.addLoadedFeatures(boneStructure.getOldMemoryAddress(), boneName, boneStructure, bone);
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Matrix4f boneMatrix = arbt.mult(objectHelper.getMatrix(boneStructure, "arm_mat", true));
Pointer pParentStructure = (Pointer) boneStructure.getFieldValue("parent");
if(pParentStructure.isNotNull()) {
Structure parentStructure = pParentStructure.fetchData(blenderContext.getInputStream()).get(0);
Matrix4f parentArmMat = objectHelper.getMatrix(parentStructure, "arm_mat", true);
parentArmMat = arbt.mult(parentArmMat).invertLocal();
boneMatrix = parentArmMat.multLocal(boneMatrix);
}
Transform baseTransform = new Transform(boneMatrix.toTranslationVector(), boneMatrix.toRotationQuat());
baseTransform.setScale(objectHelper.getScale(boneMatrix));
bone.setBindTransforms(baseTransform.getTranslation(), baseTransform.getRotation(), baseTransform.getScale());
// loading poses
Structure poseChannel = bonesPoseChannels.get(boneStructure.getOldMemoryAddress());
DynamicArray<Number> loc = (DynamicArray<Number>) poseChannel.getFieldValue("loc");
DynamicArray<Number> size = (DynamicArray<Number>) poseChannel.getFieldValue("size");
DynamicArray<Number> quat = (DynamicArray<Number>) poseChannel.getFieldValue("quat");
Transform transform = new Transform();
if (blenderContext.getBlenderKey().isFixUpAxis()) {
transform.setTranslation(loc.get(0).floatValue(), -loc.get(2).floatValue(), loc.get(1).floatValue());
transform.setRotation(new Quaternion(quat.get(1).floatValue(), -quat.get(3).floatValue(), quat.get(2).floatValue(), quat.get(0).floatValue()));
transform.setScale(size.get(0).floatValue(), size.get(2).floatValue(), size.get(1).floatValue());
} else {
transform.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue());
transform.setRotation(new Quaternion(quat.get(0).floatValue(), quat.get(1).floatValue(), quat.get(2).floatValue(), quat.get(3).floatValue()));
transform.setScale(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
}
this.boneBindTransforms.put(bone, transform);
if (parent != null) {
parent.addChild(bone);
}
result.add(bone);
List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext);
for (Structure child : childbase) {
this.buildBones(child, bone, result, arbt, bonesPoseChannels, blenderContext);
}
}
public Transform getLocalTransform(Bone bone) {
Transform transform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
transform.setScale(bone.getLocalScale());
return transform;
} }
/**
* This method returns the old memory address of a bone. If the bone does not exist in the blend file - zero is
* returned.
* @param bone
* the bone whose old memory address we seek
* @return the old memory address of the given bone
*/
public Long getBoneOMA(Bone bone) {
Long result = bonesOMAs.get(bone);
if (result == null) {
result = Long.valueOf(0);
}
return result;
}
/** /**
* This method returns the bind transform for the specified bone. * This method returns the old memory address of a bone. If the bone does
* not exist in the blend file - zero is returned.
*
* @param bone * @param bone
* the bone * the bone whose old memory address we seek
* @return bone's bind transform * @return the old memory address of the given bone
*/
public Long getBoneOMA(Bone bone) {
Long result = bonesOMAs.get(bone);
if (result == null) {
result = Long.valueOf(0);
}
return result;
}
/**
* 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 Transform getBoneBindTransform(Bone bone) { public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
return boneBindTransforms.get(bone); Map<Integer, Integer> result = null;
} if (skeleton.getBoneCount() != 0) {
result = new HashMap<Integer, Integer>();
List<Structure> deformGroups = defBaseStructure.evaluateListBase(blenderContext);// bDeformGroup
int groupIndex = 0;
for (Structure deformGroup : deformGroups) {
String deformGroupName = deformGroup.getFieldValue("name").toString();
Integer boneIndex = this.getBoneIndex(skeleton, deformGroupName);
if (boneIndex != null) {
result.put(Integer.valueOf(groupIndex), boneIndex);
}
++groupIndex;
}
}
return result;
}
/** @Override
* 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 public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
* bone index in the armature. return true;
* @param defBaseStructure }
* a bPose structure of the object
* @return bone group-to-index map
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow corrupted
*/
public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
Map<Integer, Integer> result = null;
if (skeleton.getBoneCount() != 0) {
result = new HashMap<Integer, Integer>();
List<Structure> deformGroups = defBaseStructure.evaluateListBase(blenderContext);//bDeformGroup
int groupIndex = 0;
for (Structure deformGroup : deformGroups) {
String deformGroupName = deformGroup.getFieldValue("name").toString();
Integer boneIndex = this.getBoneIndex(skeleton, deformGroupName);
if (boneIndex != null) {
result.put(Integer.valueOf(groupIndex), boneIndex);
}
++groupIndex;
}
}
return result;
}
@Override
public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
return true;
}
/** /**
* This method retuns the bone tracks for animation. * This method retuns the bone tracks for animation.
* *
@ -221,16 +158,17 @@ public class ArmatureHelper extends AbstractBlenderHelper {
* 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, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException { public BoneTrack[] getTracks(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
if (blenderVersion < 250) { if (blenderVersion < 250) {
return this.getTracks249(actionStructure, skeleton, blenderContext); return this.getTracks249(actionStructure, skeleton, blenderContext);
} else { } else {
return this.getTracks250(actionStructure, skeleton, blenderContext); return this.getTracks250(actionStructure, skeleton, blenderContext);
} }
} }
/** /**
* This method retuns the bone tracks for animation for blender version 2.50 and higher. * This method retuns the bone tracks for animation for blender version 2.50
* and higher.
* *
* @param actionStructure * @param actionStructure
* the structure containing the tracks * the structure containing the tracks
@ -241,45 +179,37 @@ public class ArmatureHelper extends AbstractBlenderHelper {
* 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, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException { private BoneTrack[] getTracks250(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.log(Level.INFO, "Getting tracks!"); LOGGER.log(Level.INFO, "Getting tracks!");
int fps = blenderContext.getBlenderKey().getFps(); IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
Structure groups = (Structure) actionStructure.getFieldValue("groups"); int fps = blenderContext.getBlenderKey().getFps();
List<Structure> actionGroups = groups.evaluateListBase(blenderContext);//bActionGroup Structure groups = (Structure) actionStructure.getFieldValue("groups");
List<BoneTrack> tracks = new ArrayList<BoneTrack>(); List<Structure> actionGroups = groups.evaluateListBase(blenderContext);// bActionGroup
for (Structure actionGroup : actionGroups) { List<BoneTrack> tracks = new ArrayList<BoneTrack>();
String name = actionGroup.getFieldValue("name").toString(); for (Structure actionGroup : actionGroups) {
Integer boneIndex = this.getBoneIndex(skeleton, name); String name = actionGroup.getFieldValue("name").toString();
if (boneIndex != null) { int boneIndex = this.getBoneIndex(skeleton, name);
List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(blenderContext); if (boneIndex >= 0) {
BezierCurve[] bezierCurves = new BezierCurve[channels.size()]; List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(blenderContext);
int channelCounter = 0; BezierCurve[] bezierCurves = new BezierCurve[channels.size()];
for (Structure c : channels) { int channelCounter = 0;
//reading rna path first for (Structure c : channels) {
BlenderInputStream bis = blenderContext.getInputStream(); int type = ipoHelper.getCurveType(c, blenderContext);
int currentPosition = bis.getPosition(); Pointer pBezTriple = (Pointer) c.getFieldValue("bezt");
Pointer pRnaPath = (Pointer) c.getFieldValue("rna_path"); List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pRnaPath.getOldMemoryAddress()); bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);
bis.setPosition(dataFileBlock.getBlockPosition()); }
String rnaPath = bis.readString();
bis.setPosition(currentPosition);
int arrayIndex = ((Number) c.getFieldValue("array_index")).intValue();
int type = this.getCurveType(rnaPath, arrayIndex);
Pointer pBezTriple = (Pointer) c.getFieldValue("bezt"); Ipo ipo = new Ipo(bezierCurves, fixUpAxis);
List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream()); tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, 0, ipo.getLastFrame(), fps, false));
bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2); }
} }
return tracks.toArray(new BoneTrack[tracks.size()]);
}
Ipo ipo = new Ipo(bezierCurves, fixUpAxis); /**
tracks.add((BoneTrack) ipo.calculateTrack(boneIndex.intValue(), 0, ipo.getLastFrame(), fps)); * This method retuns the bone tracks for animation for blender version 2.49
} * (and probably several lower versions too).
}
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 * @param actionStructure
* the structure containing the tracks * the structure containing the tracks
@ -290,61 +220,44 @@ public class ArmatureHelper extends AbstractBlenderHelper {
* 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, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException { private BoneTrack[] getTracks249(Structure actionStructure, Skeleton skeleton, 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();
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
List<BoneTrack> tracks = new ArrayList<BoneTrack>(); List<BoneTrack> tracks = new ArrayList<BoneTrack>();
for (Structure bActionChannel : actionChannels) { for (Structure bActionChannel : actionChannels) {
String name = bActionChannel.getFieldValue("name").toString(); String name = bActionChannel.getFieldValue("name").toString();
Integer boneIndex = this.getBoneIndex(skeleton, name); int boneIndex = this.getBoneIndex(skeleton, name);
if (boneIndex != null && boneIndex.intValue() >= 0) { if (boneIndex >= 0) {
Pointer p = (Pointer) bActionChannel.getFieldValue("ipo"); Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");
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.fromIpoStructure(ipoStructure, blenderContext);
tracks.add((BoneTrack) ipo.calculateTrack(boneIndex.intValue(), 0, ipo.getLastFrame(), fps)); tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, 0, ipo.getLastFrame(), fps, false));
} }
} }
} }
return tracks.toArray(new BoneTrack[tracks.size()]); return tracks.toArray(new BoneTrack[tracks.size()]);
} }
/** /**
* This method returns the index of the bone in the given skeleton. * This method returns the index of the bone in the given skeleton.
* @param skeleton the skeleton *
* @param boneName the name of the bone * @param skeleton
* @return the index of the bone * the skeleton
*/ * @param boneName
private int getBoneIndex(Skeleton skeleton, String boneName) { * the name of the bone
int result = -1; * @return the index of the bone
for(int i=0;i<skeleton.getBoneCount() && result==-1;++i) { */
if(boneName.equals(skeleton.getBone(i).getName())) { private int getBoneIndex(Skeleton skeleton, String boneName) {
result = i; int result = -1;
} for (int i = 0; i < skeleton.getBoneCount() && result == -1; ++i) {
} if (boneName.equals(skeleton.getBone(i).getName())) {
return result; result = i;
} }
}
/** return result;
* This method parses the information stored inside the curve rna path and returns the proper type }
* of the curve.
* @param rnaPath the curve's rna path
* @param arrayIndex the array index of the stored data
* @return the type of the curve
*/
protected int getCurveType(String rnaPath, int arrayIndex) {
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;
}
throw new IllegalStateException("Unknown curve rna path: " + rnaPath);
}
} }

@ -0,0 +1,204 @@
package com.jme3.scene.plugins.blender.animations;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.jme3.animation.Bone;
import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.DynamicArray;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
/**
* This class holds the basic data that describes a bone.
*
* @author Marcin Roguski (Kaelthas)
*/
public class BoneContext {
/** The structure of the bone. */
private Structure boneStructure;
/** Bone's pose channel structure. */
private Structure poseChannel;
/** Bone's name. */
private String boneName;
/** This variable indicates if the Y axis should be the UP axis. */
private boolean fixUpAxis;
/** The bone's armature matrix. */
private Matrix4f armatureMatrix;
/** The parent context. */
private BoneContext parent;
/** The children of this context. */
private List<BoneContext> children = new ArrayList<BoneContext>();
/** Created bone (available after calling 'buildBone' method). */
private Bone bone;
/** Bone's pose transform (available after calling 'buildBone' method). */
private Transform poseTransform = new Transform();
/** The bone's rest matrix. */
private Matrix4f restMatrix;
/** Bone's total inverse transformation. */
private Matrix4f inverseTotalTransformation;
/** Bone's parent inverse matrix. */
private Matrix4f inverseParentMatrix;
/**
* Constructor. Creates the basic set of bone's data.
*
* @param boneStructure
* the bone's structure
* @param objectToArmatureMatrix
* object-to-armature transformation matrix
* @param bonesPoseChannels
* a map of pose channels for each bone OMA
* @param blenderContext
* the blender context
* @throws BlenderFileException
* an exception is thrown when problem with blender data reading
* occurs
*/
public BoneContext(Structure boneStructure, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
this(boneStructure, null, objectToArmatureMatrix, bonesPoseChannels, blenderContext);
}
/**
* Constructor. Creates the basic set of bone's data.
*
* @param boneStructure
* the bone's structure
* @param parent
* bone's parent (null if the bone is the root bone)
* @param objectToArmatureMatrix
* object-to-armature transformation matrix
* @param bonesPoseChannels
* a map of pose channels for each bone OMA
* @param blenderContext
* the blender context
* @throws BlenderFileException
* an exception is thrown when problem with blender data reading
* occurs
*/
private BoneContext(Structure boneStructure, BoneContext parent, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
this.parent = parent;
this.boneStructure = boneStructure;
boneName = boneStructure.getFieldValue("name").toString();
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
armatureMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", true);
fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis();
this.computeRestMatrix(objectToArmatureMatrix);
List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext);
for (Structure child : childbase) {
this.children.add(new BoneContext(child, this, objectToArmatureMatrix, bonesPoseChannels, blenderContext));
}
poseChannel = bonesPoseChannels.get(boneStructure.getOldMemoryAddress());
blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this);
}
/**
* This method computes the rest matrix for the bone.
*
* @param objectToArmatureMatrix
* object-to-armature transformation matrix
*/
private void computeRestMatrix(Matrix4f objectToArmatureMatrix) {
if (parent != null) {
inverseParentMatrix = parent.inverseTotalTransformation.clone();
} else if (fixUpAxis) {
inverseParentMatrix = objectToArmatureMatrix.clone();
} else {
inverseParentMatrix = Matrix4f.IDENTITY.clone();
}
restMatrix = armatureMatrix.clone();
inverseTotalTransformation = restMatrix.invert();
restMatrix = inverseParentMatrix.mult(restMatrix);
for (BoneContext child : this.children) {
child.computeRestMatrix(objectToArmatureMatrix);
}
}
/**
* This method computes the pose transform for the bone.
*/
@SuppressWarnings("unchecked")
private void computePoseTransform() {
DynamicArray<Number> loc = (DynamicArray<Number>) poseChannel.getFieldValue("loc");
DynamicArray<Number> size = (DynamicArray<Number>) poseChannel.getFieldValue("size");
DynamicArray<Number> quat = (DynamicArray<Number>) poseChannel.getFieldValue("quat");
if (fixUpAxis) {
poseTransform.setTranslation(loc.get(0).floatValue(), -loc.get(2).floatValue(), loc.get(1).floatValue());
poseTransform.setRotation(new Quaternion(quat.get(1).floatValue(), quat.get(3).floatValue(), -quat.get(2).floatValue(), quat.get(0).floatValue()));
poseTransform.setScale(size.get(0).floatValue(), size.get(2).floatValue(), size.get(1).floatValue());
} else {
poseTransform.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue());
poseTransform.setRotation(new Quaternion(quat.get(0).floatValue(), quat.get(1).floatValue(), quat.get(2).floatValue(), quat.get(3).floatValue()));
poseTransform.setScale(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
}
Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
localTransform.setScale(bone.getLocalScale());
localTransform.getTranslation().addLocal(poseTransform.getTranslation());
localTransform.getRotation().multLocal(poseTransform.getRotation());
localTransform.getScale().multLocal(poseTransform.getScale());
poseTransform.set(localTransform);
}
/**
* This method builds the bone. It recursively builds the bone's children.
*
* @param bones
* a list of bones where the newly created bone will be added
* @param boneOMAs
* the map between bone and its old memory address
* @param blenderContext
* the blender context
* @return newly created bone
*/
public Bone buildBone(List<Bone> bones, Map<Bone, Long> boneOMAs, BlenderContext blenderContext) {
Long boneOMA = boneStructure.getOldMemoryAddress();
bone = new Bone(boneName);
bones.add(bone);
boneOMAs.put(bone, boneOMA);
blenderContext.addLoadedFeatures(boneOMA, boneName, boneStructure, bone);
Matrix4f pose = this.restMatrix.clone();
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Vector3f poseLocation = pose.toTranslationVector();
Quaternion rotation = pose.toRotationQuat();
Vector3f scale = objectHelper.getScale(pose);
bone.setBindTransforms(poseLocation, rotation, scale);
for (BoneContext child : children) {
bone.addChild(child.buildBone(bones, boneOMAs, blenderContext));
}
this.computePoseTransform();
return bone;
}
/**
* @return bone's pose transformation
*/
public Transform getPoseTransform() {
return poseTransform;
}
/**
* @return built bone (available after calling 'buildBone' method)
*/
public Bone getBone() {
return bone;
}
}

@ -9,209 +9,228 @@ import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.curves.BezierCurve; import com.jme3.scene.plugins.blender.curves.BezierCurve;
/** /**
* This class is used to calculate bezier curves value for the given frames. The Ipo (interpolation object) consists * This class is used to calculate bezier curves value for the given frames. The
* of several b-spline curves (connected 3rd degree bezier curves) of a different type. * Ipo (interpolation object) consists of several b-spline curves (connected 3rd
* degree bezier curves) of a different type.
*
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class Ipo { public class Ipo {
public static final int AC_LOC_X = 1; public static final int AC_LOC_X = 1;
public static final int AC_LOC_Y = 2; public static final int AC_LOC_Y = 2;
public static final int AC_LOC_Z = 3; public static final int AC_LOC_Z = 3;
public static final int OB_ROT_X = 7; public static final int OB_ROT_X = 7;
public static final int OB_ROT_Y = 8; public static final int OB_ROT_Y = 8;
public static final int OB_ROT_Z = 9; public static final int OB_ROT_Z = 9;
public static final int AC_SIZE_X = 13; public static final int AC_SIZE_X = 13;
public static final int AC_SIZE_Y = 14; public static final int AC_SIZE_Y = 14;
public static final int AC_SIZE_Z = 15; public static final int AC_SIZE_Z = 15;
public static final int AC_QUAT_W = 25; public static final int AC_QUAT_W = 25;
public static final int AC_QUAT_X = 26; public static final int AC_QUAT_X = 26;
public static final int AC_QUAT_Y = 27; public static final int AC_QUAT_Y = 27;
public static final int AC_QUAT_Z = 28; public static final int AC_QUAT_Z = 28;
/** A list of bezier curves for this interpolation object. */
private BezierCurve[] bezierCurves;
/** Each ipo contains one bone track. */
private Track calculatedTrack;
/** This variable indicates if the Y asxis is the UP axis or not. */
protected boolean fixUpAxis;
/**
* Constructor. Stores the bezier curves.
* @param bezierCurves
* a table of bezier curves
*/
public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis) {
this.bezierCurves = bezierCurves;
this.fixUpAxis = fixUpAxis;
}
/** /** A list of bezier curves for this interpolation object. */
* This method calculates the ipo value for the first curve. private BezierCurve[] bezierCurves;
* @param frame /** Each ipo contains one bone track. */
* the frame for which the value is calculated private Track calculatedTrack;
* @return calculated ipo value /** This variable indicates if the Y asxis is the UP axis or not. */
*/ protected boolean fixUpAxis;
public float calculateValue(int frame) {
return this.calculateValue(frame, 0);
}
/** /**
* This method calculates the ipo value for the curve of the specified index. Make sure you do not exceed the * Constructor. Stores the bezier curves.
* curves amount. Alway chech the amount of curves before calling this method. *
* @param frame * @param bezierCurves
* the frame for which the value is calculated * a table of bezier curves
* @param curveIndex */
* the index of the curve public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis) {
* @return calculated ipo value this.bezierCurves = bezierCurves;
*/ this.fixUpAxis = fixUpAxis;
public float calculateValue(int frame, int curveIndex) { }
return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);
}
/** /**
* This method returns the curves amount. * This method calculates the ipo value for the first curve.
* @return the curves amount *
*/ * @param frame
public int getCurvesAmount() { * the frame for which the value is calculated
return bezierCurves.length; * @return calculated ipo value
} */
public float calculateValue(int frame) {
return this.calculateValue(frame, 0);
}
/** /**
* This method returns the frame where last bezier triple center point of the specified bezier curve is located. * This method calculates the ipo value for the curve of the specified
* @return the frame number of the last defined bezier triple point for the specified ipo * index. Make sure you do not exceed the curves amount. Alway chech the
*/ * amount of curves before calling this method.
public int getLastFrame() { *
int result = 1; * @param frame
for (int i = 0; i < bezierCurves.length; ++i) { * the frame for which the value is calculated
int tempResult = bezierCurves[i].getLastFrame(); * @param curveIndex
if (tempResult > result) { * the index of the curve
result = tempResult; * @return calculated ipo value
} */
} public float calculateValue(int frame, int curveIndex) {
return result; return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);
} }
/** /**
* This method calculates the value of the curves as a bone track between the specified frames. * This method returns the curves amount.
* @param targetIndex *
* the index of the target for which the method calculates the tracks * @return the curves amount
* IMPORTANT! Aet to -1 (or any negative number) if you want to load spatial animation. */
* @param startFrame public int getCurvesAmount() {
* the firs frame of tracks (inclusive) return bezierCurves.length;
* @param stopFrame }
* the last frame of the tracks (inclusive)
* @param fps
* frame rate (frames per second)
* @return bone track for the specified bone
*/
public Track calculateTrack(int targetIndex, int startFrame, int stopFrame, int fps) {
if(calculatedTrack == null) {
//preparing data for track
int framesAmount = stopFrame - startFrame;
float start = (startFrame - 1.0f) / fps;
float timeBetweenFrames = 1.0f / fps;
float[] times = new float[framesAmount + 1]; /**
Vector3f[] translations = new Vector3f[framesAmount + 1]; * This method returns the frame where last bezier triple center point of
float[] translation = new float[3]; * the specified bezier curve is located.
Quaternion[] rotations = new Quaternion[framesAmount + 1]; *
float[] quaternionRotation = new float[4]; * @return the frame number of the last defined bezier triple point for the
float[] objectRotation = new float[3]; * specified ipo
boolean bSpatialTrack = targetIndex < 0; */
Vector3f[] scales = new Vector3f[framesAmount + 1]; public int getLastFrame() {
float[] scale = new float[] {1.0f, 1.0f, 1.0f}; int result = 1;
float degreeToRadiansFactor = FastMath.DEG_TO_RAD * 10;//the values in blender are divided by 10, so we need to mult it here for (int i = 0; i < bezierCurves.length; ++i) {
int tempResult = bezierCurves[i].getLastFrame();
//calculating track data if (tempResult > result) {
for (int frame = startFrame; frame <= stopFrame; ++frame) { result = tempResult;
int index = frame - startFrame; }
times[index] = start + (frame - 1) * timeBetweenFrames; }
for (int j = 0; j < bezierCurves.length; ++j) { return result;
double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE); }
switch (bezierCurves[j].getType()) {
//LOCATION /**
case AC_LOC_X: * This method calculates the value of the curves as a bone track between
translation[0] = (float) value; * the specified frames.
break; *
case AC_LOC_Y: * @param targetIndex
if(fixUpAxis) { * the index of the target for which the method calculates the
translation[2] = (float) -value; * tracks IMPORTANT! Aet to -1 (or any negative number) if you
} else { * want to load spatial animation.
translation[1] = (float) value; * @param startFrame
} * the firs frame of tracks (inclusive)
break; * @param stopFrame
case AC_LOC_Z: * the last frame of the tracks (inclusive)
translation[fixUpAxis ? 1 : 2] = (float) value; * @param fps
break; * frame rate (frames per second)
* @param spatialTrack
//ROTATION (used with object animation) * this flag indicates if the track belongs to a spatial or to a
//the value here is in degrees divided by 10 (so in example: 9 = PI/2) * bone; the diference is important because it appears that bones
case OB_ROT_X: * in blender have the same type of coordinate system (Y as UP)
objectRotation[0] = (float) value * degreeToRadiansFactor; * as jme while other features have different one (Z is UP)
break; * @return bone track for the specified bone
case OB_ROT_Y: */
if(fixUpAxis) { public Track calculateTrack(int targetIndex, int startFrame, int stopFrame, int fps, boolean spatialTrack) {
objectRotation[2] = (float) -value * degreeToRadiansFactor; if (calculatedTrack == null) {
} else { // preparing data for track
objectRotation[1] = (float) value * degreeToRadiansFactor; int framesAmount = stopFrame - startFrame;
} float start = (startFrame - 1.0f) / fps;
break; float timeBetweenFrames = 1.0f / fps;
case OB_ROT_Z:
objectRotation[fixUpAxis ? 1 : 2] = (float) value * degreeToRadiansFactor; float[] times = new float[framesAmount + 1];
break; Vector3f[] translations = new Vector3f[framesAmount + 1];
float[] translation = new float[3];
//SIZE Quaternion[] rotations = new Quaternion[framesAmount + 1];
case AC_SIZE_X: float[] quaternionRotation = new float[4];
scale[0] = (float) value; float[] objectRotation = new float[3];
break; Vector3f[] scales = new Vector3f[framesAmount + 1];
case AC_SIZE_Y: float[] scale = new float[] { 1.0f, 1.0f, 1.0f };
if(fixUpAxis) { float degreeToRadiansFactor = FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here
scale[2] = (float) value;
} else { // calculating track data
scale[1] = (float) value; for (int frame = startFrame; frame <= stopFrame; ++frame) {
} int index = frame - startFrame;
break; times[index] = start + (frame - 1) * timeBetweenFrames;
case AC_SIZE_Z: for (int j = 0; j < bezierCurves.length; ++j) {
scale[fixUpAxis ? 1 : 2] = (float) value; double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
break; switch (bezierCurves[j].getType()) {
// LOCATION
//QUATERNION ROTATION (used with bone animation) case AC_LOC_X:
case AC_QUAT_W: translation[0] = (float) value;
quaternionRotation[3] = (float) value; break;
break; case AC_LOC_Y:
case AC_QUAT_X: if (fixUpAxis && spatialTrack) {
quaternionRotation[0] = (float) value; translation[2] = (float) -value;
break; } else {
case AC_QUAT_Y: translation[1] = (float) value;
if(fixUpAxis) { }
quaternionRotation[2] = -(float) value; break;
} else { case AC_LOC_Z:
quaternionRotation[1] = (float) value; translation[fixUpAxis && spatialTrack ? 1 : 2] = (float) value;
} break;
break;
case AC_QUAT_Z: // ROTATION (used with object animation)
if(fixUpAxis) { // the value here is in degrees divided by 10 (so in
quaternionRotation[1] = (float) value; // example: 9 = PI/2)
} else { case OB_ROT_X:
quaternionRotation[2] = (float) value; objectRotation[0] = (float) value * degreeToRadiansFactor;
} break;
break; case OB_ROT_Y:
default: if (fixUpAxis) {
throw new IllegalStateException("Unknown ipo curve type: " + bezierCurves[j].getType()); objectRotation[2] = (float) -value * degreeToRadiansFactor;
} } else {
} objectRotation[1] = (float) value * degreeToRadiansFactor;
translations[index] = new Vector3f(translation[0], translation[1], translation[2]); }
rotations[index] = bSpatialTrack ? new Quaternion().fromAngles(objectRotation) break;
: new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]); case OB_ROT_Z:
scales[index] = new Vector3f(scale[0], scale[1], scale[2]); objectRotation[fixUpAxis ? 1 : 2] = (float) value * degreeToRadiansFactor;
} break;
if(bSpatialTrack) {
calculatedTrack = new SpatialTrack(times, translations, rotations, scales); // SIZE
} else { case AC_SIZE_X:
calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales); scale[0] = (float) value;
} break;
} case AC_SIZE_Y:
return calculatedTrack; if (fixUpAxis && spatialTrack) {
} scale[2] = (float) value;
} else {
scale[1] = (float) value;
}
break;
case AC_SIZE_Z:
scale[fixUpAxis && spatialTrack ? 1 : 2] = (float) value;
break;
// QUATERNION ROTATION (used with bone animation), dunno
// why but here we shouldn't check the
// spatialTrack flag value
case AC_QUAT_W:
quaternionRotation[3] = (float) value;
break;
case AC_QUAT_X:
quaternionRotation[0] = (float) value;
break;
case AC_QUAT_Y:
if (fixUpAxis) {
quaternionRotation[2] = -(float) value;
} else {
quaternionRotation[1] = (float) value;
}
break;
case AC_QUAT_Z:
if (fixUpAxis) {
quaternionRotation[1] = (float) value;
} else {
quaternionRotation[2] = (float) value;
}
break;
default:
throw new IllegalStateException("Unknown ipo curve type: " + bezierCurves[j].getType());
}
}
translations[index] = new Vector3f(translation[0], translation[1], translation[2]);
rotations[index] = spatialTrack ? new Quaternion().fromAngles(objectRotation) : new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);
scales[index] = new Vector3f(scale[0], scale[1], scale[2]);
}
if (spatialTrack) {
calculatedTrack = new SpatialTrack(times, translations, rotations, scales);
} else {
calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales);
}
}
return calculatedTrack;
}
} }

@ -7,114 +7,193 @@ import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.curves.BezierCurve; import com.jme3.scene.plugins.blender.curves.BezierCurve;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; import com.jme3.scene.plugins.blender.exceptions.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.Pointer;
import com.jme3.scene.plugins.blender.file.Structure; 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 * This class helps to compute values from interpolation curves for features
* curves are 3rd degree bezier curves. * like animation or constraint influence. The curves are 3rd degree bezier
* curves.
*
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class IpoHelper extends AbstractBlenderHelper { public class IpoHelper extends AbstractBlenderHelper {
/** /**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in * This constructor parses the given blender version and stores the result.
* different blender versions. * Some functionalities may differ in different blender versions.
* @param blenderVersion *
* the version read from the blend file * @param blenderVersion
* @param fixUpAxis * the version read from the blend file
* a variable that indicates if the Y asxis is the UP axis or not * @param fixUpAxis
*/ * a variable that indicates if the Y asxis is the UP axis or not
public IpoHelper(String blenderVersion, boolean fixUpAxis) { */
super(blenderVersion, fixUpAxis); public IpoHelper(String blenderVersion, boolean fixUpAxis) {
} super(blenderVersion, fixUpAxis);
}
/**
* This method creates an ipo object used for interpolation calculations. /**
* @param ipoStructure * This method creates an ipo object used for interpolation calculations.
* the structure with ipo definition *
* @param blenderContext * @param ipoStructure
* the blender context * the structure with ipo definition
* @return the ipo object * @param blenderContext
* @throws BlenderFileException * the blender context
* this exception is thrown when the blender file is somehow corrupted * @return the ipo object
*/ * @throws BlenderFileException
public Ipo createIpo(Structure ipoStructure, BlenderContext blenderContext) throws BlenderFileException { * this exception is thrown when the blender file is somehow
Structure curvebase = (Structure) ipoStructure.getFieldValue("curve"); * corrupted
*/
//preparing bezier curves public Ipo fromIpoStructure(Structure ipoStructure, BlenderContext blenderContext) throws BlenderFileException {
Ipo result = null; Structure curvebase = (Structure) ipoStructure.getFieldValue("curve");
List<Structure> curves = curvebase.evaluateListBase(blenderContext);//IpoCurve
if (curves.size() > 0) { // preparing bezier curves
BezierCurve[] bezierCurves = new BezierCurve[curves.size()]; Ipo result = null;
int frame = 0; List<Structure> curves = curvebase.evaluateListBase(blenderContext);// IpoCurve
for (Structure curve : curves) { if (curves.size() > 0) {
Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt"); BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream()); int frame = 0;
int type = ((Number) curve.getFieldValue("adrcode")).intValue(); for (Structure curve : curves) {
bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2); Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
} List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
curves.clear(); int type = ((Number) curve.getFieldValue("adrcode")).intValue();
result = new Ipo(bezierCurves, fixUpAxis); bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
blenderContext.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result); }
} curves.clear();
return result; result = new Ipo(bezierCurves, fixUpAxis);
} 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 * This method creates an ipo object used for interpolation calculations. It
* @return constant ipo * should be called for blender version 2.50 and higher.
*/ *
public Ipo createIpo(float constValue) { * @param actionStructure
return new ConstIpo(constValue); * the structure with action definition
} * @param blenderContext
* the blender context
@Override * @return the ipo object
public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) { * @throws BlenderFileException
return true; * this exception is thrown when the blender file is somehow
} * corrupted
*/
/** public Ipo fromAction(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
* Ipo constant curve. This is a curve with only one value and no specified type. This type of ipo cannot be used to Ipo result = null;
* calculate tracks. It should only be used to calculate single value for a given frame. List<Structure> curves = ((Structure) actionStructure.getFieldValue("curves")).evaluateListBase(blenderContext);// FCurve
* @author Marcin Roguski if (curves.size() > 0) {
*/ BezierCurve[] bezierCurves = new BezierCurve[curves.size()];
private class ConstIpo extends Ipo { int frame = 0;
for (Structure curve : curves) {
/** The constant value of this ipo. */ Pointer pBezTriple = (Pointer) curve.getFieldValue("bezt");
private float constValue; List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
int type = this.getCurveType(curve, blenderContext);
/** bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
* Constructor. Stores the constant value of this ipo. }
* @param constValue curves.clear();
* the constant value of this ipo result = new Ipo(bezierCurves, fixUpAxis);
*/ }
public ConstIpo(float constValue) { return result;
super(null, false); }
this.constValue = constValue;
} /**
* This method returns the type of the ipo curve.
@Override *
public float calculateValue(int frame) { * @param structure
return constValue; * the structure must contain the 'rna_path' field and
} * 'array_index' field (the type is not important here)
* @param blenderContext
@Override * the blender context
public float calculateValue(int frame, int curveIndex) { * @return the type of the curve
return constValue; */
} public int getCurveType(Structure structure, BlenderContext blenderContext) {
// reading rna path first
@Override BlenderInputStream bis = blenderContext.getInputStream();
public int getCurvesAmount() { int currentPosition = bis.getPosition();
return 0; Pointer pRnaPath = (Pointer) structure.getFieldValue("rna_path");
} FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pRnaPath.getOldMemoryAddress());
bis.setPosition(dataFileBlock.getBlockPosition());
@Override String rnaPath = bis.readString();
public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps) { bis.setPosition(currentPosition);
throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!"); 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")) {
return Ipo.OB_ROT_X + arrayIndex;
}
throw new IllegalStateException("Unknown curve rna path: " + rnaPath);
}
/**
* 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);
}
@Override
public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
return true;
}
/**
* 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
*/
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);
this.constValue = constValue;
}
@Override
public float calculateValue(int frame) {
return constValue;
}
@Override
public float calculateValue(int frame, int curveIndex) {
return constValue;
}
@Override
public int getCurvesAmount() {
return 0;
}
@Override
public BoneTrack calculateTrack(int boneIndex, int startFrame, int stopFrame, int fps, boolean boneTrack) {
throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");
}
}
} }

@ -20,9 +20,6 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
public abstract class Constraint { public abstract class Constraint {
public static final int BAKE_DYNAMIC = 0x01;
public static final int BAKE_STATIC = 0x02;
/** The name of this constraint. */ /** The name of this constraint. */
protected final String name; protected final String name;
/** The constraint's owner. */ /** The constraint's owner. */
@ -79,32 +76,19 @@ public abstract class Constraint {
/** /**
* This method bakes the required sontraints into its owner. * This method bakes the required sontraints into its owner.
* @param bakeFlag the bake type flag support the following values:
* <li> BAKE_DYNAMIC - bake animation's constraints
* <li> BAKE_STATIC - bake static constraints
*/ */
public void bake(int bakeFlag) { public void bake() {
this.owner.update(); this.owner.update();
if(this.target != null) { if(this.target != null) {
this.target.update(); this.target.update();
} }
if((bakeFlag & BAKE_DYNAMIC) != 0) { this.bakeConstraint();
this.bakeDynamic();
}
if((bakeFlag & BAKE_STATIC) != 0) {
this.bakeStatic();
}
} }
/** /**
* Bake the animation's constraints into its owner. * Bake the animation's constraints into its owner.
*/ */
protected abstract void bakeDynamic(); protected abstract void bakeConstraint();
/**
* Bake the static constraints into its owner.
*/
protected abstract void bakeStatic();
/** /**
* This method returns the bone traces for the bone that is affected by the given constraint. * This method returns the bone traces for the bone that is affected by the given constraint.

@ -35,13 +35,7 @@ import java.util.logging.Logger;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
// TODO: implement 'Action' constraint
LOGGER.log(Level.WARNING, "'Action' constraint NOT implemented!");
}
@Override
protected void bakeStatic() {
// TODO: implement 'Action' constraint // TODO: implement 'Action' constraint
LOGGER.log(Level.WARNING, "'Action' constraint NOT implemented!"); LOGGER.log(Level.WARNING, "'Action' constraint NOT implemented!");
} }

@ -35,13 +35,7 @@ import java.util.logging.Logger;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
// TODO: implement ChildOf constraint
LOGGER.log(Level.WARNING, "ChildOf constraint NOT implemented!");
}
@Override
protected void bakeStatic() {
// TODO: implement ChildOf constraint // TODO: implement ChildOf constraint
LOGGER.log(Level.WARNING, "ChildOf constraint NOT implemented!"); LOGGER.log(Level.WARNING, "ChildOf constraint NOT implemented!");
} }

@ -36,13 +36,7 @@ import java.util.logging.Logger;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
//TODO: implement when curves are implemented
LOGGER.log(Level.INFO, "'Clamp to' not yet implemented! Curves not yet implemented!", name);
}
@Override
protected void bakeStatic() {
//TODO: implement when curves are implemented //TODO: implement when curves are implemented
LOGGER.log(Level.INFO, "'Clamp to' not yet implemented! Curves not yet implemented!", name); LOGGER.log(Level.INFO, "'Clamp to' not yet implemented! Curves not yet implemented!", name);
} }

@ -36,13 +36,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
// TODO Auto-generated method stub
LOGGER.log(Level.WARNING, "'Damp Track' constraint NOT implemented!");
}
@Override
protected void bakeStatic() {
// TODO Auto-generated method stub // TODO Auto-generated method stub
LOGGER.log(Level.WARNING, "'Damp Track' constraint NOT implemented!"); LOGGER.log(Level.WARNING, "'Damp Track' constraint NOT implemented!");
} }

@ -1,7 +1,6 @@
package com.jme3.scene.plugins.blender.constraints; package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation; import com.jme3.animation.Animation;
import com.jme3.animation.Bone;
import com.jme3.math.Matrix4f; import com.jme3.math.Matrix4f;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
@ -47,10 +46,10 @@ import com.jme3.scene.plugins.ogre.AnimData;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
Object owner = this.owner.getObject();
AnimData animData = blenderContext.getAnimData(this.owner.getOma()); AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if(animData != null) { if(animData != null) {
Object owner = this.owner.getObject();
if(owner instanceof Spatial) { if(owner instanceof Spatial) {
Vector3f targetLocation = ((Spatial) owner).getWorldTranslation(); Vector3f targetLocation = ((Spatial) owner).getWorldTranslation();
for(Animation animation : animData.anims) { for(Animation animation : animData.anims) {
@ -66,22 +65,17 @@ import com.jme3.scene.plugins.ogre.AnimData;
} }
} }
} }
}
// apply static constraint only to spatials
@Override
protected void bakeStatic() {
Matrix4f targetWorldMatrix = target.getWorldTransformMatrix();
Vector3f targetLocation = targetWorldMatrix.toTranslationVector();
Matrix4f m = owner.getParentWorldTransformMatrix();
m.invertLocal();
Matrix4f ownerWorldMatrix = owner.getWorldTransformMatrix();
Vector3f ownerLocation = ownerWorldMatrix.toTranslationVector();
this.distLimit(ownerLocation, targetLocation, ipo.calculateValue(0));
Object owner = this.owner.getObject();
if(owner instanceof Spatial) { if(owner instanceof Spatial) {
Matrix4f targetWorldMatrix = target.getWorldTransformMatrix();
Vector3f targetLocation = targetWorldMatrix.toTranslationVector();
Matrix4f m = this.owner.getParentWorldTransformMatrix();
m.invertLocal();
Matrix4f ownerWorldMatrix = this.owner.getWorldTransformMatrix();
Vector3f ownerLocation = ownerWorldMatrix.toTranslationVector();
this.distLimit(ownerLocation, targetLocation, ipo.calculateValue(0));
((Spatial) owner).setLocalTranslation(m.mult(ownerLocation)); ((Spatial) owner).setLocalTranslation(m.mult(ownerLocation));
} else {
((Bone) owner).setBindTransforms(m.mult(ownerLocation), ((Bone) owner).getLocalRotation(), ((Bone) owner).getLocalScale());
} }
} }

@ -35,13 +35,7 @@ import java.util.logging.Logger;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
//TODO: implement when curves are implemented
LOGGER.log(Level.INFO, "'Follow path' not implemented! Curves not yet implemented!");
}
@Override
protected void bakeStatic() {
//TODO: implement when curves are implemented //TODO: implement when curves are implemented
LOGGER.log(Level.INFO, "'Follow path' not implemented! Curves not yet implemented!"); LOGGER.log(Level.INFO, "'Follow path' not implemented! Curves not yet implemented!");
} }

@ -93,7 +93,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo"); Pointer pIpo = (Pointer) constraintChannel.getFieldValue("ipo");
if (pIpo.isNotNull()) { if (pIpo.isNotNull()) {
String constraintName = constraintChannel.getFieldValue("name").toString(); String constraintName = constraintChannel.getFieldValue("name").toString();
Ipo ipo = ipoHelper.createIpo(pIpo.fetchData(blenderContext.getInputStream()).get(0), blenderContext); Ipo ipo = ipoHelper.fromIpoStructure(pIpo.fetchData(blenderContext.getInputStream()).get(0), blenderContext);
ipos.put(constraintName, ipo); ipos.put(constraintName, ipo);
} }
} }
@ -120,7 +120,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
Ipo ipo = ipoMap==null ? null : ipoMap.get(constraintName); Ipo ipo = ipoMap==null ? null : ipoMap.get(constraintName);
if (ipo == null) { if (ipo == null) {
float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue(); float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
ipo = ipoHelper.createIpo(enforce); ipo = ipoHelper.fromValue(enforce);
} }
constraintsList.add(this.createConstraint(constraint, boneOMA, ipo, blenderContext)); constraintsList.add(this.createConstraint(constraint, boneOMA, ipo, blenderContext));
} }
@ -140,7 +140,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
Ipo ipo = objectConstraintsIpos!=null ? objectConstraintsIpos.get(constraintName) : null; Ipo ipo = objectConstraintsIpos!=null ? objectConstraintsIpos.get(constraintName) : null;
if (ipo == null) { if (ipo == null) {
float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue(); float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
ipo = ipoHelper.createIpo(enforce); ipo = ipoHelper.fromValue(enforce);
} }
constraintsList.add(this.createConstraint(constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext)); constraintsList.add(this.createConstraint(constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext));
} }

@ -38,7 +38,7 @@ import java.util.logging.Logger;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
// try { // try {
// IK solver is only attached to bones // IK solver is only attached to bones
// Bone ownerBone = (Bone) blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE); // Bone ownerBone = (Bone) blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
@ -127,12 +127,6 @@ import java.util.logging.Logger;
// } // }
} }
@Override
protected void bakeStatic() {
// TODO Auto-generated method stub
}
/** /**
* This method returns bones used for rotation calculations. * This method returns bones used for rotation calculations.
* @param bone * @param bone

@ -3,6 +3,7 @@ package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation; import com.jme3.animation.Animation;
import com.jme3.math.Transform; import com.jme3.math.Transform;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
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.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -61,10 +62,10 @@ import com.jme3.scene.plugins.ogre.AnimData;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
AnimData animData = blenderContext.getAnimData(owner.getOma()); Object owner = this.owner.getObject();
AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if(animData != null) { if(animData != null) {
Object owner = this.owner.getObject();
Transform targetTransform = this.target.getTransform(); Transform targetTransform = this.target.getTransform();
for(Animation animation : animData.anims) { for(Animation animation : animData.anims) {
BlenderTrack blenderTrack = this.getTrack(owner, animData.skeleton, animation); BlenderTrack blenderTrack = this.getTrack(owner, animData.skeleton, animation);
@ -76,15 +77,14 @@ import com.jme3.scene.plugins.ogre.AnimData;
blenderTrack.setKeyframes(blenderTrack.getTimes(), translations, blenderTrack.getRotations(), blenderTrack.getScales()); blenderTrack.setKeyframes(blenderTrack.getTimes(), translations, blenderTrack.getRotations(), blenderTrack.getScales());
} }
} }
}
if(owner instanceof Spatial) {
@Override Transform targetTransform = this.target.getTransform();
protected void bakeStatic() { Transform ownerTransform = this.owner.getTransform();
Transform targetTransform = this.target.getTransform(); Vector3f ownerLocation = ownerTransform.getTranslation();
Transform ownerTransform = this.owner.getTransform(); this.locLike(ownerLocation, targetTransform.getTranslation(), ipo.calculateValue(0));
Vector3f ownerLocation = ownerTransform.getTranslation(); this.owner.applyTransform(ownerTransform);
this.locLike(ownerLocation, targetTransform.getTranslation(), ipo.calculateValue(0)); }
this.owner.applyTransform(ownerTransform);
} }
private void locLike(Vector3f ownerLocation, Vector3f targetLocation, float influence) { private void locLike(Vector3f ownerLocation, Vector3f targetLocation, float influence) {

@ -1,8 +1,11 @@
package com.jme3.scene.plugins.blender.constraints; package com.jme3.scene.plugins.blender.constraints;
import java.util.Arrays;
import com.jme3.animation.Animation; import com.jme3.animation.Animation;
import com.jme3.math.Transform; import com.jme3.math.Transform;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
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.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -73,7 +76,7 @@ import com.jme3.scene.plugins.ogre.AnimData;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
Object owner = this.owner.getObject(); Object owner = this.owner.getObject();
AnimData animData = blenderContext.getAnimData(this.owner.getOma()); AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if(animData != null) { if(animData != null) {
@ -82,19 +85,20 @@ import com.jme3.scene.plugins.ogre.AnimData;
Vector3f[] translations = track.getTranslations(); Vector3f[] translations = track.getTranslations();
int maxFrames = translations.length; int maxFrames = translations.length;
for (int frame = 0; frame < maxFrames; ++frame) { for (int frame = 0; frame < maxFrames; ++frame) {
System.out.print(translations[frame] + "\t\t");
this.locLimit(translations[frame], ipo.calculateValue(frame)); this.locLimit(translations[frame], ipo.calculateValue(frame));
System.out.println(translations[frame]);
} }
track.setKeyframes(track.getTimes(), translations, track.getRotations(), track.getScales()); track.setKeyframes(track.getTimes(), translations, track.getRotations(), track.getScales());
} }
} }
}
if(owner instanceof Spatial) {
@Override Transform ownerTransform = this.owner.getTransform();
protected void bakeStatic() { Vector3f ownerLocation = ownerTransform.getTranslation();
Transform ownerTransform = this.owner.getTransform(); this.locLimit(ownerLocation, ipo.calculateValue(0));
Vector3f ownerLocation = ownerTransform.getTranslation(); this.owner.applyTransform(ownerTransform);
this.locLimit(ownerLocation, ipo.calculateValue(0)); }
this.owner.applyTransform(ownerTransform);
} }
/** /**

@ -36,13 +36,7 @@ import java.util.logging.Logger;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
// TODO: implement 'Lock track' constraint
LOGGER.log(Level.WARNING, "'Lock track' constraint NOT implemented!");
}
@Override
protected void bakeStatic() {
// TODO: implement 'Lock track' constraint // TODO: implement 'Lock track' constraint
LOGGER.log(Level.WARNING, "'Lock track' constraint NOT implemented!"); LOGGER.log(Level.WARNING, "'Lock track' constraint NOT implemented!");
} }

@ -35,13 +35,7 @@ import java.util.logging.Logger;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
// TODO: implement 'Min max' constraint
LOGGER.log(Level.WARNING, "'Min max' constraint NOT implemented!");
}
@Override
protected void bakeStatic() {
// TODO: implement 'Min max' constraint // TODO: implement 'Min max' constraint
LOGGER.log(Level.WARNING, "'Min max' constraint NOT implemented!"); LOGGER.log(Level.WARNING, "'Min max' constraint NOT implemented!");
} }

@ -33,8 +33,5 @@ import com.jme3.scene.plugins.blender.file.Structure;
} }
@Override @Override
protected void bakeDynamic() {} protected void bakeConstraint() {}
@Override
protected void bakeStatic() {}
} }

@ -37,13 +37,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
// TODO Auto-generated method stub
LOGGER.log(Level.WARNING, "'Pivot' constraint NOT implemented!");
}
@Override
protected void bakeStatic() {
// TODO Auto-generated method stub // TODO Auto-generated method stub
LOGGER.log(Level.WARNING, "'Pivot' constraint NOT implemented!"); LOGGER.log(Level.WARNING, "'Pivot' constraint NOT implemented!");
} }

@ -35,13 +35,7 @@ import java.util.logging.Logger;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
// TODO: implement 'Python' constraint
LOGGER.log(Level.WARNING, "'Python' constraint NOT implemented!");
}
@Override
protected void bakeStatic() {
// TODO: implement 'Python' constraint // TODO: implement 'Python' constraint
LOGGER.log(Level.WARNING, "'Python' constraint NOT implemented!"); LOGGER.log(Level.WARNING, "'Python' constraint NOT implemented!");
} }

@ -35,13 +35,7 @@ import java.util.logging.Logger;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
// TODO: implement 'Rigid body joint' constraint
LOGGER.log(Level.WARNING, "'Rigid body joint' constraint NOT implemented!");
}
@Override
protected void bakeStatic() {
// TODO: implement 'Rigid body joint' constraint // TODO: implement 'Rigid body joint' constraint
LOGGER.log(Level.WARNING, "'Rigid body joint' constraint NOT implemented!"); LOGGER.log(Level.WARNING, "'Rigid body joint' constraint NOT implemented!");
} }

@ -3,6 +3,7 @@ package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation; import com.jme3.animation.Animation;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.math.Transform; import com.jme3.math.Transform;
import com.jme3.scene.Spatial;
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.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -47,10 +48,10 @@ import com.jme3.scene.plugins.ogre.AnimData;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
Object owner = this.owner.getObject();
AnimData animData = blenderContext.getAnimData(this.owner.getOma()); AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if(animData != null) { if(animData != null) {
Object owner = this.owner.getObject();
Transform targetTransform = this.target.getTransform(); Transform targetTransform = this.target.getTransform();
Quaternion targetRotation = targetTransform.getRotation(); Quaternion targetRotation = targetTransform.getRotation();
for(Animation animation : animData.anims) { for(Animation animation : animData.anims) {
@ -66,15 +67,14 @@ import com.jme3.scene.plugins.ogre.AnimData;
track.setKeyframes(track.getTimes(), track.getTranslations(), rotations, track.getScales()); track.setKeyframes(track.getTimes(), track.getTranslations(), rotations, track.getScales());
} }
} }
}
if(owner instanceof Spatial) {
@Override Transform targetTransform = this.target.getTransform();
protected void bakeStatic() { Transform ownerTransform = this.owner.getTransform();
Transform targetTransform = this.target.getTransform(); Quaternion ownerRotation = ownerTransform.getRotation();
Transform ownerTransform = this.owner.getTransform(); this.rotLike(ownerRotation, ownerRotation.toAngles(null), targetTransform.getRotation().toAngles(null), ipo.calculateValue(0));
Quaternion ownerRotation = ownerTransform.getRotation(); this.owner.applyTransform(ownerTransform);
this.rotLike(ownerRotation, ownerRotation.toAngles(null), targetTransform.getRotation().toAngles(null), ipo.calculateValue(0)); }
this.owner.applyTransform(ownerTransform);
} }
private void rotLike(Quaternion ownerRotation, float[] ownerAngles, float[] targetAngles, float influence) { private void rotLike(Quaternion ownerRotation, float[] ownerAngles, float[] targetAngles, float influence) {

@ -1,9 +1,12 @@
package com.jme3.scene.plugins.blender.constraints; package com.jme3.scene.plugins.blender.constraints;
import java.util.Arrays;
import com.jme3.animation.Animation; import com.jme3.animation.Animation;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.math.Transform; import com.jme3.math.Transform;
import com.jme3.scene.Spatial;
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.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -68,10 +71,10 @@ import com.jme3.scene.plugins.ogre.AnimData;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
AnimData animData = blenderContext.getAnimData(owner.getOma()); Object owner = this.owner.getObject();
AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if(animData != null) { if(animData != null) {
Object owner = this.owner.getObject();
for(Animation animation : animData.anims) { for(Animation animation : animData.anims) {
BlenderTrack track = this.getTrack(owner, animData.skeleton, animation); BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
Quaternion[] rotations = track.getRotations(); Quaternion[] rotations = track.getRotations();
@ -79,21 +82,22 @@ import com.jme3.scene.plugins.ogre.AnimData;
int maxFrames = rotations.length; int maxFrames = rotations.length;
for (int frame = 0; frame < maxFrames; ++frame) { for (int frame = 0; frame < maxFrames; ++frame) {
rotations[frame].toAngles(angles); rotations[frame].toAngles(angles);
System.out.print(Arrays.toString(angles) + "\t\t");
this.rotLimit(angles, ipo.calculateValue(frame)); this.rotLimit(angles, ipo.calculateValue(frame));
System.out.println(Arrays.toString(angles));
rotations[frame].fromAngles(angles); rotations[frame].fromAngles(angles);
} }
track.setKeyframes(track.getTimes(), track.getTranslations(), rotations, track.getScales()); track.setKeyframes(track.getTimes(), track.getTranslations(), rotations, track.getScales());
} }
} }
}
if(owner instanceof Spatial) {
@Override Transform ownerTransform = this.owner.getTransform();
protected void bakeStatic() { float[] angles = ownerTransform.getRotation().toAngles(null);
Transform ownerTransform = this.owner.getTransform(); this.rotLimit(angles, ipo.calculateValue(0));
float[] angles = ownerTransform.getRotation().toAngles(null); ownerTransform.getRotation().fromAngles(angles);
this.rotLimit(angles, ipo.calculateValue(0)); this.owner.applyTransform(ownerTransform);
ownerTransform.getRotation().fromAngles(angles); }
this.owner.applyTransform(ownerTransform);
} }
/** /**
@ -114,7 +118,7 @@ import com.jme3.scene.plugins.ogre.AnimData;
} }
angles[0] -= difference; angles[0] -= difference;
} }
if ((flag & LIMIT_YROT) != 0) { if ((flag & LIMIT_ZROT) != 0) {
float difference = 0.0f; float difference = 0.0f;
if (angles[1] < limits[1][0]) { if (angles[1] < limits[1][0]) {
difference = (angles[1] - limits[1][0]) * influence; difference = (angles[1] - limits[1][0]) * influence;
@ -123,7 +127,7 @@ import com.jme3.scene.plugins.ogre.AnimData;
} }
angles[1] -= difference; angles[1] -= difference;
} }
if ((flag & LIMIT_ZROT) != 0) { /*if ((flag & LIMIT_ZROT) != 0) {
float difference = 0.0f; float difference = 0.0f;
if (angles[2] < limits[2][0]) { if (angles[2] < limits[2][0]) {
difference = (angles[2] - limits[2][0]) * influence; difference = (angles[2] - limits[2][0]) * influence;
@ -131,6 +135,6 @@ import com.jme3.scene.plugins.ogre.AnimData;
difference = (angles[2] - limits[2][1]) * influence; difference = (angles[2] - limits[2][1]) * influence;
} }
angles[2] -= difference; angles[2] -= difference;
} }*/
} }
} }

@ -45,7 +45,7 @@ import com.jme3.scene.plugins.ogre.AnimData;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
//loading mesh points (blender ensures that the target is a mesh-object) //loading mesh points (blender ensures that the target is a mesh-object)
List<Vector3f> pts = new ArrayList<Vector3f>(); List<Vector3f> pts = new ArrayList<Vector3f>();
Node target = (Node) this.target.getObject(); Node target = (Node) this.target.getObject();
@ -86,11 +86,7 @@ import com.jme3.scene.plugins.ogre.AnimData;
track.setKeyframes(track.getTimes(), translations, rotations, track.getScales()); track.setKeyframes(track.getTimes(), translations, rotations, track.getScales());
} }
} }
}
@Override
protected void bakeStatic() {
// TODO Auto-generated method stub
//TODO: static constraint for spatials
} }
} }

@ -3,6 +3,7 @@ package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation; import com.jme3.animation.Animation;
import com.jme3.math.Transform; import com.jme3.math.Transform;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
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.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -52,10 +53,10 @@ import com.jme3.scene.plugins.ogre.AnimData;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
Object owner = this.owner.getObject();
AnimData animData = blenderContext.getAnimData(this.owner.getOma()); AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if(animData != null) { if(animData != null) {
Object owner = this.owner.getObject();
Transform targetTransform = this.target.getTransform(); Transform targetTransform = this.target.getTransform();
Vector3f targetScale = targetTransform.getScale(); Vector3f targetScale = targetTransform.getScale();
for(Animation animation : animData.anims) { for(Animation animation : animData.anims) {
@ -68,14 +69,13 @@ import com.jme3.scene.plugins.ogre.AnimData;
track.setKeyframes(track.getTimes(), track.getTranslations(), track.getRotations(), scales); track.setKeyframes(track.getTimes(), track.getTranslations(), track.getRotations(), scales);
} }
} }
}
if(owner instanceof Spatial) {
@Override Transform targetTransform = this.target.getTransform();
protected void bakeStatic() { Transform ownerTransform = this.owner.getTransform();
Transform targetTransform = this.target.getTransform(); this.sizeLike(ownerTransform.getScale(), targetTransform.getScale(), ipo.calculateValue(0));
Transform ownerTransform = this.owner.getTransform(); this.owner.applyTransform(ownerTransform);
this.sizeLike(ownerTransform.getScale(), targetTransform.getScale(), ipo.calculateValue(0)); }
this.owner.applyTransform(ownerTransform);
} }
private void sizeLike(Vector3f ownerScale, Vector3f targetScale, float influence) { private void sizeLike(Vector3f ownerScale, Vector3f targetScale, float influence) {

@ -3,6 +3,7 @@ package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation; import com.jme3.animation.Animation;
import com.jme3.math.Transform; import com.jme3.math.Transform;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
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.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -73,10 +74,10 @@ import com.jme3.scene.plugins.ogre.AnimData;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
Object owner = this.owner.getObject();
AnimData animData = blenderContext.getAnimData(this.owner.getOma()); AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if(animData != null) { if(animData != null) {
Object owner = this.owner.getObject();
for(Animation animation : animData.anims) { for(Animation animation : animData.anims) {
BlenderTrack track = this.getTrack(owner, animData.skeleton, animation); BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
Vector3f[] scales = track.getScales(); Vector3f[] scales = track.getScales();
@ -87,13 +88,12 @@ import com.jme3.scene.plugins.ogre.AnimData;
track.setKeyframes(track.getTimes(), track.getTranslations(), track.getRotations(), scales); track.setKeyframes(track.getTimes(), track.getTranslations(), track.getRotations(), scales);
} }
} }
}
if(owner instanceof Spatial) {
@Override Transform ownerTransform = this.owner.getTransform();
protected void bakeStatic() { this.sizeLimit(ownerTransform.getScale(), ipo.calculateValue(0));
Transform ownerTransform = this.owner.getTransform(); this.owner.applyTransform(ownerTransform);
this.sizeLimit(ownerTransform.getScale(), ipo.calculateValue(0)); }
this.owner.applyTransform(ownerTransform);
} }
private void sizeLimit(Vector3f scale, float influence) { private void sizeLimit(Vector3f scale, float influence) {

@ -37,14 +37,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
// TODO Auto-generated method stub // TODO Auto-generated method stub
LOGGER.log(Level.WARNING, "'Splie IK' constraint NOT implemented!"); LOGGER.log(Level.WARNING, "'Splie IK' constraint NOT implemented!");
} }
@Override
protected void bakeStatic() {
// TODO Auto-generated method stub
LOGGER.log(Level.WARNING, "'Spline IK' constraint NOT implemented!");
}
} }

@ -35,13 +35,7 @@ import java.util.logging.Logger;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
// TODO: implement 'Stretch to' constraint
LOGGER.log(Level.WARNING, "'Stretch to' constraint NOT implemented!");
}
@Override
protected void bakeStatic() {
// TODO: implement 'Stretch to' constraint // TODO: implement 'Stretch to' constraint
LOGGER.log(Level.WARNING, "'Stretch to' constraint NOT implemented!"); LOGGER.log(Level.WARNING, "'Stretch to' constraint NOT implemented!");
} }

@ -35,13 +35,7 @@ import java.util.logging.Logger;
} }
@Override @Override
protected void bakeDynamic() { protected void bakeConstraint() {
// TODO: implement 'Transform' constraint
LOGGER.log(Level.WARNING, "'Transform' constraint NOT implemented!");
}
@Override
protected void bakeStatic() {
// TODO: implement 'Transform' constraint // TODO: implement 'Transform' constraint
LOGGER.log(Level.WARNING, "'Transform' constraint NOT implemented!"); LOGGER.log(Level.WARNING, "'Transform' constraint NOT implemented!");
} }

@ -1,6 +1,7 @@
package com.jme3.scene.plugins.blender.constraints; package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Bone; import com.jme3.animation.Bone;
import com.jme3.math.FastMath;
import com.jme3.math.Matrix4f; import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.math.Transform; import com.jme3.math.Transform;
@ -8,6 +9,7 @@ import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType; import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
import com.jme3.scene.plugins.blender.animations.BoneContext;
import com.jme3.scene.plugins.blender.constraints.Constraint.Space; import com.jme3.scene.plugins.blender.constraints.Constraint.Space;
import com.jme3.scene.plugins.blender.file.DynamicArray; import com.jme3.scene.plugins.blender.file.DynamicArray;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
@ -156,6 +158,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
} }
} }
// Bone // Bone
BoneContext boneContext = blenderContext.getBoneContext(oma);
switch (space) { switch (space) {
case CONSTRAINT_SPACE_LOCAL: case CONSTRAINT_SPACE_LOCAL:
Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation()); Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
@ -173,8 +176,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
worldTransform.setScale(bone.getWorldBindScale()); worldTransform.setScale(bone.getWorldBindScale());
return worldTransform; return worldTransform;
case CONSTRAINT_SPACE_POSE: case CONSTRAINT_SPACE_POSE:
// TODO Transform poseTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
return null; poseTransform.setScale(bone.getLocalScale());
return poseTransform;
case CONSTRAINT_SPACE_PARLOCAL: case CONSTRAINT_SPACE_PARLOCAL:
Transform parentLocalTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation()); Transform parentLocalTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
parentLocalTransform.setScale(bone.getLocalScale()); parentLocalTransform.setScale(bone.getLocalScale());
@ -228,11 +232,15 @@ import com.jme3.scene.plugins.blender.file.Structure;
break; break;
case CONSTRAINT_SPACE_WORLD: case CONSTRAINT_SPACE_WORLD:
Matrix4f m = this.getParentWorldTransformMatrix(); Matrix4f m = this.getParentWorldTransformMatrix();
m.invertLocal(); // m.invertLocal();
transform.setTranslation(m.mult(transform.getTranslation())); transform.setTranslation(m.mult(transform.getTranslation()));
transform.setRotation(m.mult(transform.getRotation(), null)); transform.setRotation(m.mult(transform.getRotation(), null));
transform.setScale(transform.getScale()); transform.setScale(transform.getScale());
bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale()); bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
// float x = FastMath.HALF_PI/2;
// float y = -FastMath.HALF_PI;
// float z = -FastMath.HALF_PI/2;
// bone.setBindTransforms(new Vector3f(0,0,0), new Quaternion().fromAngles(x, y, z), new Vector3f(1,1,1));
break; break;
case CONSTRAINT_SPACE_PARLOCAL: case CONSTRAINT_SPACE_PARLOCAL:
Vector3f parentLocalTranslation = bone.getLocalPosition().add(transform.getTranslation()); Vector3f parentLocalTranslation = bone.getLocalPosition().add(transform.getTranslation());
@ -240,7 +248,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
bone.setBindTransforms(parentLocalTranslation, parentLocalRotation, transform.getScale()); bone.setBindTransforms(parentLocalTranslation, parentLocalRotation, transform.getScale());
break; break;
case CONSTRAINT_SPACE_POSE: case CONSTRAINT_SPACE_POSE:
// TODO: bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
break; break;
default: default:
throw new IllegalStateException("Invalid space type for target object: " + space.toString()); throw new IllegalStateException("Invalid space type for target object: " + space.toString());

@ -42,8 +42,8 @@ import com.jme3.util.BufferUtils;
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
/* package */class ArmatureModifier extends Modifier { /* package */class ArmatureModifier extends Modifier {
private static final Logger LOGGER = Logger.getLogger(ArmatureModifier.class.getName()); private static final Logger LOGGER = Logger.getLogger(ArmatureModifier.class.getName());
private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4; private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4;
// @Marcin it was an Ogre limitation, but as long as we use a MaxNumWeight // @Marcin it was an Ogre limitation, but as long as we use a MaxNumWeight
// variable in mesh, // variable in mesh,
// i guess this limitation has no sense for the blender loader...so i guess // i guess this limitation has no sense for the blender loader...so i guess
@ -55,17 +55,18 @@ import com.jme3.util.BufferUtils;
// Rémy // Rémy
/** Loaded animation data. */ /** Loaded animation data. */
private AnimData animData; private AnimData animData;
/** Old memory address of the mesh that will have the skeleton applied. */ /** Old memory address of the mesh that will have the skeleton applied. */
private Long meshOMA; private Long meshOMA;
/** /**
* The maxiumum amount of bone groups applied to a single vertex (max = MAXIMUM_WEIGHTS_PER_VERTEX). * The maxiumum amount of bone groups applied to a single vertex (max =
* MAXIMUM_WEIGHTS_PER_VERTEX).
*/ */
private int boneGroups; private int boneGroups;
/** The weights of vertices. */ /** The weights of vertices. */
private VertexBuffer verticesWeights; private VertexBuffer verticesWeights;
/** The indexes of bones applied to vertices. */ /** The indexes of bones applied to vertices. */
private VertexBuffer verticesWeightsIndices; private VertexBuffer verticesWeightsIndices;
/** /**
* This constructor reads animation data from the object structore. The * This constructor reads animation data from the object structore. The
@ -83,9 +84,12 @@ import com.jme3.util.BufferUtils;
*/ */
public ArmatureModifier(Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException { public ArmatureModifier(Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0); Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert
// =
//if pDvert==null then there are not vertex groups and no need to load skeleton (untill bone envelopes are supported) // DeformVERTices
// if pDvert==null then there are not vertex groups and no need to load
// skeleton (untill bone envelopes are supported)
if (this.validate(modifierStructure, blenderContext) && pDvert.isNotNull()) { if (this.validate(modifierStructure, blenderContext) && pDvert.isNotNull()) {
Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object"); Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object");
if (pArmatureObject.isNotNull()) { if (pArmatureObject.isNotNull()) {
@ -109,15 +113,16 @@ import com.jme3.util.BufferUtils;
Matrix4f armatureObjectMatrix = objectHelper.getMatrix(armatureObject, "obmat", true); Matrix4f armatureObjectMatrix = objectHelper.getMatrix(armatureObject, "obmat", true);
Matrix4f inverseMeshObjectMatrix = objectHelper.getMatrix(objectStructure, "obmat", true).invertLocal(); Matrix4f inverseMeshObjectMatrix = objectHelper.getMatrix(objectStructure, "obmat", true).invertLocal();
Matrix4f objectToArmatureTransformation = armatureObjectMatrix.multLocal(inverseMeshObjectMatrix); Matrix4f objectToArmatureTransformation = armatureObjectMatrix.multLocal(inverseMeshObjectMatrix);
List<Structure> bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase(blenderContext); List<Structure> bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase(blenderContext);
List<Bone> bonesList = new ArrayList<Bone>(); List<Bone> bonesList = new ArrayList<Bone>();
for (int i = 0; i < bonebase.size(); ++i) { for (int i = 0; i < bonebase.size(); ++i) {
armatureHelper.buildBones(bonebase.get(i), null, bonesList, objectToArmatureTransformation, bonesPoseChannels, blenderContext); armatureHelper.buildBones(bonebase.get(i), null, bonesList, objectToArmatureTransformation, bonesPoseChannels, blenderContext);
} }
bonesList.add(0, new Bone("")); bonesList.add(0, new Bone(""));
Skeleton skeleton = new Skeleton(bonesList.toArray(new Bone[bonesList.size()])); Bone[] bones = bonesList.toArray(new Bone[bonesList.size()]);
Skeleton skeleton = new Skeleton(bones);
// read mesh indexes // read mesh indexes
this.meshOMA = meshStructure.getOldMemoryAddress(); this.meshOMA = meshStructure.getOldMemoryAddress();
this.readVerticesWeightsData(objectStructure, meshStructure, skeleton, blenderContext); this.readVerticesWeightsData(objectStructure, meshStructure, skeleton, blenderContext);
@ -132,25 +137,30 @@ import com.jme3.util.BufferUtils;
String actionName = actionStructure.getName(); String actionName = actionStructure.getName();
BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, skeleton, blenderContext); BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, skeleton, blenderContext);
// determining the animation time if(tracks != null && tracks.length > 0) {
float maximumTrackLength = 0; // determining the animation time
for (BoneTrack track : tracks) { float maximumTrackLength = 0;
float length = track.getLength(); for (BoneTrack track : tracks) {
if (length > maximumTrackLength) { float length = track.getLength();
maximumTrackLength = length; if (length > maximumTrackLength) {
maximumTrackLength = length;
}
} }
}
Animation boneAnimation = new Animation(actionName, maximumTrackLength); Animation boneAnimation = new Animation(actionName, maximumTrackLength);
boneAnimation.setTracks(tracks); boneAnimation.setTracks(tracks);
animations.add(boneAnimation); animations.add(boneAnimation);
}
} }
} }
animData = new AnimData(skeleton, animations); animData = new AnimData(skeleton, animations);
// store the animation data for each bone // store the animation data for each bone
for (Structure boneStructure : bonebase) { for (Bone bone : bones) {
blenderContext.setAnimData(boneStructure.getOldMemoryAddress(), animData); Long boneOma = armatureHelper.getBoneOMA(bone);
if (boneOma != null) {
blenderContext.setAnimData(boneOma, animData);
}
} }
} }
} }
@ -177,28 +187,14 @@ import com.jme3.util.BufferUtils;
} }
} }
// applying bone transforms before constraints are baked // applying constraints to Bones
ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class); ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
//TODO: should we apply static bone poses ??? (this breaks the animation)
// for (int i = 0; i < animData.skeleton.getBoneCount(); ++i) {
// Bone bone = animData.skeleton.getBone(i);
// Transform transform = armatureHelper.getBoneBindTransform(bone);
// Transform boneTransform = armatureHelper.getLocalTransform(bone);
// if(transform!=null && boneTransform!=null) {
// bone.setBindTransforms(boneTransform.getTranslation().addLocal(transform.getTranslation()),
// boneTransform.getRotation().multLocal(transform.getRotation()),
// boneTransform.getScale().multLocal(transform.getScale()));
// }
// }
// applying constraints to Bones (and only to bones, object constraints
// are applied in the ObjectHelper)
for (int i = 0; i < animData.skeleton.getBoneCount(); ++i) { for (int i = 0; i < animData.skeleton.getBoneCount(); ++i) {
Long boneOMA = armatureHelper.getBoneOMA(animData.skeleton.getBone(i)); Long boneOMA = armatureHelper.getBoneOMA(animData.skeleton.getBone(i));
List<Constraint> constraints = blenderContext.getConstraints(boneOMA); List<Constraint> constraints = blenderContext.getConstraints(boneOMA);
if (constraints != null && constraints.size() > 0) { if (constraints != null && constraints.size() > 0) {
for (Constraint constraint : constraints) { for (Constraint constraint : constraints) {
constraint.bake(Constraint.BAKE_DYNAMIC | Constraint.BAKE_STATIC); constraint.bake();
} }
} }
} }

@ -31,82 +31,165 @@
*/ */
package com.jme3.scene.plugins.blender.modifiers; 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.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; 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.animations.Ipo;
import com.jme3.scene.plugins.blender.animations.IpoHelper;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
/** /**
* A class that is used in modifiers calculations. * A class that is used in modifiers calculations.
*
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class ModifierHelper extends AbstractBlenderHelper { public class ModifierHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(ModifierHelper.class.getName()); private static final Logger LOGGER = Logger.getLogger(ModifierHelper.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 fixUpAxis
* a variable that indicates if the Y asxis is the UP axis or not
*/
public ModifierHelper(String blenderVersion, boolean fixUpAxis) {
super(blenderVersion, fixUpAxis);
}
/**
* This method reads the given object's modifiers.
*
* @param objectStructure
* the object structure
* @param blenderContext
* the blender context
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow
* corrupted
*/
public Collection<Modifier> readModifiers(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
Collection<Modifier> result = new ArrayList<Modifier>();
Structure modifiersListBase = (Structure) objectStructure.getFieldValue("modifiers");
List<Structure> modifiers = modifiersListBase.evaluateListBase(blenderContext);
for (Structure modifierStructure : modifiers) {
Modifier modifier = null;
if (Modifier.ARRAY_MODIFIER_DATA.equals(modifierStructure.getType())) {
modifier = new ArrayModifier(modifierStructure, blenderContext);
} else if (Modifier.MIRROR_MODIFIER_DATA.equals(modifierStructure.getType())) {
modifier = new MirrorModifier(modifierStructure, blenderContext);
} else if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifierStructure.getType())) {
modifier = new ArmatureModifier(objectStructure, modifierStructure, blenderContext);
} else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifierStructure.getType())) {
modifier = new ParticlesModifier(modifierStructure, blenderContext);
}
if (modifier != null) {
result.add(modifier);
blenderContext.addModifier(objectStructure.getOldMemoryAddress(), modifier);
} else {
LOGGER.log(Level.WARNING, "Unsupported modifier type: {0}", modifierStructure.getType());
}
}
/** // at the end read object's animation modifier (object animation is
* This constructor parses the given blender version and stores the result. Some functionalities may differ in // either described by action or by ipo of the object)
* different blender versions. Modifier modifier;
* @param blenderVersion if (blenderVersion <= 249) {
* the version read from the blend file modifier = this.readAnimationModifier249(objectStructure, blenderContext);
* @param fixUpAxis } else {
* a variable that indicates if the Y asxis is the UP axis or not modifier = this.readAnimationModifier250(objectStructure, blenderContext);
*/ }
public ModifierHelper(String blenderVersion, boolean fixUpAxis) { if (modifier != null) {
super(blenderVersion, fixUpAxis); result.add(modifier);
} }
return result;
}
/** @Override
* This method reads the given object's modifiers. public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
* @param objectStructure return true;
* the object structure }
* @param blenderContext
* the blender context
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow corrupted
*/
public Collection<Modifier> readModifiers(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
Collection<Modifier> result = new ArrayList<Modifier>();
Structure modifiersListBase = (Structure) objectStructure.getFieldValue("modifiers");
List<Structure> modifiers = modifiersListBase.evaluateListBase(blenderContext);
for (Structure modifierStructure : modifiers) {
Modifier modifier = null;
if (Modifier.ARRAY_MODIFIER_DATA.equals(modifierStructure.getType())) {
modifier = new ArrayModifier(modifierStructure, blenderContext);
} else if (Modifier.MIRROR_MODIFIER_DATA.equals(modifierStructure.getType())) {
modifier = new MirrorModifier(modifierStructure, blenderContext);
} else if (Modifier.ARMATURE_MODIFIER_DATA.equals(modifierStructure.getType())) {
modifier = new ArmatureModifier(objectStructure, modifierStructure, blenderContext);
} else if (Modifier.PARTICLE_MODIFIER_DATA.equals(modifierStructure.getType())) {
modifier = new ParticlesModifier(modifierStructure, blenderContext);
}
if(modifier != null) {
result.add(modifier);
blenderContext.addModifier(objectStructure.getOldMemoryAddress(), modifier);
} else {
LOGGER.log(Level.WARNING, "Unsupported modifier type: {0}", modifierStructure.getType());
}
}
//at the end read object's animation modifier /**
Pointer pIpo = (Pointer) objectStructure.getFieldValue("ipo"); * This method reads the object's animation modifier for blender version
if (pIpo.isNotNull()) { * 2.49 and lower.
Modifier modifier = new ObjectAnimationModifier(objectStructure, blenderContext); *
result.add(modifier); * @param objectStructure
blenderContext.addModifier(objectStructure.getOldMemoryAddress(), modifier); * the object's structure
} * @param blenderContext
return result; * 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 pAction = (Pointer) objectStructure.getFieldValue("action");
IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
if (pAction.isNotNull()) {
Structure action = pAction.fetchData(blenderContext.getInputStream()).get(0);
List<Structure> actionChannels = ((Structure) action.getFieldValue("chanbase")).evaluateListBase(blenderContext);
if (actionChannels.size() == 1) {// object's animtion action has
// only one channel
Pointer pChannelIpo = (Pointer) actionChannels.get(0).getFieldValue("ipo");
Structure ipoStructure = pChannelIpo.fetchData(blenderContext.getInputStream()).get(0);
Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
result = new ObjectAnimationModifier(ipo, action.getName(), objectStructure.getOldMemoryAddress(), blenderContext);
blenderContext.addModifier(objectStructure.getOldMemoryAddress(), result);
} else {
throw new IllegalStateException("Object's action cannot have more than one channel!");
}
} else {
Pointer pIpo = (Pointer) objectStructure.getFieldValue("ipo");
if (pIpo.isNotNull()) {
Structure ipoStructure = pIpo.fetchData(blenderContext.getInputStream()).get(0);
Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
result = new ObjectAnimationModifier(ipo, objectStructure.getName(), objectStructure.getOldMemoryAddress(), blenderContext);
blenderContext.addModifier(objectStructure.getOldMemoryAddress(), result);
}
}
return result;
}
@Override /**
public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) { * This method reads the object's animation modifier for blender version
return true; * 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(blenderContext.getInputStream()).get(0);
Pointer pAction = (Pointer) animData.getFieldValue("action");
if (pAction.isNotNull()) {
Structure actionStructure = pAction.fetchData(blenderContext.getInputStream()).get(0);
IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
Ipo ipo = ipoHelper.fromAction(actionStructure, blenderContext);
result = new ObjectAnimationModifier(ipo, actionStructure.getName(), objectStructure.getOldMemoryAddress(), blenderContext);
blenderContext.addModifier(objectStructure.getOldMemoryAddress(), result);
}
}
return result;
}
} }

@ -2,7 +2,6 @@ package com.jme3.scene.plugins.blender.modifiers;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -12,11 +11,7 @@ import com.jme3.animation.SpatialTrack;
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.Ipo;
import com.jme3.scene.plugins.blender.animations.IpoHelper;
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.ogre.AnimData; import com.jme3.scene.plugins.ogre.AnimData;
/** /**
@ -25,13 +20,11 @@ import com.jme3.scene.plugins.ogre.AnimData;
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
/* package */class ObjectAnimationModifier extends Modifier { /* package */class ObjectAnimationModifier extends Modifier {
private static final Logger LOGGER = Logger.getLogger(ObjectAnimationModifier.class.getName()); private static final Logger LOGGER = Logger.getLogger(ObjectAnimationModifier.class.getName());
/** Loaded animation data. */ /** Loaded animation data. */
private AnimData animData; private AnimData animData;
/** Old memory address of the object structure that will have the modifier applied. */
private Long objectOMA;
/** /**
* This constructor reads animation of the object itself (without bones) and * This constructor reads animation of the object itself (without bones) and
* stores it as an ArmatureModifierData modifier. The animation is returned * stores it as an ArmatureModifierData modifier. The animation is returned
@ -40,67 +33,41 @@ import com.jme3.scene.plugins.ogre.AnimData;
* animation should be working. The stored modifier is an anim data and * animation should be working. The stored modifier is an anim data and
* additional data is given object's OMA. * additional data is given object's OMA.
* *
* @param objectStructure * @param ipo
* the structure of the object * the object's interpolation curves
* @param objectAnimationName
* the name of object's animation
* @param objectOMA
* the OMA of the object
* @param blenderContext * @param blenderContext
* the blender context * the blender context
* @return animation modifier is returned, it should be separately applied
* when the object is loaded
* @throws BlenderFileException * @throws BlenderFileException
* this exception is thrown when the blender file is somehow * this exception is thrown when the blender file is somehow
* corrupted * corrupted
*/ */
public ObjectAnimationModifier(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException { public ObjectAnimationModifier(Ipo ipo, String objectAnimationName, Long objectOMA, BlenderContext blenderContext) throws BlenderFileException {
objectOMA = objectStructure.getOldMemoryAddress(); int fps = blenderContext.getBlenderKey().getFps();
Pointer pIpo = (Pointer) objectStructure.getFieldValue("ipo");
if (pIpo.isNotNull()) {
// check if there is an action name connected with this ipo
String objectAnimationName = null;
List<FileBlockHeader> actionBlocks = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
if(actionBlocks != null) {
for (FileBlockHeader actionBlock : actionBlocks) {
Structure action = actionBlock.getStructure(blenderContext);
List<Structure> actionChannels = ((Structure) action.getFieldValue("chanbase")).evaluateListBase(blenderContext);
if (actionChannels.size() == 1) {// object's animtion action has only one channel
Pointer pChannelIpo = (Pointer) actionChannels.get(0).getFieldValue("ipo");
if (pChannelIpo.equals(pIpo)) {
objectAnimationName = action.getName();
break;
}
}
}
}
String objectName = objectStructure.getName(); // calculating track
if (objectAnimationName == null) {// set the object's animation name to object's name SpatialTrack track = (SpatialTrack) ipo.calculateTrack(-1, 0, ipo.getLastFrame(), fps, true);
objectAnimationName = objectName;
}
IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class); Animation animation = new Animation(objectAnimationName, ipo.getLastFrame() / fps);
Structure ipoStructure = pIpo.fetchData(blenderContext.getInputStream()).get(0); animation.setTracks(new SpatialTrack[] { track });
Ipo ipo = ipoHelper.createIpo(ipoStructure, blenderContext); ArrayList<Animation> animations = new ArrayList<Animation>(1);
int fps = blenderContext.getBlenderKey().getFps(); animations.add(animation);
// calculating track for the only bone in this skeleton
SpatialTrack track = (SpatialTrack) ipo.calculateTrack(-1, 0, ipo.getLastFrame(), fps);
Animation animation = new Animation(objectAnimationName, ipo.getLastFrame() / fps);
animation.setTracks(new SpatialTrack[] { track });
ArrayList<Animation> animations = new ArrayList<Animation>(1);
animations.add(animation);
animData = new AnimData(null, animations); animData = new AnimData(null, animations);
blenderContext.setAnimData(objectOMA, animData); blenderContext.setAnimData(objectOMA, animData);
}
} }
@Override @Override
public Node apply(Node node, BlenderContext blenderContext) { public Node apply(Node node, BlenderContext blenderContext) {
if(invalid) { if (invalid) {
LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName()); LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName());
}//if invalid, animData will be null }// if invalid, animData will be null
if(animData != null) { if (animData != null) {
//INFO: constraints for this modifier are applied in the ObjectHelper when the whole object is loaded // INFO: constraints for this modifier are applied in the
// ObjectHelper when the whole object is loaded
ArrayList<Animation> animList = animData.anims; ArrayList<Animation> animList = animData.anims;
if (animList != null && animList.size() > 0) { if (animList != null && animList.size() > 0) {
HashMap<String, Animation> anims = new HashMap<String, Animation>(); HashMap<String, Animation> anims = new HashMap<String, Animation>();

@ -261,7 +261,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
List<Constraint> objectConstraints = blenderContext.getConstraints(objectStructure.getOldMemoryAddress()); List<Constraint> objectConstraints = blenderContext.getConstraints(objectStructure.getOldMemoryAddress());
if(objectConstraints!=null) { if(objectConstraints!=null) {
for(Constraint objectConstraint : objectConstraints) { for(Constraint objectConstraint : objectConstraints) {
objectConstraint.bake(Constraint.BAKE_STATIC); objectConstraint.bake();
} }
} }

Loading…
Cancel
Save