Many changes to blender importer:

- constraints refactoring (easier use and less code)
- constraints support for blender 2.50+ (although not all of the constraints are supported)
- Y is up axis issue fixed for animations
- owner and target spaces evaluation support for constraints (two bone space modes still to do)
- simplified code for bones loading (though still needs some refactoring)

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8877 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
Kae..pl 13 years ago
parent fb2d8c647d
commit a1d73d159d
  1. 12
      engine/src/blender/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
  2. 912
      engine/src/blender/com/jme3/scene/plugins/blender/BlenderContext.java
  3. 34
      engine/src/blender/com/jme3/scene/plugins/blender/BlenderLoader.java
  4. 362
      engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java
  5. 94
      engine/src/blender/com/jme3/scene/plugins/blender/animations/Ipo.java
  6. 13
      engine/src/blender/com/jme3/scene/plugins/blender/animations/IpoHelper.java
  7. 6
      engine/src/blender/com/jme3/scene/plugins/blender/cameras/CameraHelper.java
  8. 147
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/BlenderTrack.java
  9. 211
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java
  10. 14
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintAction.java
  11. 14
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintChildOf.java
  12. 16
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintClampTo.java
  13. 49
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDampTrack.java
  14. 123
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintDistLimit.java
  15. 78
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintFactory.java
  16. 14
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintFollowPath.java
  17. 158
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
  18. 77
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintInverseKinematics.java
  19. 112
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLike.java
  20. 143
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLocLimit.java
  21. 14
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintLockTrack.java
  22. 14
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintMinMax.java
  23. 13
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintNull.java
  24. 50
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPivot.java
  25. 14
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintPython.java
  26. 14
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRigidBodyJoint.java
  27. 107
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLike.java
  28. 140
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintRotLimit.java
  29. 52
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintShrinkWrap.java
  30. 81
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLike.java
  31. 134
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSizeLimit.java
  32. 50
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintSplineInverseKinematic.java
  33. 19
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintStretchTo.java
  34. 16
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintTransform.java
  35. 145
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintType.java
  36. 274
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/Feature.java
  37. 8
      engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java
  38. 6
      engine/src/blender/com/jme3/scene/plugins/blender/lights/LightHelper.java
  39. 8
      engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java
  40. 6
      engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
  41. 278
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java
  42. 6
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java
  43. 67
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ObjectAnimationModifier.java
  44. 111
      engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java
  45. 6
      engine/src/blender/com/jme3/scene/plugins/blender/particles/ParticlesHelper.java
  46. 2
      engine/src/blender/com/jme3/scene/plugins/blender/textures/NoiseGenerator.java
  47. 6
      engine/src/blender/com/jme3/scene/plugins/blender/textures/TextureHelper.java

@ -60,17 +60,11 @@ public abstract class AbstractBlenderHelper {
* versions.
* @param blenderVersion
* the version read from the blend file
*/
public AbstractBlenderHelper(String blenderVersion) {
this.blenderVersion = Integer.parseInt(blenderVersion);
}
/**
* This method sets the Y is UP axis. By default the UP axis is Z (just like in blender).
* @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 void setyIsUpAxis(boolean fixUpAxis) {
public AbstractBlenderHelper(String blenderVersion, boolean fixUpAxis) {
this.blenderVersion = Integer.parseInt(blenderVersion);
this.fixUpAxis = fixUpAxis;
if(fixUpAxis) {
upAxisRotationQuaternion = new Quaternion().fromAngles(-FastMath.HALF_PI, 0, 0);

@ -31,6 +31,16 @@
*/
package com.jme3.scene.plugins.blender;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.asset.AssetManager;
import com.jme3.asset.BlenderKey;
import com.jme3.material.Material;
@ -44,446 +54,538 @@ import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialContext;
import com.jme3.scene.plugins.blender.meshes.MeshContext;
import com.jme3.scene.plugins.blender.modifiers.Modifier;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.scene.plugins.ogre.AnimData;
/**
* The class that stores temporary data and manages it during loading the belnd file. This class is intended to be used
* in a single loading thread. It holds the state of loading operations.
* The class that stores temporary data and manages it during loading the belnd
* file. This class is intended to be used in a single loading thread. It holds
* the state of loading operations.
*
* @author Marcin Roguski (Kaelthas)
*/
public class BlenderContext {
private static final Logger LOGGER = Logger.getLogger(BlenderContext.class.getName());
/** The blender key. */
private BlenderKey blenderKey;
/** The header of the file block. */
private DnaBlockData dnaBlockData;
/** The input stream of the blend file. */
private BlenderInputStream inputStream;
/** The asset manager. */
private AssetManager assetManager;
/** A map containing the file block headers. The key is the old pointer address. */
private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>();
/** A map containing the file block headers. The key is the block code. */
private Map<Integer, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<Integer, List<FileBlockHeader>>();
/**
* This map stores the loaded features by their old memory address. The first object in the value table is the
* loaded structure and the second - the structure already converted into proper data.
*/
private Map<Long, Object[]> loadedFeatures = new HashMap<Long, Object[]>();
/**
* This map stores the loaded features by their name. Only features with ID structure can be stored here.
* The first object in the value table is the
* loaded structure and the second - the structure already converted into proper data.
*/
private Map<String, Object[]> loadedFeaturesByName = new HashMap<String, Object[]>();
/** A stack that hold the parent structure of currently loaded feature. */
private Stack<Structure> parentStack = new Stack<Structure>();
/** A map storing loaded ipos. The key is the ipo's owner old memory address and the value is the ipo. */
private Map<Long, Ipo> loadedIpos = new HashMap<Long, Ipo>();
/** A list of modifiers for the specified object. */
protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>();
/** A list of constraints for the specified object. */
protected Map<Long, List<Constraint>> constraints = new HashMap<Long, List<Constraint>>();
/** A map of mesh contexts. */
protected Map<Long, MeshContext> meshContexts = new HashMap<Long, MeshContext>();
/** A map of material contexts. */
protected Map<Material, MaterialContext> materialContexts = new HashMap<Material, MaterialContext>();
/** A map og helpers that perform loading. */
private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>();
/**
* This method sets the blender key.
* @param blenderKey
* the blender key
*/
public void setBlenderKey(BlenderKey blenderKey) {
this.blenderKey = blenderKey;
}
/**
* This method returns the blender key.
* @return the blender key
*/
public BlenderKey getBlenderKey() {
return blenderKey;
}
/**
* This method sets the dna block data.
* @param dnaBlockData
* the dna block data
*/
public void setBlockData(DnaBlockData dnaBlockData) {
this.dnaBlockData = dnaBlockData;
}
/**
* This method returns the dna block data.
* @return the dna block data
*/
public DnaBlockData getDnaBlockData() {
return dnaBlockData;
}
/**
* This method returns the asset manager.
* @return the asset manager
*/
public AssetManager getAssetManager() {
return assetManager;
}
/**
* This method sets the asset manager.
* @param assetManager
* the asset manager
*/
public void setAssetManager(AssetManager assetManager) {
this.assetManager = assetManager;
}
/**
* This method returns the input stream of the blend file.
* @return the input stream of the blend file
*/
public BlenderInputStream getInputStream() {
return inputStream;
}
/**
* This method sets the input stream of the blend file.
* @param inputStream
* the input stream of the blend file
*/
public void setInputStream(BlenderInputStream inputStream) {
this.inputStream = inputStream;
}
/**
* This method adds a file block header to the map. Its old memory address is the key.
* @param oldMemoryAddress
* the address of the block header
* @param fileBlockHeader
* the block header to store
*/
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode()));
if (headers == null) {
headers = new ArrayList<FileBlockHeader>();
fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers);
}
headers.add(fileBlockHeader);
}
/**
* This method returns the block header of a given memory address. If the header is not present then null is
* returned.
* @param oldMemoryAddress
* the address of the block header
* @return loaded header or null if it was not yet loaded
*/
public FileBlockHeader getFileBlock(Long oldMemoryAddress) {
return fileBlockHeadersByOma.get(oldMemoryAddress);
}
/**
* This method returns a list of file blocks' headers of a specified code.
* @param code
* the code of file blocks
* @return a list of file blocks' headers of a specified code
*/
public List<FileBlockHeader> getFileBlocks(Integer code) {
return fileBlockHeadersByCode.get(code);
}
/**
* This method clears the saved block headers stored in the features map.
*/
public void clearFileBlocks() {
fileBlockHeadersByOma.clear();
fileBlockHeadersByCode.clear();
}
/**
* This method adds a helper instance to the helpers' map.
* @param <T>
* the type of the helper
* @param clazz
* helper's class definition
* @param helper
* the helper instance
*/
public <T> void putHelper(Class<T> clazz, AbstractBlenderHelper helper) {
helpers.put(clazz.getSimpleName(), helper);
}
@SuppressWarnings("unchecked")
public <T> T getHelper(Class<?> clazz) {
return (T) helpers.get(clazz.getSimpleName());
}
/**
* This method adds a loaded feature to the map. The key is its unique old memory address.
* @param oldMemoryAddress
* the address of the feature
* @param featureName the name of the feature
* @param structure
* the filled structure of the feature
* @param feature
* the feature we want to store
*/
public void addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature) {
if (oldMemoryAddress == null || structure == null || feature == null) {
throw new IllegalArgumentException("One of the given arguments is null!");
}
Object[] storedData = new Object[]{structure, feature};
loadedFeatures.put(oldMemoryAddress, storedData);
if (featureName != null) {
loadedFeaturesByName.put(featureName, storedData);
}
}
/**
* This method returns the feature of a given memory address. If the feature is not yet loaded then null is
* returned.
* @param oldMemoryAddress
* the address of the feature
* @param loadedFeatureDataType
* the type of data we want to retreive it can be either filled structure or already converted feature
* @return loaded feature or null if it was not yet loaded
*/
public Object getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType) {
Object[] result = loadedFeatures.get(oldMemoryAddress);
if (result != null) {
return result[loadedFeatureDataType.getIndex()];
}
return null;
}
/**
* This method returns the feature of a given name. If the feature is not yet loaded then null is
* returned.
* @param featureName
* the name of the feature
* @param loadedFeatureDataType
* the type of data we want to retreive it can be either filled structure or already converted feature
* @return loaded feature or null if it was not yet loaded
*/
public Object getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType) {
Object[] result = loadedFeaturesByName.get(featureName);
if (result != null) {
return result[loadedFeatureDataType.getIndex()];
}
return null;
}
/**
* This method clears the saved features stored in the features map.
*/
public void clearLoadedFeatures() {
loadedFeatures.clear();
}
/**
* This method adds the structure to the parent stack.
* @param parent
* the structure to be added to the stack
*/
public void pushParent(Structure parent) {
parentStack.push(parent);
}
/**
* This method removes the structure from the top of the parent's stack.
* @return the structure that was removed from the stack
*/
public Structure popParent() {
try {
return parentStack.pop();
} catch (EmptyStackException e) {
return null;
}
}
/**
* This method retreives the structure at the top of the parent's stack but does not remove it.
* @return the structure from the top of the stack
*/
public Structure peekParent() {
try {
return parentStack.peek();
} catch (EmptyStackException e) {
return null;
}
}
public void addIpo(Long ownerOMA, Ipo ipo) {
loadedIpos.put(ownerOMA, ipo);
}
public Ipo removeIpo(Long ownerOma) {
return loadedIpos.remove(ownerOma);
}
public Ipo getIpo(Long ownerOMA) {
return loadedIpos.get(ownerOMA);
}
/**
* This method adds a new modifier to the list.
* @param ownerOMA
* the owner's old memory address
* @param modifier
* the object's modifier
*/
public void addModifier(Long ownerOMA, Modifier modifier) {
List<Modifier> objectModifiers = this.modifiers.get(ownerOMA);
if (objectModifiers == null) {
objectModifiers = new ArrayList<Modifier>();
this.modifiers.put(ownerOMA, objectModifiers);
}
objectModifiers.add(modifier);
}
/**
* This method returns modifiers for the object specified by its old memory address and the modifier type. If no
* modifiers are found - empty list is returned. If the type is null - all modifiers for the object are returned.
* @param objectOMA
* object's old memory address
* @param type
* the type of the modifier
* @return the list of object's modifiers
*/
public List<Modifier> getModifiers(Long objectOMA, String type) {
List<Modifier> result = new ArrayList<Modifier>();
List<Modifier> readModifiers = modifiers.get(objectOMA);
if (readModifiers != null && readModifiers.size() > 0) {
for (Modifier modifier : readModifiers) {
if (type == null || type.isEmpty() || modifier.getType().equals(type)) {
result.add(modifier);
}
}
}
return result;
}
/**
* This method adds a new modifier to the list.
* @param ownerOMA
* the owner's old memory address
* @param constraints
* the object's constraints
*/
public void addConstraints(Long ownerOMA, List<Constraint> constraints) {
List<Constraint> objectConstraints = this.constraints.get(ownerOMA);
if (objectConstraints == null) {
objectConstraints = new ArrayList<Constraint>();
this.constraints.put(ownerOMA, objectConstraints);
}
objectConstraints.addAll(constraints);
}
/**
* This method returns constraints for the object specified by its old memory address. If no
* modifiers are found - <b>null</b> is returned.
* @param objectOMA
* object's old memory address
* @return the list of object's modifiers or null
*/
public List<Constraint> getConstraints(Long objectOMA) {
return constraints.get(objectOMA);
}
/**
private static final Logger LOGGER = Logger.getLogger(BlenderContext.class.getName());
/** The blender key. */
private BlenderKey blenderKey;
/** The header of the file block. */
private DnaBlockData dnaBlockData;
/** The input stream of the blend file. */
private BlenderInputStream inputStream;
/** The asset manager. */
private AssetManager assetManager;
/**
* A map containing the file block headers. The key is the old pointer
* address.
*/
private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>();
/** A map containing the file block headers. The key is the block code. */
private Map<Integer, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<Integer, List<FileBlockHeader>>();
/**
* This map stores the loaded features by their old memory address. The
* first object in the value table is the loaded structure and the second -
* the structure already converted into proper data.
*/
private Map<Long, Object[]> loadedFeatures = new HashMap<Long, Object[]>();
/**
* This map stores the loaded features by their name. Only features with ID
* structure can be stored here. The first object in the value table is the
* loaded structure and the second - the structure already converted into
* proper data.
*/
private Map<String, Object[]> loadedFeaturesByName = new HashMap<String, Object[]>();
/** A stack that hold the parent structure of currently loaded feature. */
private Stack<Structure> parentStack = new Stack<Structure>();
/**
* A map storing loaded ipos. The key is the ipo's owner old memory address
* and the value is the ipo.
*/
private Map<Long, Ipo> loadedIpos = new HashMap<Long, Ipo>();
/** A list of modifiers for the specified object. */
protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>();
/** A list of constraints for the specified object. */
protected Map<Long, List<Constraint>> constraints = new HashMap<Long, List<Constraint>>();
/** Anim data loaded for features. */
private Map<Long, AnimData> animData = new HashMap<Long, AnimData>();
/** A map of mesh contexts. */
protected Map<Long, MeshContext> meshContexts = new HashMap<Long, MeshContext>();
/** A map of material contexts. */
protected Map<Material, MaterialContext> materialContexts = new HashMap<Material, MaterialContext>();
/** A map og helpers that perform loading. */
private Map<String, AbstractBlenderHelper> helpers = new HashMap<String, AbstractBlenderHelper>();
/**
* This method sets the blender key.
*
* @param blenderKey
* the blender key
*/
public void setBlenderKey(BlenderKey blenderKey) {
this.blenderKey = blenderKey;
}
/**
* This method returns the blender key.
*
* @return the blender key
*/
public BlenderKey getBlenderKey() {
return blenderKey;
}
/**
* This method sets the dna block data.
*
* @param dnaBlockData
* the dna block data
*/
public void setBlockData(DnaBlockData dnaBlockData) {
this.dnaBlockData = dnaBlockData;
}
/**
* This method returns the dna block data.
*
* @return the dna block data
*/
public DnaBlockData getDnaBlockData() {
return dnaBlockData;
}
/**
* This method returns the asset manager.
*
* @return the asset manager
*/
public AssetManager getAssetManager() {
return assetManager;
}
/**
* This method sets the asset manager.
*
* @param assetManager
* the asset manager
*/
public void setAssetManager(AssetManager assetManager) {
this.assetManager = assetManager;
}
/**
* This method returns the input stream of the blend file.
*
* @return the input stream of the blend file
*/
public BlenderInputStream getInputStream() {
return inputStream;
}
/**
* This method sets the input stream of the blend file.
*
* @param inputStream
* the input stream of the blend file
*/
public void setInputStream(BlenderInputStream inputStream) {
this.inputStream = inputStream;
}
/**
* This method adds a file block header to the map. Its old memory address
* is the key.
*
* @param oldMemoryAddress
* the address of the block header
* @param fileBlockHeader
* the block header to store
*/
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode()));
if (headers == null) {
headers = new ArrayList<FileBlockHeader>();
fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers);
}
headers.add(fileBlockHeader);
}
/**
* This method returns the block header of a given memory address. If the
* header is not present then null is returned.
*
* @param oldMemoryAddress
* the address of the block header
* @return loaded header or null if it was not yet loaded
*/
public FileBlockHeader getFileBlock(Long oldMemoryAddress) {
return fileBlockHeadersByOma.get(oldMemoryAddress);
}
/**
* This method returns a list of file blocks' headers of a specified code.
*
* @param code
* the code of file blocks
* @return a list of file blocks' headers of a specified code
*/
public List<FileBlockHeader> getFileBlocks(Integer code) {
return fileBlockHeadersByCode.get(code);
}
/**
* This method clears the saved block headers stored in the features map.
*/
public void clearFileBlocks() {
fileBlockHeadersByOma.clear();
fileBlockHeadersByCode.clear();
}
/**
* This method adds a helper instance to the helpers' map.
*
* @param <T>
* the type of the helper
* @param clazz
* helper's class definition
* @param helper
* the helper instance
*/
public <T> void putHelper(Class<T> clazz, AbstractBlenderHelper helper) {
helpers.put(clazz.getSimpleName(), helper);
}
@SuppressWarnings("unchecked")
public <T> T getHelper(Class<?> clazz) {
return (T) helpers.get(clazz.getSimpleName());
}
/**
* This method adds a loaded feature to the map. The key is its unique old
* memory address.
*
* @param oldMemoryAddress
* the address of the feature
* @param featureName
* the name of the feature
* @param structure
* the filled structure of the feature
* @param feature
* the feature we want to store
*/
public void addLoadedFeatures(Long oldMemoryAddress, String featureName, Structure structure, Object feature) {
if (oldMemoryAddress == null || structure == null || feature == null) {
throw new IllegalArgumentException("One of the given arguments is null!");
}
Object[] storedData = new Object[] { structure, feature };
loadedFeatures.put(oldMemoryAddress, storedData);
if (featureName != null) {
loadedFeaturesByName.put(featureName, storedData);
}
}
/**
* This method returns the feature of a given memory address. If the feature
* is not yet loaded then null is returned.
*
* @param oldMemoryAddress
* the address of the feature
* @param loadedFeatureDataType
* the type of data we want to retreive it can be either filled
* structure or already converted feature
* @return loaded feature or null if it was not yet loaded
*/
public Object getLoadedFeature(Long oldMemoryAddress, LoadedFeatureDataType loadedFeatureDataType) {
Object[] result = loadedFeatures.get(oldMemoryAddress);
if (result != null) {
return result[loadedFeatureDataType.getIndex()];
}
return null;
}
/**
* This method returns the feature of a given name. If the feature is not
* yet loaded then null is returned.
*
* @param featureName
* the name of the feature
* @param loadedFeatureDataType
* the type of data we want to retreive it can be either filled
* structure or already converted feature
* @return loaded feature or null if it was not yet loaded
*/
public Object getLoadedFeature(String featureName, LoadedFeatureDataType loadedFeatureDataType) {
Object[] result = loadedFeaturesByName.get(featureName);
if (result != null) {
return result[loadedFeatureDataType.getIndex()];
}
return null;
}
/**
* This method clears the saved features stored in the features map.
*/
public void clearLoadedFeatures() {
loadedFeatures.clear();
}
/**
* This method adds the structure to the parent stack.
*
* @param parent
* the structure to be added to the stack
*/
public void pushParent(Structure parent) {
parentStack.push(parent);
}
/**
* This method removes the structure from the top of the parent's stack.
*
* @return the structure that was removed from the stack
*/
public Structure popParent() {
try {
return parentStack.pop();
} catch (EmptyStackException e) {
return null;
}
}
/**
* This method retreives the structure at the top of the parent's stack but
* does not remove it.
*
* @return the structure from the top of the stack
*/
public Structure peekParent() {
try {
return parentStack.peek();
} catch (EmptyStackException e) {
return null;
}
}
/**
* This method adds new ipo curve for the feature.
*
* @param ownerOMA
* the OMA of blender feature that owns the ipo
* @param ipo
* the ipo to be added
*/
public void addIpo(Long ownerOMA, Ipo ipo) {
loadedIpos.put(ownerOMA, ipo);
}
/**
* This method removes the ipo curve from the feature.
*
* @param ownerOMA
* the OMA of blender feature that owns the ipo
* @param ipo
* the ipo that was just removed
*/
public Ipo removeIpo(Long ownerOma) {
return loadedIpos.remove(ownerOma);
}
/**
* This method returns the ipo curve of the feature.
*
* @param ownerOMA
* the OMA of blender feature that owns the ipo
* @param ipo
* the ipo that belongs to the specified owner
*/
public Ipo getIpo(Long ownerOMA) {
return loadedIpos.get(ownerOMA);
}
/**
* This method adds a new modifier to the list.
*
* @param ownerOMA
* the owner's old memory address
* @param modifier
* the object's modifier
*/
public void addModifier(Long ownerOMA, Modifier modifier) {
List<Modifier> objectModifiers = this.modifiers.get(ownerOMA);
if (objectModifiers == null) {
objectModifiers = new ArrayList<Modifier>();
this.modifiers.put(ownerOMA, objectModifiers);
}
objectModifiers.add(modifier);
}
/**
* This method returns modifiers for the object specified by its old memory
* address and the modifier type. If no modifiers are found - empty list is
* returned. If the type is null - all modifiers for the object are
* returned.
*
* @param objectOMA
* object's old memory address
* @param type
* the type of the modifier
* @return the list of object's modifiers
*/
public List<Modifier> getModifiers(Long objectOMA, String type) {
List<Modifier> result = new ArrayList<Modifier>();
List<Modifier> readModifiers = modifiers.get(objectOMA);
if (readModifiers != null && readModifiers.size() > 0) {
for (Modifier modifier : readModifiers) {
if (type == null || type.isEmpty() || modifier.getType().equals(type)) {
result.add(modifier);
}
}
}
return result;
}
/**
* This method adds a new modifier to the list.
*
* @param ownerOMA
* the owner's old memory address
* @param constraints
* the object's constraints
*/
public void addConstraints(Long ownerOMA, List<Constraint> constraints) {
List<Constraint> objectConstraints = this.constraints.get(ownerOMA);
if (objectConstraints == null) {
objectConstraints = new ArrayList<Constraint>();
this.constraints.put(ownerOMA, objectConstraints);
}
objectConstraints.addAll(constraints);
}
/**
* This method returns constraints for the object specified by its old
* memory address. If no modifiers are found - <b>null</b> is returned.
*
* @param objectOMA
* object's old memory address
* @return the list of object's modifiers or null
*/
public List<Constraint> getConstraints(Long objectOMA) {
return objectOMA == null ? null : constraints.get(objectOMA);
}
/**
* This method sets the anim data for the specified OMA of its owner.
*
* @param ownerOMA
* the owner's old memory address
* @param animData
* the animation data for the feature specified by ownerOMA
*/
public void setAnimData(Long ownerOMA, AnimData animData) {
this.animData.put(ownerOMA, animData);
}
/**
* This method returns the animation data for the specified owner.
*
* @param ownerOMA
* the old memory address of the animation data owner
* @return the animation data or null if none exists
*/
public AnimData getAnimData(Long ownerOMA) {
return this.animData.get(ownerOMA);
}
/**
* This method sets the mesh context for the given mesh old memory address.
* If the context is already set it will be replaced.
*
* @param meshOMA
* the mesh's old memory address
* the mesh's old memory address
* @param meshContext
* the mesh's context
* the mesh's context
*/
public void setMeshContext(Long meshOMA, MeshContext meshContext) {
this.meshContexts.put(meshOMA, meshContext);
}
public void setMeshContext(Long meshOMA, MeshContext meshContext) {
this.meshContexts.put(meshOMA, meshContext);
}
/**
* This method returns the mesh context for the given mesh old memory address.
* If no context exists then <b>null</b> is returned.
/**
* This method returns the mesh context for the given mesh old memory
* address. If no context exists then <b>null</b> is returned.
*
* @param meshOMA
* the mesh's old memory address
* the mesh's old memory address
* @return mesh's context
*/
public MeshContext getMeshContext(Long meshOMA) {
return this.meshContexts.get(meshOMA);
}
public MeshContext getMeshContext(Long meshOMA) {
return this.meshContexts.get(meshOMA);
}
/**
* This method sets the material context for the given material.
* If the context is already set it will be replaced.
* This method sets the material context for the given material. If the
* context is already set it will be replaced.
*
* @param material
* the material
* the material
* @param materialContext
* the material's context
* the material's context
*/
public void setMaterialContext(Material material, MaterialContext materialContext) {
this.materialContexts.put(material, materialContext);
}
/**
* This method returns the material context for the given material.
* If no context exists then <b>null</b> is returned.
* This method returns the material context for the given material. If no
* context exists then <b>null</b> is returned.
*
* @param material
* the material
* the material
* @return material's context
*/
public MaterialContext getMaterialContext(Material material) {
return materialContexts.get(material);
}
/**
* This metod returns the default material.
* @return the default material
*/
public synchronized Material getDefaultMaterial() {
if (blenderKey.getDefaultMaterial() == null) {
Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
defaultMaterial.setColor("Color", ColorRGBA.DarkGray);
blenderKey.setDefaultMaterial(defaultMaterial);
}
return blenderKey.getDefaultMaterial();
}
public void dispose() {
try {
/**
* This metod returns the default material.
*
* @return the default material
*/
public synchronized Material getDefaultMaterial() {
if (blenderKey.getDefaultMaterial() == null) {
Material defaultMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
defaultMaterial.setColor("Color", ColorRGBA.DarkGray);
blenderKey.setDefaultMaterial(defaultMaterial);
}
return blenderKey.getDefaultMaterial();
}
public void dispose() {
try {
inputStream.close();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
loadedFeatures.clear();
loadedFeaturesByName.clear();
}
/**
* This enum defines what loaded data type user wants to retreive. It can be either filled structure or already
* converted data.
* @author Marcin Roguski
*/
public static enum LoadedFeatureDataType {
LOADED_STRUCTURE(0), LOADED_FEATURE(1);
private int index;
private LoadedFeatureDataType(int index) {
this.index = index;
}
public int getIndex() {
return index;
}
}
}
/**
* This enum defines what loaded data type user wants to retreive. It can be
* either filled structure or already converted data.
*
* @author Marcin Roguski
*/
public static enum LoadedFeatureDataType {
LOADED_STRUCTURE(0), LOADED_FEATURE(1);
private int index;
private LoadedFeatureDataType(int index) {
this.index = index;
}
public int getIndex() {
return index;
}
}
}

@ -189,30 +189,20 @@ public class BlenderLoader extends AbstractBlenderLoader {
blenderContext.setBlenderKey(blenderKey);
// creating helpers
blenderContext.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber()));
blenderContext.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber()));
blenderContext.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber()));
blenderContext.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber()));
blenderContext.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber()));
blenderContext.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber()));
blenderContext.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber()));
blenderContext.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber()));
blenderContext.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber()));
blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext));
blenderContext.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber()));
blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber()));
blenderContext.putHelper(ArmatureHelper.class, new ArmatureHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
blenderContext.putHelper(TextureHelper.class, new TextureHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
blenderContext.putHelper(MeshHelper.class, new MeshHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
blenderContext.putHelper(ObjectHelper.class, new ObjectHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
blenderContext.putHelper(CurvesHelper.class, new CurvesHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
blenderContext.putHelper(LightHelper.class, new LightHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
blenderContext.putHelper(CameraHelper.class, new CameraHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
blenderContext.putHelper(ModifierHelper.class, new ModifierHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
blenderContext.putHelper(MaterialHelper.class, new MaterialHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext, blenderKey.isFixUpAxis()));
blenderContext.putHelper(IpoHelper.class, new IpoHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderKey.isFixUpAxis()));
// setting additional data to helpers
if (blenderKey.isFixUpAxis()) {
AbstractBlenderHelper helper = blenderContext.getHelper(ObjectHelper.class);
helper.setyIsUpAxis(true);
helper = blenderContext.getHelper(CurvesHelper.class);
helper.setyIsUpAxis(true);
helper = blenderContext.getHelper(ArmatureHelper.class);
helper.setyIsUpAxis(true);
helper = blenderContext.getHelper(MeshHelper.class);
helper.setyIsUpAxis(true);
}
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
materialHelper.setFaceCullMode(blenderKey.getFaceCullMode());

@ -31,50 +31,126 @@
*/
package com.jme3.scene.plugins.blender.animations;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.animation.Bone;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.Skeleton;
import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.math.Transform;
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.curves.BezierCurve;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.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.Structure;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
/**
* This class defines the methods to calculate certain aspects of animation and armature functionalities.
* @author Marcin Roguski
* @author Marcin Roguski (Kaelthas)
*/
public class ArmatureHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(ArmatureHelper.class.getName());
/** A map of bones and their old memory addresses. */
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.
* @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 ArmatureHelper(String blenderVersion) {
super(blenderVersion);
public ArmatureHelper(String blenderVersion, boolean fixUpAxis) {
super(blenderVersion, fixUpAxis);
}
/**
* The map of the bones. Maps a bone name to its index in the armature. Should be cleared after the object had been
* read. TODO: probably bones can have identical names in different armatures
*/
protected Map<String, Integer> bonesMap = new HashMap<String, Integer>();
/** A map of bones and their old memory addresses. */
protected Map<Bone, Long> bonesOMAs = new HashMap<Bone, Long>();
/** This list contains bones hierarchy and their matrices. It is later converted into jme bones. */
protected List<BoneTransformationData> boneDataRoots = new ArrayList<BoneTransformationData>();
* This method builds the object's bones structure.
*
* @param boneStructure
* the structure containing the bones' data
* @param parent
* the parent bone
* @param result
* the list where the newly created bone will be added
* @param bonesPoseChannels
* a map of bones poses channels
* @param blenderContext
* the blender context
* @throws BlenderFileException
* an exception is thrown when there is problem with the blender
* file
*/
@SuppressWarnings("unchecked")
public void buildBones(Structure boneStructure, Bone parent, List<Bone> result, Matrix4f arbt,
final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
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
@ -91,6 +167,16 @@ public class ArmatureHelper extends AbstractBlenderHelper {
return result;
}
/**
* This method returns the bind transform for the specified bone.
* @param bone
* the bone
* @return bone's bind transform
*/
public Transform getBoneBindTransform(Bone bone) {
return boneBindTransforms.get(bone);
}
/**
* 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.
@ -100,15 +186,15 @@ public class ArmatureHelper extends AbstractBlenderHelper {
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow corrupted
*/
public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, BlenderContext blenderContext) throws BlenderFileException {
public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
Map<Integer, Integer> result = null;
if (bonesMap != null && bonesMap.size() != 0) {
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 = bonesMap.get(deformGroupName);
Integer boneIndex = this.getBoneIndex(skeleton, deformGroupName);
if (boneIndex != null) {
result.put(Integer.valueOf(groupIndex), boneIndex);
}
@ -118,191 +204,6 @@ public class ArmatureHelper extends AbstractBlenderHelper {
return result;
}
/**
* This bone returns transformation matrix of the bone that is relative to
* its armature object.
* @param boneStructure the bone's structure
* @return bone's transformation matrix in armature space
*/
@SuppressWarnings("unchecked")
protected Matrix4f getArmatureMatrix(Structure boneStructure) {
DynamicArray<Number> boneMat = (DynamicArray<Number>) boneStructure.getFieldValue("arm_mat");
Matrix4f m = new Matrix4f();
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
m.set(i, j, boneMat.get(j, i).floatValue());
}
}
if(fixUpAxis) {
Vector3f translation = m.toTranslationVector();
Quaternion rotation = m.toRotationQuat();
float y = translation.y;
translation.y = translation.z;
translation.z = -y;
rotation = upAxisRotationQuaternion.mult(rotation);
m.setRotationQuaternion(rotation);
m.setTranslation(translation);
//TODO: what about scale ??
}
return m;
}
/**
* This method reads the bone with its children.
* @param boneStructure
* a structure containing the bone data
* @param parent
* the bone parent; if null then we read the root bone
* @param blenderContext
* the blender context
* @return the bone transformation data; contains bone chierarchy and the bone's matrices
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow corrupted
*/
@SuppressWarnings("unchecked")
public BoneTransformationData readBoneAndItsChildren(Structure boneStructure, BoneTransformationData parent, BlenderContext blenderContext) throws BlenderFileException {
String name = boneStructure.getFieldValue("name").toString();
Bone bone = new Bone(name);
int bonesAmount = bonesOMAs.size();
bonesOMAs.put(bone, boneStructure.getOldMemoryAddress());
if (bonesAmount == bonesOMAs.size()) {
throw new IllegalStateException("Two bones has the same hash value and thereforw a bone was overriden in the bones<->OMA map! Improve the hash algorithm!");
}
Matrix4f boneArmatureMatrix = this.getArmatureMatrix(boneStructure);
DynamicArray<Float> sizeArray = (DynamicArray<Float>) boneStructure.getFieldValue("size");
Vector3f size = new Vector3f(sizeArray.get(0), sizeArray.get(1), sizeArray.get(2));
BoneTransformationData boneTransformationData = new BoneTransformationData(boneArmatureMatrix, size, bone, parent);
blenderContext.addLoadedFeatures(boneStructure.getOldMemoryAddress(), name, boneStructure, bone);
Structure childbase = (Structure) boneStructure.getFieldValue("childbase");
List<Structure> children = childbase.evaluateListBase(blenderContext);//Bone
for (Structure boneChild : children) {
this.readBoneAndItsChildren(boneChild, boneTransformationData, blenderContext);
}
return boneTransformationData;
}
/**
* This method assigns transformations to the bone.
* @param btd
* the bone data containing the bone we assign transformation to
* @param additionalRootBoneTransformation
* additional bone transformation which indicates it's mesh parent and armature object transformations
* @param boneList
* a list of all read bones
*/
protected void assignBonesMatrices(BoneTransformationData btd, Matrix4f additionalRootBoneTransformation, List<Bone> boneList) {
LOGGER.info("[" + btd.bone.getName() + "] additionalRootBoneTransformation =\n" + additionalRootBoneTransformation);
Matrix4f totalInverseParentMatrix = btd.parent != null ? btd.parent.totalInverseBoneParentMatrix : Matrix4f.IDENTITY;
LOGGER.info("[" + btd.bone.getName() + "] totalInverseParentMatrix =\n" + totalInverseParentMatrix);
Matrix4f restMatrix = additionalRootBoneTransformation.mult(btd.boneArmatureMatrix);
LOGGER.info("[" + btd.bone.getName() + "] restMatrix =\n" + restMatrix);
btd.totalInverseBoneParentMatrix = restMatrix.clone().invert();
restMatrix = totalInverseParentMatrix.mult(restMatrix);
LOGGER.info("[" + btd.bone.getName() + "] resultMatrix =\n" + restMatrix);
btd.bone.setBindTransforms(restMatrix.toTranslationVector(), restMatrix.toRotationQuat(), btd.size);
boneList.add(btd.bone);
bonesMap.put(btd.bone.getName(), Integer.valueOf(boneList.size() - 1));
if (btd.children != null && btd.children.size() > 0) {
for (BoneTransformationData child : btd.children) {
this.assignBonesMatrices(child, additionalRootBoneTransformation, boneList);
btd.bone.addChild(child.bone);
}
}
}
public void addBoneDataRoot(BoneTransformationData dataRoot) {
this.boneDataRoots.add(dataRoot);
}
/**
* This method returns bone transformation data for the bone of a given index.
* @param index
* the index of the bone
* @return bone's transformation data
*/
public BoneTransformationData getBoneTransformationDataRoot(int index) {
return boneDataRoots.get(index);
}
/**
* This method returns the amount of bones transformations roots.
* @return the amount of bones transformations roots
*/
public int getBoneTransformationDataRootsSize() {
return boneDataRoots.size();
}
/**
* This class holds the data needed later for bone transformation calculation and to bind parent with children.
* @author Marcin Roguski
*/
public static class BoneTransformationData {
/** Inverse matrix of bone's parent bone. */
private Matrix4f totalInverseBoneParentMatrix;
/** Bone's matrix in armature's space. */
private Matrix4f boneArmatureMatrix;
/** Bone's size (apparently it is held outside the transformation matrix. */
private Vector3f size;
/** The bone the data applies to. */
private Bone bone;
/** The parent of the above mentioned bone (not assigned yet). */
private BoneTransformationData parent;
/** The children of the current bone. */
private List<BoneTransformationData> children;
/**
* Private constructor creates the object and assigns the given data.
* @param boneArmatureMatrix
* the matrix of the current bone
* @param size
* the bone's size
* @param bone
* the current bone
* @param parent
* the parent structure of the bone
*/
private BoneTransformationData(Matrix4f boneArmatureMatrix, Vector3f size, Bone bone, BoneTransformationData parent) {
this.boneArmatureMatrix = boneArmatureMatrix;
this.size = size;
this.bone = bone;
this.parent = parent;
this.children = new ArrayList<ArmatureHelper.BoneTransformationData>();
if (this.parent != null) {
this.parent.children.add(this);
}
}
}
/**
* This method creates the whole bones structure. Assignes transformations to bones and combines children with
* parents.
* @param armatureOMA
* old memory address of bones' armature object
* @param additionalRootBoneTransformation
* additional bone transformation which indicates it's mesh parent and armature object transformations
* @return
*/
public Bone[] buildBonesStructure(Long armatureOMA, Matrix4f additionalRootBoneTransformation) {
List<Bone> bones = new ArrayList<Bone>(boneDataRoots.size() + 1);
bones.add(new Bone(""));
for (BoneTransformationData btd : boneDataRoots) {
this.assignBonesMatrices(btd, additionalRootBoneTransformation, bones);
}
return bones.toArray(new Bone[bones.size()]);
}
@Override
public void clearState() {
bonesMap.clear();
boneDataRoots.clear();
}
@Override
public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
return true;
@ -320,11 +221,11 @@ public class ArmatureHelper extends AbstractBlenderHelper {
* an exception is thrown when there are problems with the blend
* file
*/
public BoneTrack[] getTracks(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
public BoneTrack[] getTracks(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
if (blenderVersion < 250) {
return this.getTracks249(actionStructure, blenderContext);
return this.getTracks249(actionStructure, skeleton, blenderContext);
} else {
return this.getTracks250(actionStructure, blenderContext);
return this.getTracks250(actionStructure, skeleton, blenderContext);
}
}
@ -340,19 +241,15 @@ public class ArmatureHelper extends AbstractBlenderHelper {
* an exception is thrown when there are problems with the blend
* file
*/
private BoneTrack[] getTracks250(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
private BoneTrack[] getTracks250(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.log(Level.INFO, "Getting tracks!");
int fps = blenderContext.getBlenderKey().getFps();
Structure groups = (Structure) actionStructure.getFieldValue("groups");
List<Structure> actionGroups = groups.evaluateListBase(blenderContext);//bActionGroup
if (actionGroups != null && actionGroups.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) {
throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!");
}
List<BoneTrack> tracks = new ArrayList<BoneTrack>();
for (Structure actionGroup : actionGroups) {
String name = actionGroup.getFieldValue("name").toString();
Integer boneIndex = bonesMap.get(name);
Integer boneIndex = this.getBoneIndex(skeleton, name);
if (boneIndex != null) {
List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(blenderContext);
BezierCurve[] bezierCurves = new BezierCurve[channels.size()];
@ -374,8 +271,8 @@ public class ArmatureHelper extends AbstractBlenderHelper {
bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);
}
Ipo ipo = new Ipo(bezierCurves);
tracks.add( (BoneTrack) ipo.calculateTrack(boneIndex.intValue(), 0, ipo.getLastFrame(), fps) );
Ipo ipo = new Ipo(bezierCurves, fixUpAxis);
tracks.add((BoneTrack) ipo.calculateTrack(boneIndex.intValue(), 0, ipo.getLastFrame(), fps));
}
}
return tracks.toArray(new BoneTrack[tracks.size()]);
@ -393,31 +290,44 @@ public class ArmatureHelper extends AbstractBlenderHelper {
* an exception is thrown when there are problems with the blend
* file
*/
private BoneTrack[] getTracks249(Structure actionStructure, BlenderContext blenderContext) throws BlenderFileException {
private BoneTrack[] getTracks249(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.log(Level.INFO, "Getting tracks!");
IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
int fps = blenderContext.getBlenderKey().getFps();
Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");
List<Structure> actionChannels = chanbase.evaluateListBase(blenderContext);//bActionChannel
if (actionChannels != null && actionChannels.size() > 0 && (bonesMap == null || bonesMap.size() == 0)) {
throw new IllegalStateException("No bones found! Cannot proceed to calculating tracks!");
}
List<BoneTrack> tracks = new ArrayList<BoneTrack>();
for (Structure bActionChannel : actionChannels) {
String name = bActionChannel.getFieldValue("name").toString();
Integer boneIndex = bonesMap.get(name);
if (boneIndex != null) {
Integer boneIndex = this.getBoneIndex(skeleton, name);
if (boneIndex != null && boneIndex.intValue() >= 0) {
Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");
if (!p.isNull()) {
Structure ipoStructure = p.fetchData(blenderContext.getInputStream()).get(0);
Ipo ipo = ipoHelper.createIpo(ipoStructure, blenderContext);
tracks.add( (BoneTrack) ipo.calculateTrack(boneIndex.intValue(), 0, ipo.getLastFrame(), fps));
tracks.add((BoneTrack) ipo.calculateTrack(boneIndex.intValue(), 0, ipo.getLastFrame(), fps));
}
}
}
return tracks.toArray(new BoneTrack[tracks.size()]);
}
/**
* This method returns the index of the bone in the given skeleton.
* @param skeleton the skeleton
* @param boneName the name of the bone
* @return the index of the bone
*/
private int getBoneIndex(Skeleton skeleton, String boneName) {
int result = -1;
for(int i=0;i<skeleton.getBoneCount() && result==-1;++i) {
if(boneName.equals(skeleton.getBone(i).getName())) {
result = i;
}
}
return result;
}
/**
* This method parses the information stored inside the curve rna path and returns the proper type
* of the curve.

@ -3,6 +3,7 @@ package com.jme3.scene.plugins.blender.animations;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.SpatialTrack;
import com.jme3.animation.Track;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.curves.BezierCurve;
@ -28,23 +29,21 @@ public class Ipo {
public static final int AC_QUAT_Y = 27;
public static final int AC_QUAT_Z = 28;
/**
* A list of bezier curves for this interpolation object.
*/
/** A list of bezier curves for this interpolation object. */
private BezierCurve[] bezierCurves;
/**
* Each ipo contains one bone track.
*/
/** 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) {
public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis) {
this.bezierCurves = bezierCurves;
this.fixUpAxis = fixUpAxis;
}
/**
@ -93,26 +92,6 @@ public class Ipo {
return result;
}
/*
public void modifyTranslation(int frame, Vector3f translation) {
if (calculatedTrack != null) {
calculatedTrack.getTranslations()[frame].set(translation);
}
}
public void modifyRotation(int frame, Quaternion rotation) {
if (calculatedTrack != null) {
calculatedTrack.getRotations()[frame].set(rotation);
}
}
public void modifyScale(int frame, Vector3f scale) {
if (calculatedTrack != null) {
calculatedTrack.getScales()[frame].set(scale);
}
}
*/
/**
* This method calculates the value of the curves as a bone track between the specified frames.
* @param targetIndex
@ -141,40 +120,85 @@ public class Ipo {
float[] objectRotation = new float[3];
boolean bSpatialTrack = targetIndex < 0;
Vector3f[] scales = new Vector3f[framesAmount + 1];
float[] scale = new float[3];
float[] scale = new float[] {1.0f, 1.0f, 1.0f};
float degreeToRadiansFactor = FastMath.DEG_TO_RAD * 10;//the values in blender are divided by 10, so we need to mult it here
//calculating track data
for (int frame = startFrame; frame <= stopFrame; ++frame) {
int index = frame - startFrame;
times[index] = start + (frame - 1) * timeBetweenFrames;
for (int j = 0; j < bezierCurves.length; ++j) {
for (int j = 0; j < bezierCurves.length; ++j) {
double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
switch (bezierCurves[j].getType()) {
//LOCATION
case AC_LOC_X:
translation[0] = (float) value;
break;
case AC_LOC_Y:
if(fixUpAxis) {
translation[2] = (float) -value;
} else {
translation[1] = (float) value;
}
break;
case AC_LOC_Z:
translation[bezierCurves[j].getType() - 1] = (float) value;
translation[fixUpAxis ? 1 : 2] = (float) value;
break;
//ROTATION (used with object animation)
//the value here is in degrees divided by 10 (so in example: 9 = PI/2)
case OB_ROT_X:
objectRotation[0] = (float) value * degreeToRadiansFactor;
break;
case OB_ROT_Y:
if(fixUpAxis) {
objectRotation[2] = (float) -value * degreeToRadiansFactor;
} else {
objectRotation[1] = (float) value * degreeToRadiansFactor;
}
break;
case OB_ROT_Z:
objectRotation[bezierCurves[j].getType() - 7] = (float) value;
objectRotation[fixUpAxis ? 1 : 2] = (float) value * degreeToRadiansFactor;
break;
//SIZE
case AC_SIZE_X:
scale[0] = (float) value;
break;
case AC_SIZE_Y:
if(fixUpAxis) {
scale[2] = (float) value;
} else {
scale[1] = (float) value;
}
break;
case AC_SIZE_Z:
scale[bezierCurves[j].getType() - 13] = (float) value;
scale[fixUpAxis ? 1 : 2] = (float) value;
break;
//QUATERNION ROTATION (used with bone animation)
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:
quaternionRotation[bezierCurves[j].getType() - 26] = (float) value;
if(fixUpAxis) {
quaternionRotation[1] = (float) value;
} else {
quaternionRotation[2] = (float) value;
}
break;
default:
//TODO: error? info? warning?
throw new IllegalStateException("Unknown ipo curve type: " + bezierCurves[j].getType());
}
}
translations[index] = new Vector3f(translation[0], translation[1], translation[2]);

@ -1,5 +1,7 @@
package com.jme3.scene.plugins.blender.animations;
import java.util.List;
import com.jme3.animation.BoneTrack;
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext;
@ -7,7 +9,6 @@ import com.jme3.scene.plugins.blender.curves.BezierCurve;
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.List;
/**
* This class helps to compute values from interpolation curves for features like animation or constraint influence. The
@ -21,9 +22,11 @@ public class IpoHelper extends AbstractBlenderHelper {
* 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 IpoHelper(String blenderVersion) {
super(blenderVersion);
public IpoHelper(String blenderVersion, boolean fixUpAxis) {
super(blenderVersion, fixUpAxis);
}
/**
@ -52,7 +55,7 @@ public class IpoHelper extends AbstractBlenderHelper {
bezierCurves[frame++] = new BezierCurve(type, bezTriples, 2);
}
curves.clear();
result = new Ipo(bezierCurves);
result = new Ipo(bezierCurves, fixUpAxis);
blenderContext.addLoadedFeatures(ipoStructure.getOldMemoryAddress(), ipoStructure.getName(), ipoStructure, result);
}
return result;
@ -90,7 +93,7 @@ public class IpoHelper extends AbstractBlenderHelper {
* the constant value of this ipo
*/
public ConstIpo(float constValue) {
super(null);
super(null, false);
this.constValue = constValue;
}

@ -24,9 +24,11 @@ public class CameraHelper extends AbstractBlenderHelper {
* 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 CameraHelper(String blenderVersion) {
super(blenderVersion);
public CameraHelper(String blenderVersion, boolean fixUpAxis) {
super(blenderVersion, fixUpAxis);
}
/**

@ -0,0 +1,147 @@
package com.jme3.scene.plugins.blender.constraints;
import java.io.IOException;
import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.SpatialTrack;
import com.jme3.animation.Track;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.util.TempVars;
/**
* This class holds either the bone track or spatial track. Is made to improve
* code readability.
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */final class BlenderTrack implements Track {
/** The spatial track. */
private SpatialTrack spatialTrack;
/** The bone track. */
private BoneTrack boneTrack;
/**
* Constructs the object using spatial track (bone track is null).
*
* @param spatialTrack
* the spatial track
*/
public BlenderTrack(SpatialTrack spatialTrack) {
this.spatialTrack = spatialTrack;
}
/**
* Constructs the object using bone track (spatial track is null).
*
* @param spatialTrack
* the spatial track
*/
public BlenderTrack(BoneTrack boneTrack) {
this.boneTrack = boneTrack;
}
/**
* @return the stored track (either bone or spatial)
*/
public Track getTrack() {
return boneTrack != null ? boneTrack : spatialTrack;
}
/**
* @return the array of rotations of this track
*/
public Quaternion[] getRotations() {
if (boneTrack != null) {
return boneTrack.getRotations();
}
return spatialTrack.getRotations();
}
/**
* @return the array of scales for this track
*/
public Vector3f[] getScales() {
if (boneTrack != null) {
return boneTrack.getScales();
}
return spatialTrack.getScales();
}
/**
* @return the arrays of time for this track
*/
public float[] getTimes() {
if (boneTrack != null) {
return boneTrack.getTimes();
}
return spatialTrack.getTimes();
}
/**
* @return the array of translations of this track
*/
public Vector3f[] getTranslations() {
if (boneTrack != null) {
return boneTrack.getTranslations();
}
return spatialTrack.getTranslations();
}
/**
* Set the translations, rotations and scales for this bone track
*
* @param times
* a float array with the time of each frame
* @param translations
* the translation of the bone for each frame
* @param rotations
* the rotation of the bone for each frame
* @param scales
* the scale of the bone for each frame
*/
public void setKeyframes(float[] times, Vector3f[] translations,
Quaternion[] rotations, Vector3f[] scales) {
if (boneTrack != null) {
boneTrack.setKeyframes(times, translations, rotations, scales);
} else {
spatialTrack.setKeyframes(times, translations, rotations, scales);
}
}
@Override
public void write(JmeExporter ex) throws IOException {
}
@Override
public void read(JmeImporter im) throws IOException {
}
@Override
public void setTime(float time, float weight, AnimControl control,
AnimChannel channel, TempVars vars) {
if (boneTrack != null) {
boneTrack.setTime(time, weight, control, channel, vars);
} else {
spatialTrack.setTime(time, weight, control, channel, vars);
}
}
@Override
public float getLength() {
return spatialTrack == null ? boneTrack.getLength() : spatialTrack
.getLength();
}
@Override
public BlenderTrack clone() {
if (boneTrack != null) {
return new BlenderTrack(boneTrack.clone());
}
return new BlenderTrack(spatialTrack.clone());
}
}

@ -1,11 +1,12 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.animation.Bone;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.Skeleton;
import com.jme3.animation.SpatialTrack;
import com.jme3.animation.Track;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
import com.jme3.scene.plugins.blender.animations.Ipo;
@ -17,27 +18,29 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
/**
* The implementation of a constraint.
*
* @author Marcin Roguski
* @author Marcin Roguski (Kaelthas)
*/
public abstract class Constraint {
/** The name of this constraint. */
protected final String name;
/** The old memory address of the constraint's owner. */
protected Long boneOMA = -1L;
protected final Space ownerSpace;
protected final Space targetSpace;
/** The constraint's owner. */
protected final Feature owner;
/** The constraint's target. */
protected final Feature target;
/** The structure with constraint's data. */
protected final Structure data;
/** The ipo object defining influence. */
protected final Ipo ipo;
protected BlenderContext blenderContext;
/** The blender context. */
protected final BlenderContext blenderContext;
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -47,50 +50,46 @@ public abstract class Constraint {
* this exception is thrown when the blender file is somehow
* corrupted
*/
public Constraint(Structure constraintStructure, Long boneOMA,
public Constraint(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
this.blenderContext = blenderContext;
this.name = constraintStructure.getFieldValue("name").toString();
ConstraintType constraintType = ConstraintType.valueOf(((Number)constraintStructure.getFieldValue("type")).intValue());
if(constraintType != this.getType()) {
throw new IllegalStateException("Constraint structure does not match its type for constraint: " + name);
}
Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
if (pData.isNotNull()) {
data = pData.fetchData(blenderContext.getInputStream()).get(0);
Pointer pTar = (Pointer)data.getFieldValue("tar");
if(pTar!= null && pTar.isNotNull()) {
Structure targetStructure = pTar.fetchData(blenderContext.getInputStream()).get(0);
Long targetOMA = pTar.getOldMemoryAddress();
Space targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Spatial target = (Spatial) objectHelper.toObject(targetStructure, blenderContext);
this.target = new Feature(target, targetSpace, targetOMA, blenderContext);
} else {
this.target = null;
}
} else {
throw new BlenderFileException("The constraint has no data specified!");
}
this.boneOMA = boneOMA;
this.ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
this.targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
Space ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
Object owner = blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
if(owner instanceof Spatial) {
this.owner = new Feature((Spatial)owner, ownerSpace, ownerOMA, blenderContext);
} else {
this.owner = new Feature((Bone)owner, ownerSpace, ownerOMA, blenderContext);
}
this.ipo = influenceIpo;
}
/**
* This method returns the name of the constraint.
*
* @return the name of the constraint
* Bake the animation's constraints into its owner.
*/
public String getName() {
return name;
}
public abstract void bakeDynamic();
/**
* This method returns the old memoty address of the bone this constraint
* affects.
*
* @return the old memory address of the bone this constraint affects
* Bake the static constraints into its owner.
*/
public Long getBoneOMA() {
return boneOMA;
}
/**
* This method returns the type of the constraint.
*
* @return the type of the constraint
*/
public abstract ConstraintType getType();
public abstract void bakeStatic();
/**
* This method returns the bone traces for the bone that is affected by the given constraint.
@ -100,118 +99,24 @@ public abstract class Constraint {
* the bone animation that affects the skeleton
* @return the bone track for the bone that is being affected by the constraint
*/
protected Track getTrack(Animation animation, int targetIndex) {
if (boneOMA >= 0) {//bone animation
for (Track track : animation.getTracks()) {
if (((BoneTrack) track).getTargetBoneIndex() == targetIndex) {
return track;
protected BlenderTrack getTrack(Object owner, Skeleton skeleton, Animation animation) {
if(owner instanceof Bone) {
int boneIndex = skeleton.getBoneIndex((Bone) owner);
for (Track track : animation.getTracks()) {
if (((BoneTrack) track).getTargetBoneIndex() == boneIndex) {
return new BlenderTrack(((BoneTrack) track));
}
}
} else {//spatial animation
return animation.getTracks()[0];
}
return null;
}
/**
* This method returns the target or subtarget object (if specified).
* @param loadedFeatureDataType
* @return target or subtarget feature
* @throws BlenderFileException this exception is thrown if the blend file is somehow corrupted
*/
protected Object getTarget(LoadedFeatureDataType loadedFeatureDataType) throws BlenderFileException {
//load the feature through objectHelper, this way we are certain the object loads and has
//his own constraints applied to traces
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
//always load the target first
Long targetOMA = ((Pointer) data.getFieldValue("tar")).getOldMemoryAddress();
Structure objectStructure = blenderContext.getFileBlock(targetOMA).getStructure(blenderContext);
Object result = objectHelper.toObject(objectStructure, blenderContext);
//subtarget should be loaded alogn with target
Object subtarget = data.getFieldValue("subtarget");
String subtargetName = subtarget==null ? null : subtarget.toString();
if (subtargetName!=null && subtargetName.length() > 0) {
result = blenderContext.getLoadedFeature(subtargetName, loadedFeatureDataType);
}
return result;
}
/**
* This method returns target's object location.
* @return target's object location
*/
protected Vector3f getTargetLocation() {
Long targetOMA = ((Pointer) data.getFieldValue("tar")).getOldMemoryAddress();
Node targetObject = (Node) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
switch (targetSpace) {
case CONSTRAINT_SPACE_LOCAL:
return targetObject.getLocalTranslation();
case CONSTRAINT_SPACE_WORLD:
return targetObject.getWorldTranslation();
default:
throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString());
}
}
/**
* This method returns target's object location in the specified frame.
* @param frame
* the frame number
* @return target's object location
*/
protected Vector3f getTargetLocation(int frame) {
return this.getTargetLocation();//TODO: implement getting location in a specified frame
throw new IllegalStateException("Cannot find track for: " + owner);
} else {
return new BlenderTrack((SpatialTrack)animation.getTracks()[0]);
}
}
/**
* This method returns target's object rotation.
* @return target's object rotation
*/
protected Quaternion getTargetRotation() {
Long targetOMA = ((Pointer) data.getFieldValue("tar")).getOldMemoryAddress();
Node targetObject = (Node) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
switch (targetSpace) {
case CONSTRAINT_SPACE_LOCAL:
return targetObject.getLocalRotation();
case CONSTRAINT_SPACE_WORLD:
return targetObject.getWorldRotation();
default:
throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString());
}
}
/**
* This method returns target's object scale.
* @return target's object scale
*/
protected Vector3f getTargetScale() {
Long targetOMA = ((Pointer) data.getFieldValue("tar")).getOldMemoryAddress();
Node targetObject = (Node) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
switch (targetSpace) {
case CONSTRAINT_SPACE_LOCAL:
return targetObject.getLocalScale();
case CONSTRAINT_SPACE_WORLD:
return targetObject.getWorldScale();
default:
throw new IllegalStateException("Invalid space type for target object: " + targetSpace.toString());
}
}
/**
* This method affects the bone animation tracks for the given skeleton.
*
* @param animation
* the bone animation baked traces
* @param targetIndex
* the index of the constraint's target object
*/
public abstract void affectAnimation(Animation animation, int targetIndex);
/**
* The space of target or owner transformation.
*
* @author Marcin Roguski
* @author Marcin Roguski (Kaelthas)
*/
public static enum Space {
@ -227,16 +132,16 @@ public abstract class Constraint {
*/
public static Space valueOf(byte c) {
switch (c) {
case 0:
return CONSTRAINT_SPACE_WORLD;
case 1:
return CONSTRAINT_SPACE_LOCAL;
case 2:
return CONSTRAINT_SPACE_POSE;
case 3:
return CONSTRAINT_SPACE_PARLOCAL;
default:
return CONSTRAINT_SPACE_INVALID;
case 0:
return CONSTRAINT_SPACE_WORLD;
case 1:
return CONSTRAINT_SPACE_LOCAL;
case 2:
return CONSTRAINT_SPACE_POSE;
case 3:
return CONSTRAINT_SPACE_PARLOCAL;
default:
return CONSTRAINT_SPACE_INVALID;
}
}
}

@ -1,6 +1,5 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -20,7 +19,7 @@ import java.util.logging.Logger;
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -30,19 +29,20 @@ import java.util.logging.Logger;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintAction(Structure constraintStructure, Long boneOMA,
public ConstraintAction(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
public void bakeDynamic() {
// TODO: implement 'Action' constraint
LOGGER.log(Level.WARNING, "'Action' constraint NOT implemented!");
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_ACTION;
public void bakeStatic() {
// TODO: implement 'Action' constraint
LOGGER.log(Level.WARNING, "'Action' constraint NOT implemented!");
}
}

@ -1,6 +1,5 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -20,7 +19,7 @@ import java.util.logging.Logger;
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -30,19 +29,20 @@ import java.util.logging.Logger;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintChildOf(Structure constraintStructure, Long boneOMA,
public ConstraintChildOf(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
public void bakeDynamic() {
// TODO: implement ChildOf constraint
LOGGER.log(Level.WARNING, "ChildOf constraint NOT implemented!");
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_CHILDOF;
public void bakeStatic() {
// TODO: implement ChildOf constraint
LOGGER.log(Level.WARNING, "ChildOf constraint NOT implemented!");
}
}

@ -1,6 +1,5 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -20,7 +19,7 @@ import java.util.logging.Logger;
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -30,20 +29,21 @@ import java.util.logging.Logger;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintClampTo(Structure constraintStructure, Long boneOMA,
public ConstraintClampTo(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext)
throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
public void bakeDynamic() {
//TODO: implement when curves are implemented
LOGGER.log(Level.INFO, "'Clamp to' not yet implemented! Curves not yet implemented!", this.getName());
LOGGER.log(Level.INFO, "'Clamp to' not yet implemented! Curves not yet implemented!", name);
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_CLAMPTO;
public void bakeStatic() {
//TODO: implement when curves are implemented
LOGGER.log(Level.INFO, "'Clamp to' not yet implemented! Curves not yet implemented!", name);
}
}

@ -0,0 +1,49 @@
package com.jme3.scene.plugins.blender.constraints;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* The damp track constraint. Available for blender 2.50+.
* @author Marcin Roguski (Kaelthas)
*/
/*package*/ class ConstraintDampTrack extends Constraint {
private static final Logger LOGGER = Logger.getLogger(ConstraintDampTrack.class.getName());
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
* @param blenderContext
* the blender context
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintDampTrack(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
// TODO Auto-generated constructor stub
}
@Override
public void bakeDynamic() {
// TODO Auto-generated method stub
LOGGER.log(Level.WARNING, "'Damp Track' constraint NOT implemented!");
}
@Override
public void bakeStatic() {
// TODO Auto-generated method stub
LOGGER.log(Level.WARNING, "'Damp Track' constraint NOT implemented!");
}
}

@ -1,12 +1,15 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.Bone;
import com.jme3.math.Matrix4f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.ogre.AnimData;
/**
* This class represents 'Dist limit' constraint type in blender.
@ -17,12 +20,15 @@ import com.jme3.scene.plugins.blender.file.Structure;
private static final int LIMITDIST_OUTSIDE = 1;
private static final int LIMITDIST_ONSURFACE = 2;
/**
protected int mode;
protected float dist;
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -32,52 +38,89 @@ import com.jme3.scene.plugins.blender.file.Structure;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintDistLimit(Structure constraintStructure, Long boneOMA,
public ConstraintDistLimit(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
mode = ((Number) data.getFieldValue("mode")).intValue();
dist = ((Number) data.getFieldValue("dist")).floatValue();
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
Vector3f targetLocation = this.getTargetLocation();
BoneTrack boneTrack = (BoneTrack) this.getTrack(animation, targetIndex);
if (boneTrack != null) {
//TODO: target vertex group !!!
float dist = ((Number) data.getFieldValue("dist")).floatValue();
int mode = ((Number) data.getFieldValue("mode")).intValue();
int maxFrames = boneTrack.getTimes().length;
Vector3f[] translations = boneTrack.getTranslations();
for (int frame = 0; frame < maxFrames; ++frame) {
Vector3f v = translations[frame].subtract(targetLocation);
float currentDistance = v.length();
float influence = ipo.calculateValue(frame);
float modifier = 0.0f;
switch (mode) {
case LIMITDIST_INSIDE:
if (currentDistance >= dist) {
modifier = (dist - currentDistance) / currentDistance;
}
break;
case LIMITDIST_ONSURFACE:
modifier = (dist - currentDistance) / currentDistance;
break;
case LIMITDIST_OUTSIDE:
if (currentDistance <= dist) {
modifier = (dist - currentDistance) / currentDistance;
}
break;
default:
throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
public void bakeDynamic() {
AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if(animData != null) {
Object owner = this.owner.getObject();
if(owner instanceof Spatial) {
Vector3f targetLocation = ((Spatial) owner).getWorldTranslation();
for(Animation animation : animData.anims) {
BlenderTrack blenderTrack = this.getTrack(owner, animData.skeleton, animation);
int maxFrames = blenderTrack.getTimes().length;
Vector3f[] translations = blenderTrack.getTranslations();
for (int frame = 0; frame < maxFrames; ++frame) {
Vector3f v = translations[frame].subtract(targetLocation);
this.distLimit(v, targetLocation, ipo.calculateValue(frame));
translations[frame].addLocal(v);
}
blenderTrack.setKeyframes(blenderTrack.getTimes(), translations, blenderTrack.getRotations(), blenderTrack.getScales());
}
translations[frame].addLocal(v.multLocal(modifier * influence));
}
boneTrack.setKeyframes(boneTrack.getTimes(), translations, boneTrack.getRotations(), boneTrack.getScales());
}
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_DISTLIMIT;
public 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) {
((Spatial) owner).setLocalTranslation(m.mult(ownerLocation));
} else {
((Bone) owner).setBindTransforms(m.mult(ownerLocation), ((Bone) owner).getLocalRotation(), ((Bone) owner).getLocalScale());
}
}
/**
*
* @param currentLocation
* @param targetLocation
* @param influence
*/
private void distLimit(Vector3f currentLocation, Vector3f targetLocation, float influence) {
Vector3f v = currentLocation.subtract(targetLocation);
float currentDistance = v.length();
switch (mode) {
case LIMITDIST_INSIDE:
if (currentDistance >= dist) {
v.normalizeLocal();
v.multLocal(dist + (currentDistance - dist) * (1.0f - influence));
currentLocation.set(v.addLocal(targetLocation));
}
break;
case LIMITDIST_ONSURFACE:
if (currentDistance > dist) {
v.normalizeLocal();
v.multLocal(dist + (currentDistance - dist) * (1.0f - influence));
currentLocation.set(v.addLocal(targetLocation));
} else if(currentDistance < dist) {
v.normalizeLocal().multLocal(dist * influence);
currentLocation.set(targetLocation.add(v));
}
break;
case LIMITDIST_OUTSIDE:
if (currentDistance <= dist) {
v = targetLocation.subtract(currentLocation).normalizeLocal().multLocal(dist * influence);
currentLocation.set(targetLocation.add(v));
}
break;
default:
throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
}
}
}

@ -1,78 +0,0 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* A factory class to create new instances of constraints depending on the type from the constraint's structure.
* This class has a package scope.
* @author Marcin Roguski (Kaelthas)
*/
/*package*/ final class ConstraintFactory {
/**
* This method creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
* @param blenderContext
* the blender context
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow
* corrupted
*/
public static Constraint createConstraint(Structure constraintStructure, Long boneOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
int type = ((Number)constraintStructure.getFieldValue("type")).intValue();
ConstraintType constraintType = ConstraintType.valueOf(type);
switch(constraintType) {
case CONSTRAINT_TYPE_ACTION:
return new ConstraintAction(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_CHILDOF:
return new ConstraintChildOf(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_CLAMPTO:
return new ConstraintClampTo(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_DISTLIMIT:
return new ConstraintDistLimit(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_FOLLOWPATH:
return new ConstraintFollowPath(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_KINEMATIC:
return new ConstraintInverseKinematics(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_LOCKTRACK:
return new ConstraintLockTrack(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_LOCLIKE:
return new ConstraintLocLike(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_LOCLIMIT:
return new ConstraintLocLimit(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_MINMAX:
return new ConstraintMinMax(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_NULL:
return new ConstraintNull(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_PYTHON:
return new ConstraintPython(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_RIGIDBODYJOINT:
return new ConstraintRigidBodyJoint(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_ROTLIKE:
return new ConstraintRotLike(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_ROTLIMIT:
return new ConstraintRotLimit(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_SHRINKWRAP:
return new ConstraintShrinkWrap(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_SIZELIKE:
return new ConstraintSizeLike(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_SIZELIMIT:
return new ConstraintSizeLimit(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_STRETCHTO:
return new ConstraintStretchTo(constraintStructure, boneOMA, influenceIpo, blenderContext);
case CONSTRAINT_TYPE_TRANSFORM:
return new ConstraintTransform(constraintStructure, boneOMA, influenceIpo, blenderContext);
default:
throw new IllegalStateException("Unknown constraint type: " + constraintType);
}
}
}

@ -1,6 +1,5 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -20,7 +19,7 @@ import java.util.logging.Logger;
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -30,19 +29,20 @@ import java.util.logging.Logger;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintFollowPath(Structure constraintStructure, Long boneOMA,
public ConstraintFollowPath(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
public void bakeDynamic() {
//TODO: implement when curves are implemented
LOGGER.log(Level.INFO, "'Follow path' not implemented! Curves not yet implemented!");
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_FOLLOWPATH;
public void bakeStatic() {
//TODO: implement when curves are implemented
LOGGER.log(Level.INFO, "'Follow path' not implemented! Curves not yet implemented!");
}
}

@ -1,5 +1,12 @@
package com.jme3.scene.plugins.blender.constraints;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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;
@ -7,19 +14,42 @@ 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;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
/**
* This class should be used for constraint calculations.
* @author Marcin Roguski
* @author Marcin Roguski (Kaelthas)
*/
public class ConstraintHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());
private static final Map<String, Class<? extends Constraint>> constraintClasses = new HashMap<String, Class<? extends Constraint>>(22);
static {
constraintClasses.put("bActionConstraint", ConstraintAction.class);
constraintClasses.put("bChildOfConstraint", ConstraintChildOf.class);
constraintClasses.put("bClampToConstraint", ConstraintClampTo.class);
constraintClasses.put("bDistLimitConstraint", ConstraintDistLimit.class);
constraintClasses.put("bFollowPathConstraint", ConstraintFollowPath.class);
constraintClasses.put("bKinematicConstraint", ConstraintInverseKinematics.class);
constraintClasses.put("bLockTrackConstraint", ConstraintLockTrack.class);
constraintClasses.put("bLocateLikeConstraint", ConstraintLocLike.class);
constraintClasses.put("bLocLimitConstraint", ConstraintLocLimit.class);
constraintClasses.put("bMinMaxConstraint", ConstraintMinMax.class);
constraintClasses.put("bNullConstraint", ConstraintNull.class);
constraintClasses.put("bPythonConstraint", ConstraintPython.class);
constraintClasses.put("bRigidBodyJointConstraint", ConstraintRigidBodyJoint.class);
constraintClasses.put("bRotateLikeConstraint", ConstraintRotLike.class);
constraintClasses.put("bShrinkWrapConstraint", ConstraintShrinkWrap.class);
constraintClasses.put("bSizeLikeConstraint", ConstraintSizeLike.class);
constraintClasses.put("bSizeLimitConstraint", ConstraintSizeLimit.class);
constraintClasses.put("bStretchToConstraint", ConstraintStretchTo.class);
constraintClasses.put("bTransformConstraint", ConstraintTransform.class);
constraintClasses.put("bRotLimitConstraint", ConstraintRotLimit.class);
//Blender 2.50+
constraintClasses.put("bSplineIKConstraint", ConstraintSplineInverseKinematic.class);
constraintClasses.put("bDampTrackConstraint", ConstraintDampTrack.class);
constraintClasses.put("bPivotConstraint", ConstraintDampTrack.class);
}
/**
* Helper constructor. It's main task is to generate the affection functions. These functions are common to all
* ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall
@ -27,27 +57,25 @@ public class ConstraintHelper extends AbstractBlenderHelper {
* 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 ConstraintHelper(String blenderVersion, BlenderContext blenderContext) {
super(blenderVersion);
public ConstraintHelper(String blenderVersion, BlenderContext blenderContext, boolean fixUpAxis) {
super(blenderVersion, fixUpAxis);
}
/**
* This method reads constraints for for the given structure. The constraints are loaded only once for object/bone.
* @param ownerOMA
* the owner's old memory address
* This method reads constraints for for the given structure. The
* constraints are loaded only once for object/bone.
*
* @param objectStructure
* the structure we read constraint's for
* the structure we read constraint's for
* @param blenderContext
* the blender context
* the blender context
* @throws BlenderFileException
*/
public Map<Long, List<Constraint>> loadConstraints(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
if (blenderVersion >= 250) {//TODO
LOGGER.warning("Loading of constraints not yet implemented for version 2.5x !");
return new HashMap<Long, List<Constraint>>(0);
}
public void loadConstraints(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.fine("Loading constraints.");
// reading influence ipos for the constraints
IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
Map<String, Map<String, Ipo>> constraintsIpos = new HashMap<String, Map<String, Ipo>>();
@ -75,10 +103,8 @@ public class ConstraintHelper extends AbstractBlenderHelper {
}
}
Map<Long, List<Constraint>> result = new HashMap<Long, List<Constraint>>();
//loading constraints connected with the object's bones
Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");//TODO: what if the object has two armatures ????
Pointer pPose = (Pointer) objectStructure.getFieldValue("pose");
if (pPose.isNotNull()) {
List<Structure> poseChannels = ((Structure) pPose.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("chanbase")).evaluateListBase(blenderContext);
for (Structure poseChannel : poseChannels) {
@ -96,40 +122,78 @@ public class ConstraintHelper extends AbstractBlenderHelper {
float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
ipo = ipoHelper.createIpo(enforce);
}
constraintsList.add(ConstraintFactory.createConstraint(constraint, boneOMA, ipo, blenderContext));
constraintsList.add(this.createConstraint(constraint, boneOMA, ipo, blenderContext));
}
result.put(boneOMA, constraintsList);
blenderContext.addConstraints(boneOMA, constraintsList);
}
}
// TODO: reading constraints for objects (implement when object's animation will be available)
List<Structure> constraintChannels = ((Structure)objectStructure.getFieldValue("constraintChannels")).evaluateListBase(blenderContext);
for(Structure constraintChannel : constraintChannels) {
System.out.println(constraintChannel);
}
//loading constraints connected with the object itself (TODO: test this)
if(!result.containsKey(objectStructure.getOldMemoryAddress())) {
List<Structure> constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(blenderContext);
List<Constraint> constraintsList = new ArrayList<Constraint>(constraints.size());
//loading constraints connected with the object itself
List<Structure> constraints = ((Structure)objectStructure.getFieldValue("constraints")).evaluateListBase(blenderContext);
List<Constraint> constraintsList = new ArrayList<Constraint>(constraints.size());
for(Structure constraint : constraints) {
String constraintName = constraint.getFieldValue("name").toString();
String objectName = objectStructure.getName();
for(Structure constraint : constraints) {
String constraintName = constraint.getFieldValue("name").toString();
String objectName = objectStructure.getName();
Map<String, Ipo> objectConstraintsIpos = constraintsIpos.get(objectName);
Ipo ipo = objectConstraintsIpos!=null ? objectConstraintsIpos.get(constraintName) : null;
if (ipo == null) {
float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
ipo = ipoHelper.createIpo(enforce);
}
constraintsList.add(ConstraintFactory.createConstraint(constraint, null, ipo, blenderContext));
Map<String, Ipo> objectConstraintsIpos = constraintsIpos.get(objectName);
Ipo ipo = objectConstraintsIpos!=null ? objectConstraintsIpos.get(constraintName) : null;
if (ipo == null) {
float enforce = ((Number) constraint.getFieldValue("enforce")).floatValue();
ipo = ipoHelper.createIpo(enforce);
}
constraintsList.add(this.createConstraint(constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext));
}
blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList);
}
/**
* This method creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param ownerOMA
* the old memory address of the constraint's owner
* @param influenceIpo
* the ipo curve of the influence factor
* @param blenderContext
* the blender context
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow
* corrupted
*/
protected Constraint createConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo,
BlenderContext blenderContext) throws BlenderFileException {
String constraintClassName = this.getConstraintClassName(constraintStructure, blenderContext);
Class<? extends Constraint> constraintClass = constraintClasses.get(constraintClassName);
if(constraintClass != null) {
try {
return (Constraint) constraintClass.getDeclaredConstructors()[0].newInstance(constraintStructure, ownerOMA, influenceIpo,
blenderContext);
} catch (IllegalArgumentException e) {
throw new BlenderFileException(e.getLocalizedMessage(), e);
} catch (SecurityException e) {
throw new BlenderFileException(e.getLocalizedMessage(), e);
} catch (InstantiationException e) {
throw new BlenderFileException(e.getLocalizedMessage(), e);
} catch (IllegalAccessException e) {
throw new BlenderFileException(e.getLocalizedMessage(), e);
} catch (InvocationTargetException e) {
throw new BlenderFileException(e.getLocalizedMessage(), e);
}
result.put(objectStructure.getOldMemoryAddress(), constraintsList);
blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList);
} else {
throw new BlenderFileException("Unknown constraint type: " + constraintClassName);
}
}
protected String getConstraintClassName(Structure constraintStructure, BlenderContext blenderContext) throws BlenderFileException {
Pointer pData = (Pointer)constraintStructure.getFieldValue("data");
if(pData.isNotNull()) {
Structure data = pData.fetchData(blenderContext.getInputStream()).get(0);
return data.getType();
}
return result;
return constraintStructure.getType();
}
@Override

@ -1,7 +1,9 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.animation.Skeleton;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.CalculationBone;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
@ -20,7 +22,7 @@ import java.util.logging.Logger;
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -31,23 +33,30 @@ import java.util.logging.Logger;
* corrupted
*/
public ConstraintInverseKinematics(Structure constraintStructure,
Long boneOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
public void bakeDynamic() {
// try {
// IK solver is only attached to bones
// Bone ownerBone = (Bone) blenderContext.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE);
//
// // get the target point
// Bone ownerBone = (Bone) blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
// AnimData animData = blenderContext.getAnimData(ownerOMA);
// if(animData == null) {
//TODO: to nie mo¿e byæ null, utworzyæ dane bez ruchu, w zale¿noœci czy target siê rusza
// }
//prepare a list of all parents of this bone
// CalculationBone[] bones = this.getBonesToCalculate(skeleton, boneAnimation);
// get the target point
// Object targetObject = this.getTarget(LoadedFeatureDataType.LOADED_FEATURE);
// Vector3f pt = null;// Point Target
// if (targetObject instanceof Bone) {
// pt = ((Bone) targetObject).getModelSpacePosition();
// } else if (targetObject instanceof Node) {
// pt = ((Node) targetObject).getWorldTranslation();
// } else if (targetObject instanceof Spatial) {
// pt = ((Spatial) targetObject).getWorldTranslation();
// } else if (targetObject instanceof Skeleton) {
// Structure armatureNodeStructure = (Structure) this.getTarget(LoadedFeatureDataType.LOADED_STRUCTURE);
// ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
@ -106,7 +115,7 @@ import java.util.logging.Logger;
// for (CalculationBone bone : bones) {
// bone.applyCalculatedTracks();
// }
//
// System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
// for (int i = 0; i < bones.length; ++i) {
// System.out.println(Arrays.toString(bones[i].track.getTranslations()));
@ -118,40 +127,42 @@ import java.util.logging.Logger;
// }
}
// /**
// * This method returns bones used for rotation calculations.
// * @param bone
// * the bone to which the constraint is applied
// * @param skeleton
// * the skeleton owning the bone and its ancestors
// * @param boneAnimation
// * the bone animation data that stores the traces for the skeleton's bones
// * @return a list of bones to imitate the bone's movement during IK solving
// */
// private CalculationBone[] getBonesToCalculate(Bone bone, Skeleton skeleton, Animation boneAnimation) {
@Override
public void bakeStatic() {
// TODO Auto-generated method stub
}
/**
* This method returns bones used for rotation calculations.
* @param bone
* the bone to which the constraint is applied
* @param skeleton
* the skeleton owning the bone and its ancestors
* @param boneAnimation
* the bone animation data that stores the traces for the skeleton's bones
* @return a list of bones to imitate the bone's movement during IK solving
*/
private CalculationBone[] getBonesToCalculate(Skeleton skeleton, Animation boneAnimation) {
// Bone ownerBone = (Bone) blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
// List<CalculationBone> bonesList = new ArrayList<CalculationBone>();
// Bone currentBone = bone;
// do {
// bonesList.add(new CalculationBone(currentBone, 1));
// int boneIndex = skeleton.getBoneIndex(currentBone);
// bonesList.add(new CalculationBone(ownerBone, 1));
// int boneIndex = skeleton.getBoneIndex(ownerBone);
// for (int i = 0; i < boneAnimation.getTracks().length; ++i) {
// if (boneAnimation.getTracks()[i].getTargetBoneIndex() == boneIndex) {
// bonesList.add(new CalculationBone(currentBone, boneAnimation.getTracks()[i]));
// if (((BoneTrack[])boneAnimation.getTracks())[i].getTargetBoneIndex() == boneIndex) {
// bonesList.add(new CalculationBone(ownerBone, (BoneTrack)boneAnimation.getTracks()[i]));
// break;
// }
// }
// currentBone = currentBone.getParent();
// } while (currentBone != null);
// ownerBone = ownerBone.getParent();
// } while (ownerBone != null);
// //attaching children
// CalculationBone[] result = bonesList.toArray(new CalculationBone[bonesList.size()]);
// for (int i = result.length - 1; i > 0; --i) {
// result[i].attachChild(result[i - 1]);
// }
// return result;
// }
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_KINEMATIC;
return null;
}
}

@ -1,12 +1,13 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.animation.BoneTrack;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.ogre.AnimData;
/**
* This class represents 'Loc like' constraint type in blender.
@ -16,19 +17,20 @@ import com.jme3.scene.plugins.blender.file.Structure;
private static final int LOCLIKE_X = 0x01;
private static final int LOCLIKE_Y = 0x02;
private static final int LOCLIKE_Z = 0x04;
/* LOCLIKE_TIP is a depreceated option... use headtail=1.0f instead */
//protected static final int LOCLIKE_TIP = 0x08;
//protected static final int LOCLIKE_TIP = 0x08;//this is deprecated in blender
private static final int LOCLIKE_X_INVERT = 0x10;
private static final int LOCLIKE_Y_INVERT = 0x20;
private static final int LOCLIKE_Z_INVERT = 0x40;
private static final int LOCLIKE_OFFSET = 0x80;
protected int flag;
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -38,49 +40,83 @@ import com.jme3.scene.plugins.blender.file.Structure;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintLocLike(Structure constraintStructure, Long boneOMA,
public ConstraintLocLike(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
flag = ((Number) data.getFieldValue("flag")).intValue();
if(blenderContext.getBlenderKey().isFixUpAxis()) {
//swapping Y and X limits flag in the bitwise flag
int y = flag & LOCLIKE_Y;
int invY = flag & LOCLIKE_Y_INVERT;
int z = flag & LOCLIKE_Z;
int invZ = flag & LOCLIKE_Z_INVERT;
flag &= LOCLIKE_X | LOCLIKE_X_INVERT | LOCLIKE_OFFSET;//clear the other flags to swap them
flag |= y << 2;
flag |= invY << 2;
flag |= z >> 2;
flag |= invZ >> 2;
}
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
BoneTrack track = (BoneTrack) this.getTrack(animation, targetIndex);
if (track != null) {
Vector3f targetLocation = this.getTargetLocation();
int flag = ((Number) data.getFieldValue("flag")).intValue();
Vector3f[] translations = track.getTranslations();
int maxFrames = translations.length;
for (int frame = 0; frame < maxFrames; ++frame) {
Vector3f offset = Vector3f.ZERO;
if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original location to the copied location
offset = translations[frame].clone();
public void bakeDynamic() {
AnimData animData = blenderContext.getAnimData(owner.getOma());
if(animData != null) {
Object owner = this.owner.getObject();
Transform targetTransform = this.target.getTransform();
for(Animation animation : animData.anims) {
BlenderTrack blenderTrack = this.getTrack(owner, animData.skeleton, animation);
Vector3f[] translations = blenderTrack.getTranslations();
int maxFrames = translations.length;
for (int frame = 0; frame < maxFrames; ++frame) {
this.locLike(translations[frame], targetTransform.getTranslation(), ipo.calculateValue(frame));
}
if ((flag & LOCLIKE_X) != 0) {
translations[frame].x = targetLocation.x;
if ((flag & LOCLIKE_X_INVERT) != 0) {
translations[frame].x = -translations[frame].x;
}
} else if ((flag & LOCLIKE_Y) != 0) {
translations[frame].y = targetLocation.y;
if ((flag & LOCLIKE_Y_INVERT) != 0) {
translations[frame].y = -translations[frame].y;
}
} else if ((flag & LOCLIKE_Z) != 0) {
translations[frame].z = targetLocation.z;
if ((flag & LOCLIKE_Z_INVERT) != 0) {
translations[frame].z = -translations[frame].z;
}
}
translations[frame].addLocal(offset);//TODO: ipo influence
blenderTrack.setKeyframes(blenderTrack.getTimes(), translations, blenderTrack.getRotations(), blenderTrack.getScales());
}
track.setKeyframes(track.getTimes(), translations, track.getRotations(), track.getScales());
}
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_LOCLIKE;
public void bakeStatic() {
Transform targetTransform = this.target.getTransform();
Transform ownerTransform = this.owner.getTransform();
Vector3f ownerLocation = ownerTransform.getTranslation();
this.locLike(ownerLocation, targetTransform.getTranslation(), ipo.calculateValue(0));
this.owner.applyTransform(ownerTransform);
}
private void locLike(Vector3f ownerLocation, Vector3f targetLocation, float influence) {
Vector3f startLocation = ownerLocation.clone();
Vector3f offset = Vector3f.ZERO;
if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original location to the copied location
offset = startLocation;
}
if ((flag & LOCLIKE_X) != 0) {
ownerLocation.x = targetLocation.x;
if ((flag & LOCLIKE_X_INVERT) != 0) {
ownerLocation.x = -ownerLocation.x;
}
}
if ((flag & LOCLIKE_Y) != 0) {
ownerLocation.y = targetLocation.y;
if ((flag & LOCLIKE_Y_INVERT) != 0) {
ownerLocation.y = -ownerLocation.y;
}
}
if ((flag & LOCLIKE_Z) != 0) {
ownerLocation.z = targetLocation.z;
if ((flag & LOCLIKE_Z_INVERT) != 0) {
ownerLocation.z = -ownerLocation.z;
}
}
ownerLocation.addLocal(offset);
if(influence < 1.0f) {
startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
ownerLocation.addLocal(startLocation);
}
}
}

@ -1,12 +1,14 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.Track;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.ogre.AnimData;
/**
* This class represents 'Loc limit' constraint type in blender.
@ -20,12 +22,15 @@ import com.jme3.scene.plugins.blender.file.Structure;
private static final int LIMIT_ZMIN = 0x10;
private static final int LIMIT_ZMAX = 0x20;
protected float[][] limits = new float[3][2];
protected int flag;
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -35,63 +40,101 @@ import com.jme3.scene.plugins.blender.file.Structure;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintLocLimit(Structure constraintStructure, Long boneOMA,
public ConstraintLocLimit(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
flag = ((Number) data.getFieldValue("flag")).intValue();
if(blenderContext.getBlenderKey().isFixUpAxis()) {
limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
limits[2][0] = -((Number) data.getFieldValue("ymin")).floatValue();
limits[2][1] = -((Number) data.getFieldValue("ymax")).floatValue();
limits[1][0] = ((Number) data.getFieldValue("zmin")).floatValue();
limits[1][1] = ((Number) data.getFieldValue("zmax")).floatValue();
//swapping Y and X limits flag in the bitwise flag
int ymin = flag & LIMIT_YMIN;
int ymax = flag & LIMIT_YMAX;
int zmin = flag & LIMIT_ZMIN;
int zmax = flag & LIMIT_ZMAX;
flag &= LIMIT_XMIN | LIMIT_XMAX;//clear the other flags to swap them
flag |= ymin << 2;
flag |= ymax << 2;
flag |= zmin >> 2;
flag |= zmax >> 2;
} else {
limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
limits[1][0] = ((Number) data.getFieldValue("ymin")).floatValue();
limits[1][1] = ((Number) data.getFieldValue("ymax")).floatValue();
limits[2][0] = ((Number) data.getFieldValue("zmin")).floatValue();
limits[2][1] = ((Number) data.getFieldValue("zmax")).floatValue();
}
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
BoneTrack track = (BoneTrack) this.getTrack(animation, targetIndex);
if (track != null) {
int flag = ((Number) data.getFieldValue("flag")).intValue();
Vector3f[] translations = track.getTranslations();
int maxFrames = translations.length;
for (int frame = 0; frame < maxFrames; ++frame) {
float influence = ipo.calculateValue(frame);
if ((flag & LIMIT_XMIN) != 0) {
float xmin = ((Number) data.getFieldValue("xmin")).floatValue();
if (translations[frame].x < xmin) {
translations[frame].x -= (translations[frame].x - xmin) * influence;
}
}
if ((flag & LIMIT_XMAX) != 0) {
float xmax = ((Number) data.getFieldValue("xmax")).floatValue();
if (translations[frame].x > xmax) {
translations[frame].x -= (translations[frame].x - xmax) * influence;
}
}
if ((flag & LIMIT_YMIN) != 0) {
float ymin = ((Number) data.getFieldValue("ymin")).floatValue();
if (translations[frame].y < ymin) {
translations[frame].y -= (translations[frame].y - ymin) * influence;
}
public void bakeDynamic() {
Object owner = this.owner.getObject();
AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if(animData != null) {
for(Animation animation : animData.anims) {
BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
Vector3f[] translations = track.getTranslations();
int maxFrames = translations.length;
for (int frame = 0; frame < maxFrames; ++frame) {
this.locLimit(translations[frame], ipo.calculateValue(frame));
}
if ((flag & LIMIT_YMAX) != 0) {
float ymax = ((Number) data.getFieldValue("ymax")).floatValue();
if (translations[frame].y > ymax) {
translations[frame].y -= (translations[frame].y - ymax) * influence;
}
}
if ((flag & LIMIT_ZMIN) != 0) {
float zmin = ((Number) data.getFieldValue("zmin")).floatValue();
if (translations[frame].z < zmin) {
translations[frame].z -= (translations[frame].z - zmin) * influence;
}
}
if ((flag & LIMIT_ZMAX) != 0) {
float zmax = ((Number) data.getFieldValue("zmax")).floatValue();
if (translations[frame].z > zmax) {
translations[frame].z -= (translations[frame].z - zmax) * influence;
}
}//TODO: consider constraint space !!!
track.setKeyframes(track.getTimes(), translations, track.getRotations(), track.getScales());
translations = track.getTranslations();
animation.setTracks(new Track[] {track.getTrack()});
}
track.setKeyframes(track.getTimes(), translations, track.getRotations(), track.getScales());
}
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_LOCLIMIT;
public void bakeStatic() {
Transform ownerTransform = this.owner.getTransform();
Vector3f ownerLocation = ownerTransform.getTranslation();
this.locLimit(ownerLocation, ipo.calculateValue(0));
this.owner.applyTransform(ownerTransform);
}
/**
* This method modifies the given translation.
* @param translation the translation to be modified.
* @param influence the influence value
*/
private void locLimit(Vector3f translation, float influence) {
if ((flag & LIMIT_XMIN) != 0) {
if (translation.x < limits[0][0]) {
translation.x -= (translation.x - limits[0][0]) * influence;
}
}
if ((flag & LIMIT_XMAX) != 0) {
if (translation.x > limits[0][1]) {
translation.x -= (translation.x - limits[0][1]) * influence;
}
}
if ((flag & LIMIT_YMIN) != 0) {
if (translation.y < limits[1][0]) {
translation.y -= (translation.y - limits[1][0]) * influence;
}
}
if ((flag & LIMIT_YMAX) != 0) {
if (translation.y > limits[1][1]) {
translation.y -= (translation.y - limits[1][1]) * influence;
}
}
if ((flag & LIMIT_ZMIN) != 0) {
if (translation.z < limits[2][0]) {
translation.z -= (translation.z - limits[2][0]) * influence;
}
}
if ((flag & LIMIT_ZMAX) != 0) {
if (translation.z > limits[2][1]) {
translation.z -= (translation.z - limits[2][1]) * influence;
}
}
}
}

@ -1,6 +1,5 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -20,7 +19,7 @@ import java.util.logging.Logger;
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -30,20 +29,21 @@ import java.util.logging.Logger;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintLockTrack(Structure constraintStructure, Long boneOMA,
public ConstraintLockTrack(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext)
throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
public void bakeDynamic() {
// TODO: implement 'Lock track' constraint
LOGGER.log(Level.WARNING, "'Lock track' constraint NOT implemented!");
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_LOCKTRACK;
public void bakeStatic() {
// TODO: implement 'Lock track' constraint
LOGGER.log(Level.WARNING, "'Lock track' constraint NOT implemented!");
}
}

@ -1,6 +1,5 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -20,7 +19,7 @@ import java.util.logging.Logger;
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -30,19 +29,20 @@ import java.util.logging.Logger;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintMinMax(Structure constraintStructure, Long boneOMA,
public ConstraintMinMax(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
public void bakeDynamic() {
// TODO: implement 'Min max' constraint
LOGGER.log(Level.WARNING, "'Min max' constraint NOT implemented!");
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_MINMAX;
public void bakeStatic() {
// TODO: implement 'Min max' constraint
LOGGER.log(Level.WARNING, "'Min max' constraint NOT implemented!");
}
}

@ -1,6 +1,5 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -17,7 +16,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -27,17 +26,15 @@ import com.jme3.scene.plugins.blender.file.Structure;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintNull(Structure constraintStructure, Long boneOMA,
public ConstraintNull(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext)
throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {}
public void bakeDynamic() {}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_NULL;
}
public void bakeStatic() {}
}

@ -0,0 +1,50 @@
package com.jme3.scene.plugins.blender.constraints;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* The pivot constraint. Available for blender 2.50+.
* @author Marcin Roguski (Kaelthas)
*/
/*package*/ class ConstraintPivot extends Constraint {
private static final Logger LOGGER = Logger.getLogger(ConstraintPivot.class.getName());
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
* @param blenderContext
* the blender context
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintPivot(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo,
BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
// TODO Auto-generated constructor stub
}
@Override
public void bakeDynamic() {
// TODO Auto-generated method stub
LOGGER.log(Level.WARNING, "'Pivot' constraint NOT implemented!");
}
@Override
public void bakeStatic() {
// TODO Auto-generated method stub
LOGGER.log(Level.WARNING, "'Pivot' constraint NOT implemented!");
}
}

@ -1,6 +1,5 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -20,7 +19,7 @@ import java.util.logging.Logger;
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -30,19 +29,20 @@ import java.util.logging.Logger;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintPython(Structure constraintStructure, Long boneOMA,
public ConstraintPython(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
public void bakeDynamic() {
// TODO: implement 'Python' constraint
LOGGER.log(Level.WARNING, "'Python' constraint NOT implemented!");
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_PYTHON;
public void bakeStatic() {
// TODO: implement 'Python' constraint
LOGGER.log(Level.WARNING, "'Python' constraint NOT implemented!");
}
}

@ -1,6 +1,5 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -20,7 +19,7 @@ import java.util.logging.Logger;
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -31,18 +30,19 @@ import java.util.logging.Logger;
* corrupted
*/
public ConstraintRigidBodyJoint(Structure constraintStructure,
Long boneOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
public void bakeDynamic() {
// TODO: implement 'Rigid body joint' constraint
LOGGER.log(Level.WARNING, "'Rigid body joint' constraint NOT implemented!");
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_RIGIDBODYJOINT;
public void bakeStatic() {
// TODO: implement 'Rigid body joint' constraint
LOGGER.log(Level.WARNING, "'Rigid body joint' constraint NOT implemented!");
}
}

@ -1,12 +1,13 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.animation.BoneTrack;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.ogre.AnimData;
/**
* This class represents 'Rot like' constraint type in blender.
@ -21,12 +22,14 @@ import com.jme3.scene.plugins.blender.file.Structure;
private static final int ROTLIKE_Z_INVERT = 0x40;
private static final int ROTLIKE_OFFSET = 0x80;
/**
protected int flag;
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -36,52 +39,76 @@ import com.jme3.scene.plugins.blender.file.Structure;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintRotLike(Structure constraintStructure, Long boneOMA,
public ConstraintRotLike(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
flag = ((Number) data.getFieldValue("flag")).intValue();
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
BoneTrack track = (BoneTrack) this.getTrack(animation, targetIndex);
if (track != null) {
Quaternion targetRotation = this.getTargetRotation();
int flag = ((Number) data.getFieldValue("flag")).intValue();
float[] targetAngles = targetRotation.toAngles(null);
Quaternion[] rotations = track.getRotations();
int maxFrames = rotations.length;
for (int frame = 0; frame < maxFrames; ++frame) {
float[] angles = rotations[frame].toAngles(null);
Quaternion offset = Quaternion.IDENTITY;
if ((flag & ROTLIKE_OFFSET) != 0) {//we add the original rotation to the copied rotation
offset = rotations[frame].clone();
}
if ((flag & ROTLIKE_X) != 0) {
angles[0] = targetAngles[0];
if ((flag & ROTLIKE_X_INVERT) != 0) {
angles[0] = -angles[0];
}
} else if ((flag & ROTLIKE_Y) != 0) {
angles[1] = targetAngles[1];
if ((flag & ROTLIKE_Y_INVERT) != 0) {
angles[1] = -angles[1];
}
} else if ((flag & ROTLIKE_Z) != 0) {
angles[2] = targetAngles[2];
if ((flag & ROTLIKE_Z_INVERT) != 0) {
angles[2] = -angles[2];
}
public void bakeDynamic() {
AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if(animData != null) {
Object owner = this.owner.getObject();
Transform targetTransform = this.target.getTransform();
Quaternion targetRotation = targetTransform.getRotation();
for(Animation animation : animData.anims) {
BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
float[] targetAngles = targetRotation.toAngles(null);
Quaternion[] rotations = track.getRotations();
int maxFrames = rotations.length;
float[] angles = new float[3];
for (int frame = 0; frame < maxFrames; ++frame) {
rotations[frame].toAngles(angles);
this.rotLike(rotations[frame], angles, targetAngles, ipo.calculateValue(frame));
}
rotations[frame].fromAngles(angles).multLocal(offset);//TODO: ipo influence
track.setKeyframes(track.getTimes(), track.getTranslations(), rotations, track.getScales());
}
track.setKeyframes(track.getTimes(), track.getTranslations(), rotations, track.getScales());
}
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_ROTLIKE;
public void bakeStatic() {
Transform targetTransform = this.target.getTransform();
Transform ownerTransform = this.owner.getTransform();
Quaternion ownerRotation = ownerTransform.getRotation();
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) {
Quaternion startRotation = ownerRotation.clone();
Quaternion offset = Quaternion.IDENTITY;
if ((flag & ROTLIKE_OFFSET) != 0) {//we add the original rotation to the copied rotation
offset = startRotation;
}
if ((flag & ROTLIKE_X) != 0) {
ownerAngles[0] = targetAngles[0];
if ((flag & ROTLIKE_X_INVERT) != 0) {
ownerAngles[0] = -ownerAngles[0];
}
}
if ((flag & ROTLIKE_Y) != 0) {
ownerAngles[1] = targetAngles[1];
if ((flag & ROTLIKE_Y_INVERT) != 0) {
ownerAngles[1] = -ownerAngles[1];
}
}
if ((flag & ROTLIKE_Z) != 0) {
ownerAngles[2] = targetAngles[2];
if ((flag & ROTLIKE_Z_INVERT) != 0) {
ownerAngles[2] = -ownerAngles[2];
}
}
ownerRotation.fromAngles(ownerAngles).multLocal(offset);
if(influence < 1.0f) {
// startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
// ownerLocation.addLocal(startLocation);
//TODO
}
}
}

@ -1,13 +1,14 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.animation.BoneTrack;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.ogre.AnimData;
/**
* This class represents 'Rot limit' constraint type in blender.
@ -18,12 +19,15 @@ import com.jme3.scene.plugins.blender.file.Structure;
private static final int LIMIT_YROT = 0x02;
private static final int LIMIT_ZROT = 0x04;
protected float[][] limits = new float[3][2];
protected int flag;
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -33,62 +37,100 @@ import com.jme3.scene.plugins.blender.file.Structure;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintRotLimit(Structure constraintStructure, Long boneOMA,
public ConstraintRotLimit(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
if(blenderContext.getBlenderKey().isFixUpAxis()) {
limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue() * FastMath.DEG_TO_RAD;
limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue() * FastMath.DEG_TO_RAD;
limits[2][0] = -((Number) data.getFieldValue("ymin")).floatValue() * FastMath.DEG_TO_RAD;
limits[2][1] = -((Number) data.getFieldValue("ymax")).floatValue() * FastMath.DEG_TO_RAD;
limits[1][0] = ((Number) data.getFieldValue("zmin")).floatValue() * FastMath.DEG_TO_RAD;
limits[1][1] = ((Number) data.getFieldValue("zmax")).floatValue() * FastMath.DEG_TO_RAD;
} else {
limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue() * FastMath.DEG_TO_RAD;
limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue() * FastMath.DEG_TO_RAD;
limits[1][0] = ((Number) data.getFieldValue("ymin")).floatValue() * FastMath.DEG_TO_RAD;
limits[1][1] = ((Number) data.getFieldValue("ymax")).floatValue() * FastMath.DEG_TO_RAD;
limits[2][0] = ((Number) data.getFieldValue("zmin")).floatValue() * FastMath.DEG_TO_RAD;
limits[2][1] = ((Number) data.getFieldValue("zmax")).floatValue() * FastMath.DEG_TO_RAD;
}
flag = ((Number) data.getFieldValue("flag")).intValue();
if(blenderContext.getBlenderKey().isFixUpAxis()) {
//swapping Y and X limits flag in the bitwise flag
int limitY = flag & LIMIT_YROT;
int limitZ = flag & LIMIT_ZROT;
flag &= LIMIT_XROT;//clear the other flags to swap them
flag |= limitY << 1;
flag |= limitZ >> 1;
}
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
BoneTrack track = (BoneTrack) this.getTrack(animation, targetIndex);
if (track != null) {
int flag = ((Number) data.getFieldValue("flag")).intValue();
Quaternion[] rotations = track.getRotations();
int maxFrames = rotations.length;
for (int frame = 0; frame < maxFrames; ++frame) {
float[] angles = rotations[frame].toAngles(null);
float influence = ipo.calculateValue(frame);
if ((flag & LIMIT_XROT) != 0) {
float xmin = ((Number) data.getFieldValue("xmin")).floatValue() * FastMath.DEG_TO_RAD;
float xmax = ((Number) data.getFieldValue("xmax")).floatValue() * FastMath.DEG_TO_RAD;
float difference = 0.0f;
if (angles[0] < xmin) {
difference = (angles[0] - xmin) * influence;
} else if (angles[0] > xmax) {
difference = (angles[0] - xmax) * influence;
}
angles[0] -= difference;
}
if ((flag & LIMIT_YROT) != 0) {
float ymin = ((Number) data.getFieldValue("ymin")).floatValue() * FastMath.DEG_TO_RAD;
float ymax = ((Number) data.getFieldValue("ymax")).floatValue() * FastMath.DEG_TO_RAD;
float difference = 0.0f;
if (angles[1] < ymin) {
difference = (angles[1] - ymin) * influence;
} else if (angles[1] > ymax) {
difference = (angles[1] - ymax) * influence;
}
angles[1] -= difference;
public void bakeDynamic() {
AnimData animData = blenderContext.getAnimData(owner.getOma());
if(animData != null) {
Object owner = this.owner.getObject();
for(Animation animation : animData.anims) {
BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
Quaternion[] rotations = track.getRotations();
float[] angles = new float[3];
int maxFrames = rotations.length;
for (int frame = 0; frame < maxFrames; ++frame) {
rotations[frame].toAngles(angles);
this.rotLimit(angles, ipo.calculateValue(frame));
rotations[frame].fromAngles(angles);
}
if ((flag & LIMIT_ZROT) != 0) {
float zmin = ((Number) data.getFieldValue("zmin")).floatValue() * FastMath.DEG_TO_RAD;
float zmax = ((Number) data.getFieldValue("zmax")).floatValue() * FastMath.DEG_TO_RAD;
float difference = 0.0f;
if (angles[2] < zmin) {
difference = (angles[2] - zmin) * influence;
} else if (angles[2] > zmax) {
difference = (angles[2] - zmax) * influence;
}
angles[2] -= difference;
}
rotations[frame].fromAngles(angles);//TODO: consider constraint space !!!
track.setKeyframes(track.getTimes(), track.getTranslations(), rotations, track.getScales());
}
track.setKeyframes(track.getTimes(), track.getTranslations(), rotations, track.getScales());
}
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_ROTLIMIT;
public void bakeStatic() {
Transform ownerTransform = this.owner.getTransform();
float[] angles = ownerTransform.getRotation().toAngles(null);
this.rotLimit(angles, ipo.calculateValue(0));
ownerTransform.getRotation().fromAngles(angles);
this.owner.applyTransform(ownerTransform);
}
/**
* This method computes new constrained angles.
*
* @param angles
* angles to be altered
* @param influence
* the alteration influence
*/
private void rotLimit(float[] angles, float influence) {
if ((flag & LIMIT_XROT) != 0) {
float difference = 0.0f;
if (angles[0] < limits[0][0]) {
difference = (angles[0] - limits[0][0]) * influence;
} else if (angles[0] > limits[0][1]) {
difference = (angles[0] - limits[0][1]) * influence;
}
angles[0] -= difference;
}
if ((flag & LIMIT_YROT) != 0) {
float difference = 0.0f;
if (angles[1] < limits[1][0]) {
difference = (angles[1] - limits[1][0]) * influence;
} else if (angles[1] > limits[1][1]) {
difference = (angles[1] - limits[1][1]) * influence;
}
angles[1] -= difference;
}
if ((flag & LIMIT_ZROT) != 0) {
float difference = 0.0f;
if (angles[2] < limits[2][0]) {
difference = (angles[2] - limits[2][0]) * influence;
} else if (angles[2] > limits[2][1]) {
difference = (angles[2] - limits[2][1]) * influence;
}
angles[2] -= difference;
}
}
}

@ -1,7 +1,10 @@
package com.jme3.scene.plugins.blender.constraints;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import com.jme3.animation.Animation;
import com.jme3.animation.BoneTrack;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
@ -10,28 +13,23 @@ import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import com.jme3.scene.plugins.ogre.AnimData;
/**
* This class represents 'Shrink wrap' constraint type in blender.
* @author Marcin Roguski (Kaelthas)
*/
/*package*/ class ConstraintShrinkWrap extends Constraint {
private static final Logger LOGGER = Logger.getLogger(ConstraintShrinkWrap.class.getName());
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -41,30 +39,31 @@ import java.util.logging.Logger;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintShrinkWrap(Structure constraintStructure, Long boneOMA,
public ConstraintShrinkWrap(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
public void bakeDynamic() {
//loading mesh points (blender ensures that the target is a mesh-object)
List<Vector3f> pts = new ArrayList<Vector3f>();
try {
Node node = (Node)this.getTarget(LoadedFeatureDataType.LOADED_FEATURE);
for(Spatial spatial : node.getChildren()) {
if(spatial instanceof Geometry) {
Mesh mesh = ((Geometry) spatial).getMesh();
FloatBuffer floatBuffer = mesh.getFloatBuffer(Type.Position);
for(int i=0;i<floatBuffer.limit();i+=3) {
pts.add(new Vector3f(floatBuffer.get(i), floatBuffer.get(i + 1), floatBuffer.get(i + 2)));
}
Node target = (Node) this.target.getObject();
for(Spatial spatial : target.getChildren()) {
if(spatial instanceof Geometry) {
Mesh mesh = ((Geometry) spatial).getMesh();
FloatBuffer floatBuffer = mesh.getFloatBuffer(Type.Position);
for(int i=0;i<floatBuffer.limit();i+=3) {
pts.add(new Vector3f(floatBuffer.get(i), floatBuffer.get(i + 1), floatBuffer.get(i + 2)));
}
}
}
//modifying traces
BoneTrack track = (BoneTrack) this.getTrack(animation, targetIndex);
if (track != null) {
AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if(animData != null) {
Object owner = this.owner.getObject();
for(Animation animation : animData.anims) {
BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
Vector3f[] translations = track.getTranslations();
Quaternion[] rotations = track.getRotations();
int maxFrames = translations.length;
@ -86,13 +85,12 @@ import java.util.logging.Logger;
track.setKeyframes(track.getTimes(), translations, rotations, track.getScales());
}
} catch (BlenderFileException e) {
LOGGER.severe(e.getLocalizedMessage());
}
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_SHRINKWRAP;
public void bakeStatic() {
// TODO Auto-generated method stub
}
}

@ -1,12 +1,13 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.animation.BoneTrack;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.ogre.AnimData;
/**
* This class represents 'Size like' constraint type in blender.
@ -18,12 +19,14 @@ import com.jme3.scene.plugins.blender.file.Structure;
private static final int SIZELIKE_Z = 0x04;
private static final int LOCLIKE_OFFSET = 0x80;
protected int flag;
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -33,41 +36,63 @@ import com.jme3.scene.plugins.blender.file.Structure;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintSizeLike(Structure constraintStructure, Long boneOMA,
public ConstraintSizeLike(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
flag = ((Number) data.getFieldValue("flag")).intValue();
if(blenderContext.getBlenderKey().isFixUpAxis()) {
//swapping Y and X limits flag in the bitwise flag
int y = flag & SIZELIKE_Y;
int z = flag & SIZELIKE_Z;
flag &= SIZELIKE_X | LOCLIKE_OFFSET;//clear the other flags to swap them
flag |= y << 1;
flag |= z >> 1;
}
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
Vector3f targetScale = this.getTargetLocation();
BoneTrack track = (BoneTrack) this.getTrack(animation, targetIndex);
if (track != null) {
int flag = ((Number) data.getFieldValue("flag")).intValue();
Vector3f[] scales = track.getScales();
int maxFrames = scales.length;
for (int frame = 0; frame < maxFrames; ++frame) {
Vector3f offset = Vector3f.ZERO;
if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original scale to the copied scale
offset = scales[frame].clone();
public void bakeDynamic() {
AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if(animData != null) {
Object owner = this.owner.getObject();
Transform targetTransform = this.target.getTransform();
Vector3f targetScale = targetTransform.getScale();
for(Animation animation : animData.anims) {
BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
Vector3f[] scales = track.getScales();
int maxFrames = scales.length;
for (int frame = 0; frame < maxFrames; ++frame) {
this.sizeLike(scales[frame], targetScale, ipo.calculateValue(frame));
}
if ((flag & SIZELIKE_X) != 0) {
scales[frame].x = targetScale.x;
} else if ((flag & SIZELIKE_Y) != 0) {
scales[frame].y = targetScale.y;
} else if ((flag & SIZELIKE_Z) != 0) {
scales[frame].z = targetScale.z;
}
scales[frame].addLocal(offset);//TODO: ipo influence
//TODO: add or multiply???
track.setKeyframes(track.getTimes(), track.getTranslations(), track.getRotations(), scales);
}
track.setKeyframes(track.getTimes(), track.getTranslations(), track.getRotations(), scales);
}
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_SIZELIKE;
public void bakeStatic() {
Transform targetTransform = this.target.getTransform();
Transform ownerTransform = this.owner.getTransform();
this.sizeLike(ownerTransform.getScale(), targetTransform.getScale(), ipo.calculateValue(0));
this.owner.applyTransform(ownerTransform);
}
private void sizeLike(Vector3f ownerScale, Vector3f targetScale, float influence) {
Vector3f offset = Vector3f.ZERO;
if ((flag & LOCLIKE_OFFSET) != 0) {//we add the original scale to the copied scale
offset = ownerScale.clone();
}
if ((flag & SIZELIKE_X) != 0) {
ownerScale.x = targetScale.x * influence + (1.0f - influence) * ownerScale.x;
}
if ((flag & SIZELIKE_Y) != 0) {
ownerScale.y = targetScale.y * influence + (1.0f - influence) * ownerScale.y;
}
if ((flag & SIZELIKE_Z) != 0) {
ownerScale.z = targetScale.z * influence + (1.0f - influence) * ownerScale.z;
}
ownerScale.addLocal(offset);
}
}

@ -1,12 +1,13 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.animation.BoneTrack;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.ogre.AnimData;
/**
* This class represents 'Size limit' constraint type in blender.
@ -20,12 +21,15 @@ import com.jme3.scene.plugins.blender.file.Structure;
private static final int LIMIT_ZMIN = 0x10;
private static final int LIMIT_ZMAX = 0x20;
protected float[][] limits = new float[3][2];
protected int flag;
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -35,63 +39,93 @@ import com.jme3.scene.plugins.blender.file.Structure;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintSizeLimit(Structure constraintStructure, Long boneOMA,
public ConstraintSizeLimit(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
flag = ((Number) data.getFieldValue("flag")).intValue();
if(blenderContext.getBlenderKey().isFixUpAxis()) {
limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
limits[2][0] = -((Number) data.getFieldValue("ymin")).floatValue();
limits[2][1] = -((Number) data.getFieldValue("ymax")).floatValue();
limits[1][0] = ((Number) data.getFieldValue("zmin")).floatValue();
limits[1][1] = ((Number) data.getFieldValue("zmax")).floatValue();
//swapping Y and X limits flag in the bitwise flag
int ymin = flag & LIMIT_YMIN;
int ymax = flag & LIMIT_YMAX;
int zmin = flag & LIMIT_ZMIN;
int zmax = flag & LIMIT_ZMAX;
flag &= LIMIT_XMIN | LIMIT_XMAX;//clear the other flags to swap them
flag |= ymin << 2;
flag |= ymax << 2;
flag |= zmin >> 2;
flag |= zmax >> 2;
} else {
limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
limits[1][0] = ((Number) data.getFieldValue("ymin")).floatValue();
limits[1][1] = ((Number) data.getFieldValue("ymax")).floatValue();
limits[2][0] = ((Number) data.getFieldValue("zmin")).floatValue();
limits[2][1] = ((Number) data.getFieldValue("zmax")).floatValue();
}
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
BoneTrack track = (BoneTrack) this.getTrack(animation, targetIndex);
if (track != null) {
int flag = ((Number) data.getFieldValue("flag")).intValue();
Vector3f[] scales = track.getScales();
int maxFrames = scales.length;
for (int frame = 0; frame < maxFrames; ++frame) {
float influence = ipo.calculateValue(frame);
if ((flag & LIMIT_XMIN) != 0) {
float xmin = ((Number) data.getFieldValue("xmin")).floatValue();
if (scales[frame].x < xmin) {
scales[frame].x -= (scales[frame].x - xmin) * influence;
}
}
if ((flag & LIMIT_XMAX) != 0) {
float xmax = ((Number) data.getFieldValue("xmax")).floatValue();
if (scales[frame].x > xmax) {
scales[frame].x -= (scales[frame].x - xmax) * influence;
}
}
if ((flag & LIMIT_YMIN) != 0) {
float ymin = ((Number) data.getFieldValue("ymin")).floatValue();
if (scales[frame].y < ymin) {
scales[frame].y -= (scales[frame].y - ymin) * influence;
}
public void bakeDynamic() {
AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if(animData != null) {
Object owner = this.owner.getObject();
for(Animation animation : animData.anims) {
BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
Vector3f[] scales = track.getScales();
int maxFrames = scales.length;
for (int frame = 0; frame < maxFrames; ++frame) {
this.sizeLimit(scales[frame], ipo.calculateValue(frame));
}
if ((flag & LIMIT_YMAX) != 0) {
float ymax = ((Number) data.getFieldValue("ymax")).floatValue();
if (scales[frame].y > ymax) {
scales[frame].y -= (scales[frame].y - ymax) * influence;
}
}
if ((flag & LIMIT_ZMIN) != 0) {
float zmin = ((Number) data.getFieldValue("zmin")).floatValue();
if (scales[frame].z < zmin) {
scales[frame].z -= (scales[frame].z - zmin) * influence;
}
}
if ((flag & LIMIT_ZMAX) != 0) {
float zmax = ((Number) data.getFieldValue("zmax")).floatValue();
if (scales[frame].z > zmax) {
scales[frame].z -= (scales[frame].z - zmax) * influence;
}
}//TODO: consider constraint space !!!
track.setKeyframes(track.getTimes(), track.getTranslations(), track.getRotations(), scales);
}
track.setKeyframes(track.getTimes(), track.getTranslations(), track.getRotations(), scales);
}
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_SIZELIMIT;
public void bakeStatic() {
Transform ownerTransform = this.owner.getTransform();
this.sizeLimit(ownerTransform.getScale(), ipo.calculateValue(0));
this.owner.applyTransform(ownerTransform);
}
private void sizeLimit(Vector3f scale, float influence) {
if ((flag & LIMIT_XMIN) != 0) {
if (scale.x < limits[0][0]) {
scale.x -= (scale.x - limits[0][0]) * influence;
}
}
if ((flag & LIMIT_XMAX) != 0) {
if (scale.x > limits[0][1]) {
scale.x -= (scale.x - limits[0][1]) * influence;
}
}
if ((flag & LIMIT_YMIN) != 0) {
if (scale.y < limits[1][0]) {
scale.y -= (scale.y - limits[1][0]) * influence;
}
}
if ((flag & LIMIT_YMAX) != 0) {
if (scale.y > limits[1][1]) {
scale.y -= (scale.y - limits[1][1]) * influence;
}
}
if ((flag & LIMIT_ZMIN) != 0) {
if (scale.z < limits[2][0]) {
scale.z -= (scale.z - limits[2][0]) * influence;
}
}
if ((flag & LIMIT_ZMAX) != 0) {
if (scale.z > limits[2][1]) {
scale.z -= (scale.z - limits[2][1]) * influence;
}
}
}
}

@ -0,0 +1,50 @@
package com.jme3.scene.plugins.blender.constraints;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* The spline inverse kinematic constraint. Available for blender 2.50+.
* @author Marcin Roguski (Kaelthas)
*/
/*package*/ class ConstraintSplineInverseKinematic extends Constraint {
private static final Logger LOGGER = Logger.getLogger(ConstraintSplineInverseKinematic.class.getName());
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
* @param blenderContext
* the blender context
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintSplineInverseKinematic(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo,
BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
// TODO Auto-generated constructor stub
}
@Override
public void bakeDynamic() {
// TODO Auto-generated method stub
LOGGER.log(Level.WARNING, "'Splie IK' constraint NOT implemented!");
}
@Override
public void bakeStatic() {
// TODO Auto-generated method stub
LOGGER.log(Level.WARNING, "'Spline IK' constraint NOT implemented!");
}
}

@ -1,6 +1,5 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -19,8 +18,8 @@ import java.util.logging.Logger;
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* the constraint's structure
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -30,20 +29,20 @@ import java.util.logging.Logger;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintStretchTo(Structure constraintStructure, Long boneOMA,
Ipo influenceIpo, BlenderContext blenderContext)
throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
public ConstraintStretchTo(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
public void bakeDynamic() {
// TODO: implement 'Stretch to' constraint
LOGGER.log(Level.WARNING, "'Stretch to' constraint NOT implemented!");
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_STRETCHTO;
public void bakeStatic() {
// TODO: implement 'Stretch to' constraint
LOGGER.log(Level.WARNING, "'Stretch to' constraint NOT implemented!");
}
}

@ -1,6 +1,5 @@
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -19,8 +18,8 @@ import java.util.logging.Logger;
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param boneOMA
* the constraint's structure
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
@ -30,19 +29,20 @@ import java.util.logging.Logger;
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintTransform(Structure constraintStructure, Long boneOMA,
public ConstraintTransform(Structure constraintStructure, Long ownerOMA,
Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, boneOMA, influenceIpo, blenderContext);
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
}
@Override
public void affectAnimation(Animation animation, int targetIndex) {
public void bakeDynamic() {
// TODO: implement 'Transform' constraint
LOGGER.log(Level.WARNING, "'Transform' constraint NOT implemented!");
}
@Override
public ConstraintType getType() {
return ConstraintType.CONSTRAINT_TYPE_TRANSFORM;
public void bakeStatic() {
// TODO: implement 'Transform' constraint
LOGGER.log(Level.WARNING, "'Transform' constraint NOT implemented!");
}
}

@ -1,145 +0,0 @@
package com.jme3.scene.plugins.blender.constraints;
import java.util.HashMap;
import java.util.Map;
/**
* Constraint types. Definitions taken from blender sources, file: DNA_constraint_types.h. Constraint id's the same as
* used in blender. The constraints might have duplicated type ids, depending on the blender version. The purpose of
* this enum is to combine class name and the constraint type (id).
* @author Marcin Roguski
*/
public enum ConstraintType {
/* Invalid/legacy constraint */
CONSTRAINT_TYPE_NULL(0, "bNullConstraint"),
/* Unimplemented non longer :) - during constraints recode, Aligorith */
CONSTRAINT_TYPE_CHILDOF(1, "bChildOfConstraint"),
CONSTRAINT_TYPE_KINEMATIC(3, "bKinematicConstraint"),
CONSTRAINT_TYPE_FOLLOWPATH(4, "bFollowPathConstraint"),
/* Unimplemented no longer :) - Aligorith */
CONSTRAINT_TYPE_ROTLIMIT(5, "bRotLimitConstraint"),
/* Unimplemented no longer :) - Aligorith */
CONSTRAINT_TYPE_LOCLIMIT(6, "bLocLimitConstraint"),
/* Unimplemented no longer :) - Aligorith */
CONSTRAINT_TYPE_SIZELIMIT(7, "bSizeLimitConstraint"),
CONSTRAINT_TYPE_ROTLIKE(8, "bRotateLikeConstraint"),
CONSTRAINT_TYPE_LOCLIKE(9, "bLocateLikeConstraint"),
CONSTRAINT_TYPE_SIZELIKE(10, "bSizeLikeConstraint"),
/* Unimplemented no longer :) - Aligorith. Scripts */
CONSTRAINT_TYPE_PYTHON(11, "bPythonConstraint"),
CONSTRAINT_TYPE_ACTION(12, "bActionConstraint"),
/* New Tracking constraint that locks an axis in place - theeth */
CONSTRAINT_TYPE_LOCKTRACK(13, "bLockTrackConstraint"),
/* limit distance */
CONSTRAINT_TYPE_DISTLIMIT(14, "bDistLimitConstraint"),
/* claiming this to be mine :) is in tuhopuu bjornmose */
CONSTRAINT_TYPE_STRETCHTO(15, "bStretchToConstraint"),
/* floor constraint */
CONSTRAINT_TYPE_MINMAX(16, "bMinMaxConstraint"),
/* rigidbody constraint */
CONSTRAINT_TYPE_RIGIDBODYJOINT(17, "bRigidBodyConstraint"),
/* clampto constraint */
CONSTRAINT_TYPE_CLAMPTO(18, "bClampToConstraint"),
/* transformation (loc/rot/size -> loc/rot/size) constraint */
CONSTRAINT_TYPE_TRANSFORM(19, "bTransformConstraint"),
/* shrinkwrap (loc/rot) constraint */
CONSTRAINT_TYPE_SHRINKWRAP(20, "bShrinkwrapConstraint");
/** The constraint's id (in blender known as 'type'). */
private int constraintId;
/** The name of constraint class used by blender. */
private String className;
/** The map containing class names and types of constraints. */
private static final Map<String, ConstraintType> typesMap = new HashMap<String, ConstraintType>(ConstraintType.values().length);
/** The map containing class names and types of constraints. */
private static final Map<Integer, ConstraintType> idsMap = new HashMap<Integer, ConstraintType>(ConstraintType.values().length);
static {
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_NULL.constraintId), CONSTRAINT_TYPE_NULL);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CHILDOF.constraintId), CONSTRAINT_TYPE_CHILDOF);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_KINEMATIC.constraintId), CONSTRAINT_TYPE_KINEMATIC);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_FOLLOWPATH.constraintId), CONSTRAINT_TYPE_FOLLOWPATH);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIMIT.constraintId), CONSTRAINT_TYPE_ROTLIMIT);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIMIT.constraintId), CONSTRAINT_TYPE_LOCLIMIT);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIMIT.constraintId), CONSTRAINT_TYPE_SIZELIMIT);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ROTLIKE.constraintId), CONSTRAINT_TYPE_ROTLIKE);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCLIKE.constraintId), CONSTRAINT_TYPE_LOCLIKE);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SIZELIKE.constraintId), CONSTRAINT_TYPE_SIZELIKE);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_PYTHON.constraintId), CONSTRAINT_TYPE_PYTHON);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_ACTION.constraintId), CONSTRAINT_TYPE_ACTION);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_LOCKTRACK.constraintId), CONSTRAINT_TYPE_LOCKTRACK);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_DISTLIMIT.constraintId), CONSTRAINT_TYPE_DISTLIMIT);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_STRETCHTO.constraintId), CONSTRAINT_TYPE_STRETCHTO);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_MINMAX.constraintId), CONSTRAINT_TYPE_MINMAX);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_RIGIDBODYJOINT.constraintId), CONSTRAINT_TYPE_RIGIDBODYJOINT);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_CLAMPTO.constraintId), CONSTRAINT_TYPE_CLAMPTO);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_TRANSFORM.constraintId), CONSTRAINT_TYPE_TRANSFORM);
idsMap.put(Integer.valueOf(CONSTRAINT_TYPE_SHRINKWRAP.constraintId), CONSTRAINT_TYPE_SHRINKWRAP);
}
/**
* Constructor. Stores constraint type and class name.
* @param constraintId
* the constraint's type
* @param className
* the constraint's type name
*/
private ConstraintType(int constraintId, String className) {
this.constraintId = constraintId;
this.className = className;
}
/**
* This method returns the type by given constraint id.
* @param constraintId
* the id of the constraint
* @return the constraint type enum value
*/
public static ConstraintType valueOf(int constraintId) {
return idsMap.get(Integer.valueOf(constraintId));
}
/**
* This method returns the constraint's id (type).
* @return the constraint's id (type)
*/
public int getConstraintId() {
return constraintId;
}
/**
* This method returns the constraint's class name.
* @return the constraint's class name
*/
public String getClassName() {
return className;
}
/**
* This method returns constraint enum type by the given class name.
* @param className
* the blender's constraint class name
* @return the constraint enum type of the specified class name
*/
public static ConstraintType getByBlenderClassName(String className) {
ConstraintType result = typesMap.get(className);
if (result == null) {
ConstraintType[] constraints = ConstraintType.values();
for (ConstraintType constraint : constraints) {
if (constraint.className.equals(className)) {
return constraint;
}
}
}
return result;
}
/**
* This method returns the type value of the last defined constraint. It can be used for allocating tables for
* storing constraint procedures since not all type values from 0 to the last value are used.
* @return the type value of the last defined constraint
*/
public static int getLastDefinedTypeValue() {
return CONSTRAINT_TYPE_SHRINKWRAP.getConstraintId();
}
}

@ -0,0 +1,274 @@
package com.jme3.scene.plugins.blender.constraints;
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.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
import com.jme3.scene.plugins.blender.constraints.Constraint.Space;
import com.jme3.scene.plugins.blender.file.DynamicArray;
import com.jme3.scene.plugins.blender.file.Structure;
/**
* This class represents either owner or target of the constraint. It has the
* common methods that take the evalueation space of the feature.
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */class Feature {
/** The evalueation space. */
protected Space space;
/** Old memory address of the feature. */
protected Long oma;
/** The spatial that is hold by the Feature. */
protected Spatial spatial;
/** The bone that is hold by the Feature. */
protected Bone bone;
/** The blender context. */
protected BlenderContext blenderContext;
/**
* Constructs the feature based on spatial.
*
* @param spatial
* the spatial
* @param space
* the spatial's evaluation space
* @param oma
* the spatial's old memory address
* @param blenderContext
* the blender context
*/
public Feature(Spatial spatial, Space space, Long oma, BlenderContext blenderContext) {
this.space = space;
this.oma = oma;
this.spatial = spatial;
this.blenderContext = blenderContext;
}
/**
* Constructs the feature based on bone.
*
* @param bone
* the bone
* @param space
* the bone evaluation space
* @param oma
* the bone old memory address
* @param blenderContext
* the blender context
*/
public Feature(Bone bone, Space space, Long oma, BlenderContext blenderContext) {
this.space = space;
this.oma = oma;
this.blenderContext = blenderContext;
this.bone = bone;
}
/**
* @return the feature's old memory address
*/
public Long getOma() {
return oma;
}
/**
* @return the object held by the feature (either bone or spatial)
*/
public Object getObject() {
if (spatial != null) {
return spatial;
}
return bone;
}
/**
* @return the feature's transform depending on the evaluation space
*/
@SuppressWarnings("unchecked")
public Transform getTransform() {
if (spatial != null) {
switch (space) {
case CONSTRAINT_SPACE_LOCAL:
Structure targetStructure = (Structure) blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_STRUCTURE);
DynamicArray<Number> locArray = ((DynamicArray<Number>) targetStructure.getFieldValue("loc"));
Vector3f loc = new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), locArray.get(2).floatValue());
DynamicArray<Number> rotArray = ((DynamicArray<Number>) targetStructure.getFieldValue("rot"));
Quaternion rot = new Quaternion(new float[] { rotArray.get(0).floatValue(), rotArray.get(1).floatValue(), rotArray.get(2).floatValue() });
DynamicArray<Number> sizeArray = ((DynamicArray<Number>) targetStructure.getFieldValue("size"));
Vector3f size = new Vector3f(sizeArray.get(0).floatValue(), sizeArray.get(1).floatValue(), sizeArray.get(2).floatValue());
if (blenderContext.getBlenderKey().isFixUpAxis()) {
float y = loc.y;
loc.y = loc.z;
loc.z = -y;
y = rot.getY();
float z = rot.getZ();
rot.set(rot.getX(), z, -y, rot.getW());
y = size.y;
size.y = size.z;
size.z = y;
}
Transform result = new Transform(loc, rot);
result.setScale(size);
return result;
case CONSTRAINT_SPACE_WORLD:
return spatial.getWorldTransform();
default:
throw new IllegalStateException("Invalid space type for target object: " + space.toString());
}
}
// Bone
switch (space) {
case CONSTRAINT_SPACE_LOCAL:
Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
localTransform.setScale(bone.getLocalScale());
return localTransform;
case CONSTRAINT_SPACE_WORLD:
if(bone.getParent()!=null) {
System.out.println(bone.getParent().getLocalRotation());
System.out.println(bone.getParent().getWorldBindRotation());
System.out.println(bone.getParent().getModelSpaceRotation());
System.out.println(bone.getParent().getWorldBindInverseRotation());
}
Transform worldTransform = new Transform(bone.getWorldBindPosition(), bone.getWorldBindRotation());
worldTransform.setScale(bone.getWorldBindScale());
return worldTransform;
case CONSTRAINT_SPACE_POSE:
// TODO
return null;
case CONSTRAINT_SPACE_PARLOCAL:
Transform parentLocalTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
parentLocalTransform.setScale(bone.getLocalScale());
return parentLocalTransform;
default:
throw new IllegalStateException("Invalid space type for target object: " + space.toString());
}
}
/**
* This method applies the given transform to the feature in the proper
* evaluation space.
*
* @param transform
* the transform to be applied
*/
public void applyTransform(Transform transform) {
if (spatial != null) {
switch (space) {
case CONSTRAINT_SPACE_LOCAL:
Transform ownerLocalTransform = spatial.getLocalTransform();
ownerLocalTransform.getTranslation().addLocal(transform.getTranslation());
ownerLocalTransform.getRotation().multLocal(transform.getRotation());
ownerLocalTransform.getScale().multLocal(transform.getScale());
break;
case CONSTRAINT_SPACE_WORLD:
Matrix4f m = this.getParentWorldTransformMatrix();
m.invertLocal();
Matrix4f matrix = this.toMatrix(transform);
m.multLocal(matrix);
float scaleX = (float) Math.sqrt(m.m00 * m.m00 + m.m10 * m.m10 + m.m20 * m.m20);
float scaleY = (float) Math.sqrt(m.m01 * m.m01 + m.m11 * m.m11 + m.m21 * m.m21);
float scaleZ = (float) Math.sqrt(m.m02 * m.m02 + m.m12 * m.m12 + m.m22 * m.m22);
transform.setTranslation(m.toTranslationVector());
transform.setRotation(m.toRotationQuat());
transform.setScale(scaleX, scaleY, scaleZ);
spatial.setLocalTransform(transform);
break;
case CONSTRAINT_SPACE_PARLOCAL:
case CONSTRAINT_SPACE_POSE:
throw new IllegalStateException("Invalid space type (" + space.toString() + ") for owner object.");
default:
throw new IllegalStateException("Invalid space type for target object: " + space.toString());
}
} else {// Bone
switch (space) {
case CONSTRAINT_SPACE_LOCAL:
bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
break;
case CONSTRAINT_SPACE_WORLD:
Matrix4f m = this.getParentWorldTransformMatrix();
m.invertLocal();
transform.setTranslation(m.mult(transform.getTranslation()));
transform.setRotation(m.mult(transform.getRotation(), null));
transform.setScale(transform.getScale());
bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
break;
case CONSTRAINT_SPACE_PARLOCAL:
Vector3f parentLocalTranslation = bone.getLocalPosition().add(transform.getTranslation());
Quaternion parentLocalRotation = bone.getLocalRotation().mult(transform.getRotation());
bone.setBindTransforms(parentLocalTranslation, parentLocalRotation, transform.getScale());
break;
case CONSTRAINT_SPACE_POSE:
// TODO:
break;
default:
throw new IllegalStateException("Invalid space type for target object: " + space.toString());
}
}
}
/**
* @return world transform matrix of the feature
*/
public Matrix4f getWorldTransformMatrix() {
if (spatial != null) {
Matrix4f result = new Matrix4f();
Transform t = spatial.getWorldTransform();
result.setTransform(t.getTranslation(), t.getScale(), t.getRotation().toRotationMatrix());
return result;
}
// Bone
Matrix4f result = new Matrix4f();
result.setTransform(bone.getWorldBindPosition(), bone.getWorldBindScale(), bone.getWorldBindRotation().toRotationMatrix());
return result;
}
/**
* @return world transform matrix of the feature's parent or identity matrix
* if the feature has no parent
*/
public Matrix4f getParentWorldTransformMatrix() {
Matrix4f result = new Matrix4f();
if (spatial != null) {
if (spatial.getParent() != null) {
Transform t = spatial.getParent().getWorldTransform();
result.setTransform(t.getTranslation(), t.getScale(), t.getRotation().toRotationMatrix());
}
} else {// Bone
Bone parent = bone.getParent();
if (parent != null) {
result.setTransform(parent.getWorldBindPosition(), parent.getWorldBindScale(), parent.getWorldBindRotation().toRotationMatrix());
}
}
return result;
}
/**
* Converts given transform to the matrix.
*
* @param transform
* the transform to be converted
* @return 4x4 matri that represents the given transform
*/
protected Matrix4f toMatrix(Transform transform) {
Matrix4f result = Matrix4f.IDENTITY;
if (transform != null) {
result = new Matrix4f();
result.setTranslation(transform.getTranslation());
result.setRotationQuaternion(transform.getRotation());
result.setScale(transform.getScale());
}
return result;
}
}

@ -43,9 +43,11 @@ public class CurvesHelper extends AbstractBlenderHelper {
* 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 CurvesHelper(String blenderVersion) {
super(blenderVersion);
public CurvesHelper(String blenderVersion, boolean fixUpAxis) {
super(blenderVersion, fixUpAxis);
}
/**
@ -457,7 +459,7 @@ public class CurvesHelper extends AbstractBlenderHelper {
temp[1] = vertices[j * 3 + 1] * taperScale;
temp[2] = 0;
m.mult(temp);//the result is stored in the array
if (fixUpAxis) {
if (fixUpAxis) {//TODO: not the other way ???
verts[j] = new Vector3f(temp[0], temp[1], temp[2]);
} else {
verts[j] = new Vector3f(temp[0], temp[2], -temp[1]);

@ -59,9 +59,11 @@ public class LightHelper extends AbstractBlenderHelper {
* 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 LightHelper(String blenderVersion) {
super(blenderVersion);
public LightHelper(String blenderVersion, boolean fixUpAxis) {
super(blenderVersion, fixUpAxis);
}
public Light toLight(Structure structure, BlenderContext blenderContext) throws BlenderFileException {

@ -45,7 +45,6 @@ import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
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 com.jme3.scene.plugins.blender.textures.TextureHelper;
import com.jme3.shader.VarType;
import com.jme3.texture.Image;
import com.jme3.texture.Image.Format;
@ -101,9 +100,11 @@ public class MaterialHelper extends AbstractBlenderHelper {
*
* @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 MaterialHelper(String blenderVersion) {
super(blenderVersion);
public MaterialHelper(String blenderVersion, boolean fixUpAxis) {
super(blenderVersion, false);
// setting alpha masks
alphaMasks.put(ALPHA_MASK_NONE, new IAlphaMask() {
@Override
@ -204,7 +205,6 @@ public class MaterialHelper extends AbstractBlenderHelper {
// texture
Type colorTextureType = null;
Map<String, Texture> texturesMap = new HashMap<String, Texture>();
TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
for(Entry<Number, Texture> textureEntry : materialContext.loadedTextures.entrySet()) {
int mapto = textureEntry.getKey().intValue();
Texture texture = textureEntry.getValue();

@ -74,9 +74,11 @@ public class MeshHelper extends AbstractBlenderHelper {
*
* @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 MeshHelper(String blenderVersion) {
super(blenderVersion);
public MeshHelper(String blenderVersion, boolean fixUpAxis) {
super(blenderVersion,fixUpAxis);
}
/**

@ -1,6 +1,20 @@
package com.jme3.scene.plugins.blender.modifiers;
import com.jme3.animation.*;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.animation.AnimControl;
import com.jme3.animation.Animation;
import com.jme3.animation.Bone;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.Skeleton;
import com.jme3.animation.SkeletonControl;
import com.jme3.math.Matrix4f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
@ -12,8 +26,8 @@ import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
import com.jme3.scene.plugins.blender.animations.ArmatureHelper.BoneTransformationData;
import com.jme3.scene.plugins.blender.constraints.Constraint;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.Pointer;
@ -22,14 +36,6 @@ import com.jme3.scene.plugins.blender.meshes.MeshContext;
import com.jme3.scene.plugins.blender.objects.ObjectHelper;
import com.jme3.scene.plugins.ogre.AnimData;
import com.jme3.util.BufferUtils;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This modifier allows to add bone animation to the object.
@ -37,34 +43,30 @@ import java.util.logging.Logger;
* @author Marcin Roguski (Kaelthas)
*/
/* package */class ArmatureModifier extends Modifier {
private static final Logger LOGGER = Logger.getLogger(ArmatureModifier.class.getName());
private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4; // have no idea why 4, could someone please explain ?
//@Marcin it was an Ogre limitation, but as long as we use a MaxNumWeight variable in mesh,
//i guess this limitation has no sense for the blender loader...so i guess it's up to you. You'll have to deternine the max weight according to the provided blend file
//I added a check to avoid crash when loading a model that has more than 4 weight per vertex on line 258
//If you decide to remove this limitation, remove this code.
//Rémy
private static final Logger LOGGER = Logger.getLogger(ArmatureModifier.class.getName());
private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4;
// @Marcin it was an Ogre limitation, but as long as we use a MaxNumWeight
// variable in mesh,
// i guess this limitation has no sense for the blender loader...so i guess
// it's up to you. You'll have to deternine the max weight according to the
// provided blend file
// I added a check to avoid crash when loading a model that has more than 4
// weight per vertex on line 258
// If you decide to remove this limitation, remove this code.
// Rémy
/** Loaded animation data. */
private AnimData animData;
/** Old memory address of the armature's object. */
private Long armatureObjectOMA;
private AnimData animData;
/** Old memory address of the mesh that will have the skeleton applied. */
private Long meshOMA;
/** The maxiumum amount of bone groups applied to a single vertex (max = MAXIMUM_WEIGHTS_PER_VERTEX). */
private int boneGroups;
/** The weights of vertices. */
private VertexBuffer verticesWeights;
/** The indexes of bones applied to vertices. */
private VertexBuffer verticesWeightsIndices;
private Long meshOMA;
/**
* This constructor is only temporary. It will be removed when object
* animation is implemented in jme. TODO!!!!!!!
* The maxiumum amount of bone groups applied to a single vertex (max = MAXIMUM_WEIGHTS_PER_VERTEX).
*/
/* package */ArmatureModifier() {
}
private int boneGroups;
/** The weights of vertices. */
private VertexBuffer verticesWeights;
/** The indexes of bones applied to vertices. */
private VertexBuffer verticesWeightsIndices;
/**
* This constructor reads animation data from the object structore. The
@ -81,48 +83,58 @@ import java.util.logging.Logger;
* corrupted
*/
public ArmatureModifier(Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
if(this.validate(modifierStructure, blenderContext)) {
if (this.validate(modifierStructure, blenderContext)) {
Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object");
if (pArmatureObject.isNotNull()) {
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
Structure armatureObject = pArmatureObject.fetchData(blenderContext.getInputStream()).get(0);
this.armatureObjectOMA = armatureObject.getOldMemoryAddress();
//read skeleton
// changing bones matrices so that they fit the current object
Structure armatureStructure = ((Pointer)armatureObject.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
Structure bonebase = (Structure) armatureStructure.getFieldValue("bonebase");
List<Structure> bonesStructures = bonebase.evaluateListBase(blenderContext);
for (Structure boneStructure : bonesStructures) {
BoneTransformationData rootBoneTransformationData = armatureHelper.readBoneAndItsChildren(boneStructure, null, blenderContext);
armatureHelper.addBoneDataRoot(rootBoneTransformationData);
}
Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject);
Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert();
Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix);
Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation);
//read mesh indexes
Structure meshStructure = ((Pointer)objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
// load skeleton
Structure armatureStructure = ((Pointer) armatureObject.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
Structure pose = ((Pointer) armatureObject.getFieldValue("pose")).fetchData(blenderContext.getInputStream()).get(0);
List<Structure> chanbase = ((Structure) pose.getFieldValue("chanbase")).evaluateListBase(blenderContext);
Map<Long, Structure> bonesPoseChannels = new HashMap<Long, Structure>(chanbase.size());
for (Structure poseChannel : chanbase) {
Pointer pBone = (Pointer) poseChannel.getFieldValue("bone");
bonesPoseChannels.put(pBone.getOldMemoryAddress(), poseChannel);
}
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Matrix4f armatureObjectMatrix = objectHelper.getMatrix(armatureObject, "obmat", true);//TODO: fixupaxis ???
Matrix4f inverseMeshObjectMatrix = objectHelper.getMatrix(objectStructure, "obmat", true).invertLocal();
Matrix4f objectToArmatureTransformation = armatureObjectMatrix.multLocal(inverseMeshObjectMatrix);
List<Structure> bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase(blenderContext);
List<Bone> bonesList = new ArrayList<Bone>();
for (int i = 0; i < bonebase.size(); ++i) {
armatureHelper.buildBones(bonebase.get(i), null, bonesList, objectToArmatureTransformation, bonesPoseChannels, blenderContext);
}
bonesList.add(0, new Bone(""));
Skeleton skeleton = new Skeleton(bonesList.toArray(new Bone[bonesList.size()]));
// read mesh indexes
Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
this.meshOMA = meshStructure.getOldMemoryAddress();
this.readVerticesWeightsData(objectStructure, meshStructure, blenderContext);
this.readVerticesWeightsData(objectStructure, meshStructure, skeleton, blenderContext);
//read animations
// read animations
ArrayList<Animation> animations = new ArrayList<Animation>();
List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
if(actionHeaders != null) {//it may happen that the model has armature with no actions
if (actionHeaders != null) {// it may happen that the model has
// armature with no actions
for (FileBlockHeader header : actionHeaders) {
Structure actionStructure = header.getStructure(blenderContext);
String actionName = actionStructure.getName();
BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, blenderContext);
//determining the animation time
BoneTrack[] tracks = armatureHelper.getTracks(actionStructure, skeleton, blenderContext);
// determining the animation time
float maximumTrackLength = 0;
for(BoneTrack track : tracks) {
for (BoneTrack track : tracks) {
float length = track.getLength();
if(length > maximumTrackLength) {
if (length > maximumTrackLength) {
maximumTrackLength = length;
}
}
@ -132,7 +144,16 @@ import java.util.logging.Logger;
animations.add(boneAnimation);
}
}
animData = new AnimData(new Skeleton(bones), animations);
animData = new AnimData(skeleton, animations);
// store the animation data for each bone
for (Structure boneStructure : bonebase) {
blenderContext.setAnimData(boneStructure.getOldMemoryAddress(), animData);
}
// loading constraints connected with this object
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
constraintHelper.loadConstraints(armatureObject, blenderContext);
}
}
}
@ -140,16 +161,16 @@ import java.util.logging.Logger;
@Override
@SuppressWarnings("unchecked")
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());
}//if invalid, animData will be null
if(animData == null) {
}// if invalid, animData will be null
if (animData == null) {
return node;
}
// setting weights for bones
List<Geometry> geomList = (List<Geometry>) blenderContext.getLoadedFeature(this.meshOMA, LoadedFeatureDataType.LOADED_FEATURE);
for(Geometry geom : geomList) {
for (Geometry geom : geomList) {
Mesh mesh = geom.getMesh();
if (this.verticesWeights != null) {
mesh.setMaxNumWeights(this.boneGroups);
@ -158,84 +179,108 @@ import java.util.logging.Logger;
}
}
// applying bone transforms before constraints are baked
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) {
Long boneOMA = armatureHelper.getBoneOMA(animData.skeleton.getBone(i));
List<Constraint> constraints = blenderContext.getConstraints(boneOMA);
if (constraints != null && constraints.size() > 0) {
for (Constraint constraint : constraints) {
constraint.bakeDynamic();
constraint.bakeStatic();
}
}
}
// applying animations
ArrayList<Animation> animList = animData.anims;
if (animList != null && animList.size() > 0) {
List<Constraint> constraints = blenderContext.getConstraints(this.armatureObjectOMA);
HashMap<String, Animation> anims = new HashMap<String, Animation>();
HashMap<String, Animation> anims = new HashMap<String, Animation>(animList.size());
for (int i = 0; i < animList.size(); ++i) {
Animation animation = (Animation) animList.get(i).clone();
// baking constraints into animations
if (constraints != null && constraints.size() > 0) {
for (Constraint constraint : constraints) {
Long boneOMA = constraint.getBoneOMA();
Bone bone = (Bone) blenderContext.getLoadedFeature(boneOMA, LoadedFeatureDataType.LOADED_FEATURE);
int targetIndex = bone==null ? 0 : animData.skeleton.getBoneIndex(bone);//bone==null may mean the object animation
constraint.affectAnimation(animation, targetIndex);
}
}
Animation animation = animList.get(i);
anims.put(animation.getName(), animation);
}
// applying the control to the node
SkeletonControl skeletonControl = new SkeletonControl(animData.skeleton);
AnimControl control = new AnimControl(animData.skeleton);
control.setAnimations(anims);
node.addControl(control);
node.addControl(skeletonControl);
node.addControl(new SkeletonControl(animData.skeleton));
}
return node;
}
/**
* This method reads mesh indexes
* @param objectStructure structure of the object that has the armature modifier applied
* @param meshStructure the structure of the object's mesh
* @param blenderContext the blender context
*
* @param objectStructure
* structure of the object that has the armature modifier applied
* @param meshStructure
* the structure of the object's mesh
* @param blenderContext
* the blender context
* @throws BlenderFileException
* this exception is thrown when the blend file structure is somehow invalid or corrupted
* this exception is thrown when the blend file structure is
* somehow invalid or corrupted
*/
private void readVerticesWeightsData(Structure objectStructure, Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
private void readVerticesWeightsData(Structure objectStructure, Structure meshStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
Structure defBase = (Structure) objectStructure.getFieldValue("defbase");
Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, blenderContext);
Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, skeleton, blenderContext);
int[] bonesGroups = new int[] { 0 };
MeshContext meshContext = blenderContext.getMeshContext(meshStructure.getOldMemoryAddress());
VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(meshStructure, meshContext.getVertexList().size(), bonesGroups,
meshContext.getVertexReferenceMap(), groupToBoneIndexMap, blenderContext);
VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(meshStructure, meshContext.getVertexList().size(), bonesGroups, meshContext.getVertexReferenceMap(), groupToBoneIndexMap, blenderContext);
this.verticesWeights = boneWeightsAndIndex[0];
this.verticesWeightsIndices = boneWeightsAndIndex[1];
this.boneGroups = bonesGroups[0];
}
/**
* This method returns an array of size 2. The first element is a vertex buffer holding bone weights for every vertex in the model. The
* second element is a vertex buffer holding bone indices for vertices (the indices of bones the vertices are assigned to).
* This method returns an array of size 2. The first element is a vertex
* buffer holding bone weights for every vertex in the model. The second
* element is a vertex buffer holding bone indices for vertices (the indices
* of bones the vertices are assigned to).
*
* @param meshStructure
* the mesh structure object
* @param vertexListSize
* a number of vertices in the model
* @param bonesGroups
* this is an output parameter, it should be a one-sized array; the maximum amount of weights per vertex (up to
* this is an output parameter, it should be a one-sized array;
* the maximum amount of weights per vertex (up to
* MAXIMUM_WEIGHTS_PER_VERTEX) is stored there
* @param vertexReferenceMap
* this reference map allows to map the original vertices read from blender to vertices that are really in the model; one
* this reference map allows to map the original vertices read
* from blender to vertices that are really in the model; one
* vertex may appear several times in the result model
* @param groupToBoneIndexMap
* this object maps the group index (to which a vertices in blender belong) to bone index of the model
* this object maps the group index (to which a vertices in
* blender belong) to bone index of the model
* @param blenderContext
* the blender context
* @return arrays of vertices weights and their bone indices and (as an output parameter) the maximum amount of weights for a vertex
* @return arrays of vertices weights and their bone indices and (as an
* output parameter) the maximum amount of weights for a vertex
* @throws BlenderFileException
* this exception is thrown when the blend file structure is somehow invalid or corrupted
* this exception is thrown when the blend file structure is
* somehow invalid or corrupted
*/
private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups,
Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, BlenderContext blenderContext)
private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups, Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, BlenderContext blenderContext)
throws BlenderFileException {
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
@ -253,15 +298,16 @@ import java.util.logging.Logger;
int weightIndex = 0;
List<Structure> dw = pDW.fetchData(blenderContext.getInputStream());
for (Structure deformWeight : dw) {
Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
//Remove this code if 4 weights limitation is removed
if(weightIndex==4){
LOGGER.log(Level.WARNING,"{0} has more than 4 weight on bone index {1}",new Object[]{meshStructure.getName(),boneIndex});
break;
}
// Remove this code if 4 weights limitation is removed
if (weightIndex == 4) {
LOGGER.log(Level.WARNING, "{0} has more than 4 weight on bone index {1}", new Object[] { meshStructure.getName(), boneIndex });
break;
}
if (boneIndex != null) {// null here means that we came accross group that has no bone attached to
// null here means that we came accross group that has no bone attached to
if (boneIndex != null) {
float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
if (weight == 0.0f) {
weight = 1;
@ -285,8 +331,10 @@ import java.util.logging.Logger;
}
} else {
// always bind all vertices to 0-indexed bone
// this bone makes the model look normally if vertices have no bone assigned
// and it is used in object animation, so if we come accross object animation
// this bone makes the model look normally if vertices have no bone
// assigned
// and it is used in object animation, so if we come accross object
// animation
// we can use the 0-indexed bone for this
for (List<Integer> vertexIndexList : vertexReferenceMap.values()) {
// we apply the weight to all referenced vertices
@ -307,9 +355,13 @@ import java.util.logging.Logger;
}
/**
* Normalizes weights if needed and finds largest amount of weights used for all vertices in the buffer.
* @param vertCount amount of vertices
* @param weightsFloatData weights for vertices
* Normalizes weights if needed and finds largest amount of weights used for
* all vertices in the buffer.
*
* @param vertCount
* amount of vertices
* @param weightsFloatData
* weights for vertices
*/
private int endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
int maxWeightsPerVert = 0;
@ -339,8 +391,6 @@ import java.util.logging.Logger;
}
}
weightsFloatData.rewind();
// mesh.setMaxNumWeights(maxWeightsPerVert);
return maxWeightsPerVert;
}

@ -55,9 +55,11 @@ public class ModifierHelper extends AbstractBlenderHelper {
* 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) {
super(blenderVersion);
public ModifierHelper(String blenderVersion, boolean fixUpAxis) {
super(blenderVersion, fixUpAxis);
}
/**

@ -1,5 +1,11 @@
package com.jme3.scene.plugins.blender.modifiers;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.animation.AnimControl;
import com.jme3.animation.Animation;
import com.jme3.animation.SpatialTrack;
@ -7,17 +13,11 @@ import com.jme3.scene.Node;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.animations.IpoHelper;
import com.jme3.scene.plugins.blender.constraints.Constraint;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This modifier allows to add animation to the object.
@ -51,21 +51,22 @@ import java.util.logging.Logger;
* corrupted
*/
public ObjectAnimationModifier(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.warning("Object animation modifier not yet implemented!");
objectOMA = objectStructure.getOldMemoryAddress();
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));
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;
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;
}
}
}
}
@ -89,7 +90,7 @@ import java.util.logging.Logger;
animations.add(animation);
animData = new AnimData(null, animations);
objectOMA = objectStructure.getOldMemoryAddress();
blenderContext.setAnimData(objectOMA, animData);
}
}
@ -98,30 +99,20 @@ import java.util.logging.Logger;
if(invalid) {
LOGGER.log(Level.WARNING, "Armature modifier is invalid! Cannot be applied to: {0}", node.getName());
}//if invalid, animData will be null
if(animData == null) {
return node;
}
ArrayList<Animation> animList = animData.anims;
if (animList != null && animList.size() > 0) {
List<Constraint> constraints = blenderContext.getConstraints(this.objectOMA);
HashMap<String, Animation> anims = new HashMap<String, Animation>();
for (int i = 0; i < animList.size(); ++i) {
Animation animation = (Animation) animList.get(i).clone();
// baking constraints into animations
if (constraints != null && constraints.size() > 0) {
for (Constraint constraint : constraints) {
constraint.affectAnimation(animation, 0);
}
if(animData != null) {
//INFO: constraints for this modifier are applied in the ObjectHelper when the whole object is loaded
ArrayList<Animation> animList = animData.anims;
if (animList != null && animList.size() > 0) {
HashMap<String, Animation> anims = new HashMap<String, Animation>();
for (int i = 0; i < animList.size(); ++i) {
Animation animation = animList.get(i);
anims.put(animation.getName(), animation);
}
anims.put(animation.getName(), animation);
AnimControl control = new AnimControl(null);
control.setAnimations(anims);
node.addControl(control);
}
AnimControl control = new AnimControl(null);
control.setAnimations(anims);
node.addControl(control);
}
return node;
}

@ -49,6 +49,7 @@ import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
import com.jme3.scene.plugins.blender.cameras.CameraHelper;
import com.jme3.scene.plugins.blender.constraints.Constraint;
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
import com.jme3.scene.plugins.blender.curves.CurvesHelper;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
@ -99,9 +100,11 @@ public class ObjectHelper extends AbstractBlenderHelper {
* 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 ObjectHelper(String blenderVersion) {
super(blenderVersion);
public ObjectHelper(String blenderVersion, boolean fixUpAxis) {
super(blenderVersion, fixUpAxis);
}
/**
@ -121,17 +124,12 @@ public class ObjectHelper extends AbstractBlenderHelper {
}
blenderContext.pushParent(objectStructure);
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
//get object data
int type = ((Number)objectStructure.getFieldValue("type")).intValue();
String name = objectStructure.getName();
LOGGER.log(Level.INFO, "Loading obejct: {0}", name);
//loading constraints connected with this object
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
constraintHelper.loadConstraints(objectStructure, blenderContext);
int restrictflag = ((Number)objectStructure.getFieldValue("restrictflag")).intValue();
boolean visible = (restrictflag & 0x01) != 0;
Object result = null;
@ -143,7 +141,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
parent = this.toObject(parentStructure, blenderContext);
}
Transform t = objectHelper.getTransformation(objectStructure, blenderContext);
Transform t = this.getTransformation(objectStructure, blenderContext);
try {
switch(type) {
@ -245,7 +243,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
}
break;
case OBJECT_TYPE_ARMATURE:
//Do not do anything, the object with all needed data is loaded when armature modifier loads
//Do nothing, the object with all needed data is loaded when armature modifier loads
break;
default:
LOGGER.log(Level.WARNING, "Unknown object type: {0}", type);
@ -255,13 +253,25 @@ public class ObjectHelper extends AbstractBlenderHelper {
}
if(result != null) {
blenderContext.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result);
//loading constraints connected with this object
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
constraintHelper.loadConstraints(objectStructure, blenderContext);
//baking constraints
List<Constraint> objectConstraints = blenderContext.getConstraints(objectStructure.getOldMemoryAddress());
if(objectConstraints!=null) {
for(Constraint objectConstraint : objectConstraints) {
objectConstraint.bakeStatic();
}
}
//reading custom properties
Properties properties = this.loadProperties(objectStructure, blenderContext);
if(result instanceof Spatial && properties != null && properties.getValue() != null) {
((Spatial)result).setUserData("properties", properties);
}
blenderContext.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result);
}
return result;
}
@ -292,13 +302,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
Vector3f translation = localMatrix.toTranslationVector();
Quaternion rotation = localMatrix.toRotationQuat();
//getting the scale
float scaleX = (float) Math.sqrt(parentInv.m00 * parentInv.m00 + parentInv.m10 * parentInv.m10 + parentInv.m20 * parentInv.m20);
float scaleY = (float) Math.sqrt(parentInv.m01 * parentInv.m01 + parentInv.m11 * parentInv.m11 + parentInv.m21 * parentInv.m21);
float scaleZ = (float) Math.sqrt(parentInv.m02 * parentInv.m02 + parentInv.m12 * parentInv.m12 + parentInv.m22 * parentInv.m22);
Vector3f scale = new Vector3f(size.get(0).floatValue() * scaleX,
size.get(1).floatValue() * scaleY,
size.get(2).floatValue() * scaleZ);
Vector3f scale = this.getScale(parentInv).multLocal(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
if(fixUpAxis) {
float y = translation.y;
@ -321,35 +325,76 @@ public class ObjectHelper extends AbstractBlenderHelper {
}
/**
* This method returns the transformation matrix of the given object structure.
* @param objectStructure
* the structure with object's data
* @return object's transformation matrix
* This method returns the matrix of a given name for the given structure.
* The matrix is NOT transformed if Y axis is up - the raw data is loaded from the blender file.
* @param structure
* the structure with matrix data
* @param matrixName
* the name of the matrix
* @return the required matrix
*/
public Matrix4f getTransformationMatrix(Structure objectStructure) {
return this.getMatrix(objectStructure, "obmat");
public Matrix4f getMatrix(Structure structure, String matrixName) {
return this.getMatrix(structure, matrixName, false);
}
/**
* This method returns the matrix of a given name for the given object structure.
* @param objectStructure
* the structure with object's data
* This method returns the matrix of a given name for the given structure.
* It takes up axis into consideration.
* @param structure
* the structure with matrix data
* @param matrixName
* the name of the matrix structure
* @return object's matrix
* the name of the matrix
* @return the required matrix
*/
@SuppressWarnings("unchecked")
protected Matrix4f getMatrix(Structure objectStructure, String matrixName) {
public Matrix4f getMatrix(Structure structure, String matrixName, boolean applyFixUpAxis) {
Matrix4f result = new Matrix4f();
DynamicArray<Number> obmat = (DynamicArray<Number>)objectStructure.getFieldValue(matrixName);
for(int i = 0; i < 4; ++i) {
for(int j = 0; j < 4; ++j) {
DynamicArray<Number> obmat = (DynamicArray<Number>)structure.getFieldValue(matrixName);
int rowAndColumnSize = Math.abs((int)Math.sqrt(obmat.getTotalSize()));//the matrix must be square
for(int i = 0; i < rowAndColumnSize; ++i) {
for(int j = 0; j < rowAndColumnSize; ++j) {
result.set(i, j, obmat.get(j, i).floatValue());
}
}
if(applyFixUpAxis && fixUpAxis) {
Vector3f translation = result.toTranslationVector();
Quaternion rotation = result.toRotationQuat();
Vector3f scale = this.getScale(result);
float y = translation.y;
translation.y = translation.z;
translation.z = -y;
y = rotation.getY();
float z = rotation.getZ();
rotation.set(rotation.getX(), z, -y, rotation.getW());
y=scale.y;
scale.y = scale.z;
scale.z = y;
result.loadIdentity();
result.setTranslation(translation);
result.setRotationQuaternion(rotation);
result.setScale(scale);
}
return result;
}
/**
* This method returns the scale from the given matrix.
*
* @param matrix
* the transformation matrix
* @return the scale from the given matrix
*/
public Vector3f getScale(Matrix4f matrix) {
float scaleX = (float) Math.sqrt(matrix.m00 * matrix.m00 + matrix.m10 * matrix.m10 + matrix.m20 * matrix.m20);
float scaleY = (float) Math.sqrt(matrix.m01 * matrix.m01 + matrix.m11 * matrix.m11 + matrix.m21 * matrix.m21);
float scaleZ = (float) Math.sqrt(matrix.m02 * matrix.m02 + matrix.m12 * matrix.m12 + matrix.m22 * matrix.m22);
return new Vector3f(scaleX, scaleY, scaleZ);
}
@Override
public void clearState() {
fixUpAxis = false;

@ -86,9 +86,11 @@ public class ParticlesHelper extends AbstractBlenderHelper {
* 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 ParticlesHelper(String blenderVersion) {
super(blenderVersion);
public ParticlesHelper(String blenderVersion, boolean fixUpAxis) {
super(blenderVersion, fixUpAxis);
}
@SuppressWarnings("unchecked")

@ -96,7 +96,7 @@ import java.util.logging.Logger;
* the number of blender version
*/
public NoiseGenerator(String blenderVersion) {
super(blenderVersion);
super(blenderVersion, false);
this.loadConstants();
}

@ -135,9 +135,11 @@ public class TextureHelper extends AbstractBlenderHelper {
*
* @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 TextureHelper(String blenderVersion) {
super(blenderVersion);
public TextureHelper(String blenderVersion, boolean fixUpAxis) {
super(blenderVersion, false);
noiseGenerator = new NoiseGenerator(blenderVersion);
textureGenerators.put(Integer.valueOf(TEX_BLEND), new TextureGeneratorBlend(noiseGenerator));
textureGenerators.put(Integer.valueOf(TEX_CLOUDS), new TextureGeneratorClouds(noiseGenerator));

Loading…
Cancel
Save