Refactoring: large changes in constraints system (see the proper topic on the forum for further changes)
git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10581 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
c4fc9b723f
commit
29dd973122
@ -46,8 +46,8 @@ import com.jme3.asset.AssetManager;
|
|||||||
import com.jme3.asset.BlenderKey;
|
import com.jme3.asset.BlenderKey;
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
import com.jme3.scene.plugins.blender.animations.Ipo;
|
|
||||||
import com.jme3.scene.plugins.blender.constraints.Constraint;
|
import com.jme3.scene.plugins.blender.constraints.Constraint;
|
||||||
import com.jme3.scene.plugins.blender.file.BlenderInputStream;
|
import com.jme3.scene.plugins.blender.file.BlenderInputStream;
|
||||||
import com.jme3.scene.plugins.blender.file.DnaBlockData;
|
import com.jme3.scene.plugins.blender.file.DnaBlockData;
|
||||||
@ -101,11 +101,6 @@ public class BlenderContext {
|
|||||||
private Map<String, Object[]> loadedFeaturesByName = new HashMap<String, Object[]>();
|
private Map<String, Object[]> loadedFeaturesByName = new HashMap<String, Object[]>();
|
||||||
/** A stack that hold the parent structure of currently loaded feature. */
|
/** A stack that hold the parent structure of currently loaded feature. */
|
||||||
private Stack<Structure> parentStack = new Stack<Structure>();
|
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. */
|
/** A list of modifiers for the specified object. */
|
||||||
protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>();
|
protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>();
|
||||||
/** A list of constraints for the specified object. */
|
/** A list of constraints for the specified object. */
|
||||||
@ -114,6 +109,8 @@ public class BlenderContext {
|
|||||||
private Map<Long, AnimData> animData = new HashMap<Long, AnimData>();
|
private Map<Long, AnimData> animData = new HashMap<Long, AnimData>();
|
||||||
/** Loaded skeletons. */
|
/** Loaded skeletons. */
|
||||||
private Map<Long, Skeleton> skeletons = new HashMap<Long, Skeleton>();
|
private Map<Long, Skeleton> skeletons = new HashMap<Long, Skeleton>();
|
||||||
|
/** A map between skeleton and node it modifies. */
|
||||||
|
private Map<Skeleton, Node> nodesWithSkeletons = new HashMap<Skeleton, Node>();
|
||||||
/** A map of mesh contexts. */
|
/** A map of mesh contexts. */
|
||||||
protected Map<Long, MeshContext> meshContexts = new HashMap<Long, MeshContext>();
|
protected Map<Long, MeshContext> meshContexts = new HashMap<Long, MeshContext>();
|
||||||
/** A map of bone contexts. */
|
/** A map of bone contexts. */
|
||||||
@ -345,25 +342,6 @@ public class BlenderContext {
|
|||||||
return null;
|
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.
|
* This method clears the saved features stored in the features map.
|
||||||
*/
|
*/
|
||||||
@ -408,38 +386,6 @@ public class BlenderContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
public Ipo getIpo(Long ownerOMA) {
|
|
||||||
return loadedIpos.get(ownerOMA);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method adds a new modifier to the list.
|
* This method adds a new modifier to the list.
|
||||||
*
|
*
|
||||||
@ -499,18 +445,6 @@ public class BlenderContext {
|
|||||||
objectConstraints.addAll(constraints);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return all available constraints
|
* @return all available constraints
|
||||||
*/
|
*/
|
||||||
@ -557,6 +491,30 @@ public class BlenderContext {
|
|||||||
this.skeletons.put(skeletonOMA, skeleton);
|
this.skeletons.put(skeletonOMA, skeleton);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method stores a binding between the skeleton and the proper armature
|
||||||
|
* node.
|
||||||
|
*
|
||||||
|
* @param skeleton
|
||||||
|
* the skeleton
|
||||||
|
* @param node
|
||||||
|
* the armature node
|
||||||
|
*/
|
||||||
|
public void setNodeForSkeleton(Skeleton skeleton, Node node) {
|
||||||
|
nodesWithSkeletons.put(skeleton, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the armature node that is defined for the skeleton.
|
||||||
|
*
|
||||||
|
* @param skeleton
|
||||||
|
* the skeleton
|
||||||
|
* @return the armature node that defines the skeleton in blender
|
||||||
|
*/
|
||||||
|
public Node getControlledNode(Skeleton skeleton) {
|
||||||
|
return nodesWithSkeletons.get(skeleton);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns the skeleton for the specified OMA of its owner.
|
* This method returns the skeleton for the specified OMA of its owner.
|
||||||
*
|
*
|
||||||
@ -635,6 +593,22 @@ public class BlenderContext {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns bone context for the given bone.
|
||||||
|
*
|
||||||
|
* @param bone
|
||||||
|
* the bone
|
||||||
|
* @return the bone's bone context
|
||||||
|
*/
|
||||||
|
public BoneContext getBoneContext(Bone bone) {
|
||||||
|
for (Entry<Long, BoneContext> entry : boneContexts.entrySet()) {
|
||||||
|
if (entry.getValue().getBone().equals(bone)) {
|
||||||
|
return entry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Cannot find context for bone: " + bone);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This metod returns the default material.
|
* This metod returns the default material.
|
||||||
*
|
*
|
||||||
@ -658,7 +632,6 @@ public class BlenderContext {
|
|||||||
loadedFeatures.clear();
|
loadedFeatures.clear();
|
||||||
loadedFeaturesByName.clear();
|
loadedFeaturesByName.clear();
|
||||||
parentStack.clear();
|
parentStack.clear();
|
||||||
loadedIpos.clear();
|
|
||||||
modifiers.clear();
|
modifiers.clear();
|
||||||
constraints.clear();
|
constraints.clear();
|
||||||
animData.clear();
|
animData.clear();
|
||||||
@ -672,7 +645,7 @@ public class BlenderContext {
|
|||||||
* This enum defines what loaded data type user wants to retreive. It can be
|
* This enum defines what loaded data type user wants to retreive. It can be
|
||||||
* either filled structure or already converted data.
|
* either filled structure or already converted data.
|
||||||
*
|
*
|
||||||
* @author Marcin Roguski
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
public static enum LoadedFeatureDataType {
|
public static enum LoadedFeatureDataType {
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.jme3.animation.Bone;
|
import com.jme3.animation.Bone;
|
||||||
|
import com.jme3.animation.Skeleton;
|
||||||
import com.jme3.math.Matrix4f;
|
import com.jme3.math.Matrix4f;
|
||||||
import com.jme3.math.Quaternion;
|
import com.jme3.math.Quaternion;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
@ -19,6 +20,7 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
|||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
public class BoneContext {
|
public class BoneContext {
|
||||||
|
private BlenderContext blenderContext;
|
||||||
/** The OMA of the bone's armature object. */
|
/** The OMA of the bone's armature object. */
|
||||||
private Long armatureObjectOMA;
|
private Long armatureObjectOMA;
|
||||||
/** The structure of the bone. */
|
/** The structure of the bone. */
|
||||||
@ -30,7 +32,7 @@ public class BoneContext {
|
|||||||
/** The parent context. */
|
/** The parent context. */
|
||||||
private BoneContext parent;
|
private BoneContext parent;
|
||||||
/** The children of this context. */
|
/** The children of this context. */
|
||||||
private List<BoneContext> children = new ArrayList<BoneContext>();
|
private List<BoneContext> children = new ArrayList<BoneContext>();
|
||||||
/** Created bone (available after calling 'buildBone' method). */
|
/** Created bone (available after calling 'buildBone' method). */
|
||||||
private Bone bone;
|
private Bone bone;
|
||||||
/** The bone's rest matrix. */
|
/** The bone's rest matrix. */
|
||||||
@ -74,6 +76,7 @@ public class BoneContext {
|
|||||||
*/
|
*/
|
||||||
private BoneContext(Structure boneStructure, Long armatureObjectOMA, BoneContext parent, BlenderContext blenderContext) throws BlenderFileException {
|
private BoneContext(Structure boneStructure, Long armatureObjectOMA, BoneContext parent, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
this.blenderContext = blenderContext;
|
||||||
this.boneStructure = boneStructure;
|
this.boneStructure = boneStructure;
|
||||||
this.armatureObjectOMA = armatureObjectOMA;
|
this.armatureObjectOMA = armatureObjectOMA;
|
||||||
boneName = boneStructure.getFieldValue("name").toString();
|
boneName = boneStructure.getFieldValue("name").toString();
|
||||||
@ -81,14 +84,14 @@ public class BoneContext {
|
|||||||
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
||||||
armatureMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", blenderContext.getBlenderKey().isFixUpAxis());
|
armatureMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", blenderContext.getBlenderKey().isFixUpAxis());
|
||||||
|
|
||||||
//compute the bone's rest matrix
|
// compute the bone's rest matrix
|
||||||
restMatrix = armatureMatrix.clone();
|
restMatrix = armatureMatrix.clone();
|
||||||
inverseTotalTransformation = restMatrix.invert();
|
inverseTotalTransformation = restMatrix.invert();
|
||||||
if(parent != null) {
|
if (parent != null) {
|
||||||
restMatrix = parent.inverseTotalTransformation.mult(restMatrix);
|
restMatrix = parent.inverseTotalTransformation.mult(restMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
//create the children
|
// create the children
|
||||||
List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext);
|
List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext);
|
||||||
for (Structure child : childbase) {
|
for (Structure child : childbase) {
|
||||||
this.children.add(new BoneContext(child, armatureObjectOMA, this, blenderContext));
|
this.children.add(new BoneContext(child, armatureObjectOMA, this, blenderContext));
|
||||||
@ -97,7 +100,6 @@ public class BoneContext {
|
|||||||
blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this);
|
blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method builds the bone. It recursively builds the bone's children.
|
* This method builds the bone. It recursively builds the bone's children.
|
||||||
*
|
*
|
||||||
@ -118,14 +120,12 @@ public class BoneContext {
|
|||||||
boneOMAs.put(bone, boneOMA);
|
boneOMAs.put(bone, boneOMA);
|
||||||
blenderContext.addLoadedFeatures(boneOMA, boneName, boneStructure, bone);
|
blenderContext.addLoadedFeatures(boneOMA, boneName, boneStructure, bone);
|
||||||
|
|
||||||
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
|
|
||||||
|
|
||||||
Vector3f poseLocation = restMatrix.toTranslationVector();
|
Vector3f poseLocation = restMatrix.toTranslationVector();
|
||||||
Quaternion rotation = restMatrix.toRotationQuat().normalizeLocal();
|
Quaternion rotation = restMatrix.toRotationQuat().normalizeLocal();
|
||||||
Vector3f scale = objectHelper.getScale(restMatrix);
|
Vector3f scale = restMatrix.toScaleVector();
|
||||||
if(parent == null) {
|
if (parent == null) {
|
||||||
Quaternion rotationQuaternion = objectToArmatureMatrix.toRotationQuat().normalizeLocal();
|
Quaternion rotationQuaternion = objectToArmatureMatrix.toRotationQuat().normalizeLocal();
|
||||||
scale.multLocal(objectHelper.getScale(objectToArmatureMatrix));
|
scale.multLocal(objectToArmatureMatrix.toScaleVector());
|
||||||
rotationQuaternion.multLocal(poseLocation.addLocal(objectToArmatureMatrix.toTranslationVector()));
|
rotationQuaternion.multLocal(poseLocation.addLocal(objectToArmatureMatrix.toTranslationVector()));
|
||||||
rotation.multLocal(rotationQuaternion);
|
rotation.multLocal(rotationQuaternion);
|
||||||
}
|
}
|
||||||
@ -165,4 +165,11 @@ public class BoneContext {
|
|||||||
public Long getArmatureObjectOMA() {
|
public Long getArmatureObjectOMA() {
|
||||||
return armatureObjectOMA;
|
return armatureObjectOMA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the skeleton the bone of this context belongs to
|
||||||
|
*/
|
||||||
|
public Skeleton getSkeleton() {
|
||||||
|
return blenderContext.getSkeleton(armatureObjectOMA);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,10 @@ public class Ipo {
|
|||||||
private Track calculatedTrack;
|
private Track calculatedTrack;
|
||||||
/** This variable indicates if the Y asxis is the UP axis or not. */
|
/** This variable indicates if the Y asxis is the UP axis or not. */
|
||||||
protected boolean fixUpAxis;
|
protected boolean fixUpAxis;
|
||||||
/** Depending on the blender version rotations are stored in degrees or radians so we need to know the version that is used. */
|
/**
|
||||||
|
* Depending on the blender version rotations are stored in degrees or
|
||||||
|
* radians so we need to know the version that is used.
|
||||||
|
*/
|
||||||
protected final int blenderVersion;
|
protected final int blenderVersion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -158,7 +161,8 @@ public class Ipo {
|
|||||||
// calculating track data
|
// calculating track data
|
||||||
for (int frame = startFrame; frame <= stopFrame; ++frame) {
|
for (int frame = startFrame; frame <= stopFrame; ++frame) {
|
||||||
int index = frame - startFrame;
|
int index = frame - startFrame;
|
||||||
times[index] = index * timeBetweenFrames;// start + (frame - 1) * timeBetweenFrames;
|
times[index] = index * timeBetweenFrames;// start + (frame - 1)
|
||||||
|
// * timeBetweenFrames;
|
||||||
for (int j = 0; j < bezierCurves.length; ++j) {
|
for (int j = 0; j < bezierCurves.length; ++j) {
|
||||||
double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
|
double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
|
||||||
switch (bezierCurves[j].getType()) {
|
switch (bezierCurves[j].getType()) {
|
||||||
@ -168,7 +172,7 @@ public class Ipo {
|
|||||||
break;
|
break;
|
||||||
case AC_LOC_Y:
|
case AC_LOC_Y:
|
||||||
if (fixUpAxis) {
|
if (fixUpAxis) {
|
||||||
translation[2] = (float) -value;
|
translation[2] = value == 0.0f ? 0 : (float) -value;
|
||||||
} else {
|
} else {
|
||||||
translation[1] = (float) value;
|
translation[1] = (float) value;
|
||||||
}
|
}
|
||||||
@ -185,7 +189,7 @@ public class Ipo {
|
|||||||
break;
|
break;
|
||||||
case OB_ROT_Y:
|
case OB_ROT_Y:
|
||||||
if (fixUpAxis) {
|
if (fixUpAxis) {
|
||||||
objectRotation[2] = (float) -value * degreeToRadiansFactor;
|
objectRotation[2] = value == 0.0f ? 0 : (float) -value * degreeToRadiansFactor;
|
||||||
} else {
|
} else {
|
||||||
objectRotation[1] = (float) value * degreeToRadiansFactor;
|
objectRotation[1] = (float) value * degreeToRadiansFactor;
|
||||||
}
|
}
|
||||||
@ -199,11 +203,7 @@ public class Ipo {
|
|||||||
scale[0] = (float) value;
|
scale[0] = (float) value;
|
||||||
break;
|
break;
|
||||||
case AC_SIZE_Y:
|
case AC_SIZE_Y:
|
||||||
if (fixUpAxis) {
|
scale[fixUpAxis ? 2 : 1] = (float) value;
|
||||||
scale[2] = (float) value;
|
|
||||||
} else {
|
|
||||||
scale[1] = (float) value;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case AC_SIZE_Z:
|
case AC_SIZE_Z:
|
||||||
scale[fixUpAxis ? 1 : 2] = (float) value;
|
scale[fixUpAxis ? 1 : 2] = (float) value;
|
||||||
@ -220,17 +220,13 @@ public class Ipo {
|
|||||||
break;
|
break;
|
||||||
case AC_QUAT_Y:
|
case AC_QUAT_Y:
|
||||||
if (fixUpAxis) {
|
if (fixUpAxis) {
|
||||||
quaternionRotation[2] = -(float) value;
|
quaternionRotation[2] = value == 0.0f ? 0 : -(float) value;
|
||||||
} else {
|
} else {
|
||||||
quaternionRotation[1] = (float) value;
|
quaternionRotation[1] = (float) value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AC_QUAT_Z:
|
case AC_QUAT_Z:
|
||||||
if (fixUpAxis) {
|
quaternionRotation[fixUpAxis ? 1 : 2] = (float) value;
|
||||||
quaternionRotation[1] = (float) value;
|
|
||||||
} else {
|
|
||||||
quaternionRotation[2] = (float) value;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOGGER.warning("Unknown ipo curve type: " + bezierCurves[j].getType());
|
LOGGER.warning("Unknown ipo curve type: " + bezierCurves[j].getType());
|
||||||
|
@ -3,10 +3,6 @@ package com.jme3.scene.plugins.blender.constraints;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import com.jme3.animation.Animation;
|
|
||||||
import com.jme3.animation.Bone;
|
|
||||||
import com.jme3.animation.BoneTrack;
|
|
||||||
import com.jme3.animation.Track;
|
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
@ -14,13 +10,12 @@ import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
|
|||||||
import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
|
import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
|
||||||
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
import com.jme3.scene.plugins.blender.animations.Ipo;
|
import com.jme3.scene.plugins.blender.animations.Ipo;
|
||||||
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
|
||||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
import com.jme3.scene.plugins.ogre.AnimData;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constraint applied on the bone.
|
* Constraint applied on the bone.
|
||||||
|
*
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
/* package */class BoneConstraint extends Constraint {
|
/* package */class BoneConstraint extends Constraint {
|
||||||
@ -47,12 +42,14 @@ import com.jme3.scene.plugins.ogre.AnimData;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean validate() {
|
public boolean validate() {
|
||||||
if (targetOMA != null) {
|
if (targetOMA != null) {
|
||||||
Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
||||||
// the second part of the if expression verifies if the found node (if any) is an armature node
|
// the second part of the if expression verifies if the found node
|
||||||
|
// (if any) is an armature node
|
||||||
if (nodeTarget == null || nodeTarget.getUserData(ArmatureHelper.ARMETURE_NODE_MARKER) != null) {
|
if (nodeTarget == null || nodeTarget.getUserData(ArmatureHelper.ARMETURE_NODE_MARKER) != null) {
|
||||||
// if the target is not an object node then it is an Armature, so make sure the bone is in the current skeleton
|
// if the target is not an object node then it is an Armature,
|
||||||
|
// so make sure the bone is in the current skeleton
|
||||||
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
||||||
if (targetOMA.longValue() != boneContext.getArmatureObjectOMA().longValue()) {
|
if (targetOMA.longValue() != boneContext.getArmatureObjectOMA().longValue()) {
|
||||||
LOGGER.log(Level.WARNING, "Bone constraint {0} must target bone in the its own skeleton! Targeting bone in another skeleton is not supported!", name);
|
LOGGER.log(Level.WARNING, "Bone constraint {0} must target bone in the its own skeleton! Targeting bone in another skeleton is not supported!", name);
|
||||||
@ -62,120 +59,24 @@ import com.jme3.scene.plugins.ogre.AnimData;
|
|||||||
isNodeTarget = true;
|
isNodeTarget = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void performBakingOperation() {
|
public void apply(int frame) {
|
||||||
Bone owner = blenderContext.getBoneContext(ownerOMA).getBone();
|
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
||||||
|
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
|
||||||
if (targetOMA != null) {
|
if (targetOMA != null) {
|
||||||
if (isNodeTarget) {
|
if (isNodeTarget) {
|
||||||
Spatial target = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
|
||||||
this.prepareTracksForApplyingConstraints();
|
constraintDefinition.bake(ownerTransform, targetTransform, this.ipo.calculateValue(frame));
|
||||||
AnimData animData = blenderContext.getAnimData(ownerOMA);
|
|
||||||
if (animData != null) {
|
|
||||||
for (Animation animation : animData.anims) {
|
|
||||||
Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
|
|
||||||
Transform targetTransform = constraintHelper.getNodeObjectTransform(targetSpace, targetOMA, blenderContext);
|
|
||||||
|
|
||||||
Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
|
|
||||||
Track targetTrack = constraintHelper.getTrack(target, animation);
|
|
||||||
|
|
||||||
constraintDefinition.bake(ownerTransform, targetTransform, boneTrack, targetTrack, this.ipo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
BoneContext boneContext = blenderContext.getBoneByName(subtargetName);
|
Transform targetTransform = constraintHelper.getTransform(targetOMA, subtargetName, targetSpace);
|
||||||
Bone target = boneContext.getBone();
|
constraintDefinition.bake(ownerTransform, targetTransform, this.ipo.calculateValue(frame));
|
||||||
this.targetOMA = boneContext.getBoneOma();
|
|
||||||
|
|
||||||
this.prepareTracksForApplyingConstraints();
|
|
||||||
AnimData animData = blenderContext.getAnimData(ownerOMA);
|
|
||||||
if (animData != null) {
|
|
||||||
for (Animation animation : animData.anims) {
|
|
||||||
Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
|
|
||||||
Transform targetTransform = constraintHelper.getBoneTransform(targetSpace, target);
|
|
||||||
|
|
||||||
Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
|
|
||||||
Track targetTrack = constraintHelper.getTrack(target, animData.skeleton, animation);
|
|
||||||
|
|
||||||
constraintDefinition.bake(ownerTransform, targetTransform, boneTrack, targetTrack, this.ipo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.prepareTracksForApplyingConstraints();
|
constraintDefinition.bake(ownerTransform, null, this.ipo.calculateValue(frame));
|
||||||
AnimData animData = blenderContext.getAnimData(ownerOMA);
|
|
||||||
if (animData != null) {
|
|
||||||
for (Animation animation : animData.anims) {
|
|
||||||
Transform ownerTransform = constraintHelper.getBoneTransform(ownerSpace, owner);
|
|
||||||
Track boneTrack = constraintHelper.getTrack(owner, animData.skeleton, animation);
|
|
||||||
|
|
||||||
constraintDefinition.bake(ownerTransform, null, boneTrack, null, this.ipo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void prepareTracksForApplyingConstraints() {
|
|
||||||
Long[] bonesOMAs = new Long[] { ownerOMA, targetOMA };
|
|
||||||
Space[] spaces = new Space[] { ownerSpace, targetSpace };
|
|
||||||
|
|
||||||
// creating animations for current objects if at least on of their parents have an animation
|
|
||||||
for (int i = 0; i < bonesOMAs.length; ++i) {
|
|
||||||
Long oma = bonesOMAs[i];
|
|
||||||
if (this.hasAnimation(oma)) {
|
|
||||||
Bone currentBone = blenderContext.getBoneContext(oma).getBone();
|
|
||||||
Bone parent = currentBone.getParent();
|
|
||||||
boolean foundAnimation = false;
|
|
||||||
AnimData animData = null;
|
|
||||||
while (parent != null && !foundAnimation) {
|
|
||||||
BoneContext boneContext = blenderContext.getBoneByName(parent.getName());
|
|
||||||
foundAnimation = this.hasAnimation(boneContext.getBoneOma());
|
|
||||||
animData = blenderContext.getAnimData(boneContext.getBoneOma());
|
|
||||||
parent = parent.getParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundAnimation) {
|
|
||||||
this.applyAnimData(blenderContext.getBoneContext(oma), spaces[i], animData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// creating animation for owner if it doesn't have one already and if the target has it
|
|
||||||
if (!this.hasAnimation(ownerOMA) && this.hasAnimation(targetOMA)) {
|
|
||||||
AnimData targetAnimData = blenderContext.getAnimData(targetOMA);
|
|
||||||
this.applyAnimData(blenderContext.getBoneContext(ownerOMA), ownerSpace, targetAnimData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The method determines if the bone has animations.
|
|
||||||
*
|
|
||||||
* @param animOwnerOMA
|
|
||||||
* OMA of the animation's owner
|
|
||||||
* @return <b>true</b> if the target has animations and <b>false</b> otherwise
|
|
||||||
*/
|
|
||||||
protected boolean hasAnimation(Long animOwnerOMA) {
|
|
||||||
AnimData animData = blenderContext.getAnimData(animOwnerOMA);
|
|
||||||
if (animData != null) {
|
|
||||||
if (!isNodeTarget) {
|
|
||||||
Bone bone = blenderContext.getBoneContext(animOwnerOMA).getBone();
|
|
||||||
int boneIndex = animData.skeleton.getBoneIndex(bone);
|
|
||||||
for (Animation animation : animData.anims) {
|
|
||||||
for (Track track : animation.getTracks()) {
|
|
||||||
if (track instanceof BoneTrack && ((BoneTrack) track).getTargetBoneIndex() == boneIndex) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,9 @@
|
|||||||
package com.jme3.scene.plugins.blender.constraints;
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import com.jme3.animation.Animation;
|
|
||||||
import com.jme3.animation.BoneTrack;
|
|
||||||
import com.jme3.math.Quaternion;
|
|
||||||
import com.jme3.math.Transform;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
|
||||||
import com.jme3.scene.plugins.blender.animations.Ipo;
|
import com.jme3.scene.plugins.blender.animations.Ipo;
|
||||||
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
import com.jme3.scene.plugins.blender.constraints.definitions.ConstraintDefinition;
|
import com.jme3.scene.plugins.blender.constraints.definitions.ConstraintDefinition;
|
||||||
@ -19,7 +11,6 @@ import com.jme3.scene.plugins.blender.constraints.definitions.ConstraintDefiniti
|
|||||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
||||||
import com.jme3.scene.plugins.blender.file.Pointer;
|
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
import com.jme3.scene.plugins.ogre.AnimData;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The implementation of a constraint.
|
* The implementation of a constraint.
|
||||||
@ -55,6 +46,8 @@ public abstract class Constraint {
|
|||||||
* the constraint's structure (bConstraint clss in blender 2.49).
|
* the constraint's structure (bConstraint clss in blender 2.49).
|
||||||
* @param ownerOMA
|
* @param ownerOMA
|
||||||
* the old memory address of the constraint owner
|
* the old memory address of the constraint owner
|
||||||
|
* @param ownerType
|
||||||
|
* the type of the constraint owner
|
||||||
* @param influenceIpo
|
* @param influenceIpo
|
||||||
* the ipo curve of the influence factor
|
* the ipo curve of the influence factor
|
||||||
* @param blenderContext
|
* @param blenderContext
|
||||||
@ -69,101 +62,88 @@ public abstract class Constraint {
|
|||||||
Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
|
Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
|
||||||
if (pData.isNotNull()) {
|
if (pData.isNotNull()) {
|
||||||
Structure data = pData.fetchData(blenderContext.getInputStream()).get(0);
|
Structure data = pData.fetchData(blenderContext.getInputStream()).get(0);
|
||||||
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, blenderContext);
|
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, ownerOMA, blenderContext);
|
||||||
Pointer pTar = (Pointer) data.getFieldValue("tar");
|
Pointer pTar = (Pointer) data.getFieldValue("tar");
|
||||||
if (pTar != null && pTar.isNotNull()) {
|
if (pTar != null && pTar.isNotNull()) {
|
||||||
this.targetOMA = pTar.getOldMemoryAddress();
|
this.targetOMA = pTar.getOldMemoryAddress();
|
||||||
this.targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
|
this.targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
|
||||||
Object subtargetValue = data.getFieldValue("subtarget");
|
Object subtargetValue = data.getFieldValue("subtarget");
|
||||||
if (subtargetValue != null) {// not all constraint data have the subtarget field
|
if (subtargetValue != null) {// not all constraint data have the
|
||||||
|
// subtarget field
|
||||||
subtargetName = subtargetValue.toString();
|
subtargetName = subtargetValue.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Null constraint has no data, so create it here
|
// Null constraint has no data, so create it here
|
||||||
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, blenderContext);
|
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, null, blenderContext);
|
||||||
}
|
}
|
||||||
this.ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
|
this.ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
|
||||||
this.ipo = influenceIpo;
|
this.ipo = influenceIpo;
|
||||||
this.ownerOMA = ownerOMA;
|
this.ownerOMA = ownerOMA;
|
||||||
this.constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
this.constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
||||||
|
LOGGER.log(Level.INFO, "Created constraint: {0} with definition: {1}", new Object[] { name, constraintDefinition });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method bakes the required sontraints into its owner. It checks if the constraint is invalid
|
* @return <b>true</b> if the constraint is implemented and <b>false</b>
|
||||||
* or if it isn't yet baked. It also performs baking of its target constraints so that the proper baking
|
* otherwise
|
||||||
* order is kept.
|
|
||||||
*/
|
*/
|
||||||
public void bake() {
|
public boolean isImplemented() {
|
||||||
if (!this.validate()) {
|
return constraintDefinition == null ? true : constraintDefinition.isImplemented();
|
||||||
LOGGER.warning("The constraint " + name + " is invalid and will not be applied.");
|
|
||||||
} else if (!baked) {
|
|
||||||
if (targetOMA != null) {
|
|
||||||
List<Constraint> targetConstraints = blenderContext.getConstraints(targetOMA);
|
|
||||||
if (targetConstraints != null && targetConstraints.size() > 0) {
|
|
||||||
LOGGER.log(Level.FINE, "Baking target constraints of constraint: {0}", name);
|
|
||||||
for (Constraint targetConstraint : targetConstraints) {
|
|
||||||
targetConstraint.bake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGGER.log(Level.FINE, "Performing baking of constraint: {0}", name);
|
|
||||||
this.performBakingOperation();
|
|
||||||
baked = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs validation before baking. Checks factors that can prevent constraint from baking that could not be
|
* @return the name of the constraint type, similar to the constraint name
|
||||||
* checked during constraint loading.
|
* used in Blender
|
||||||
*/
|
*/
|
||||||
protected abstract boolean validate();
|
public String getConstraintTypeName() {
|
||||||
|
return constraintDefinition.getConstraintTypeName();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method should be overwridden and perform the baking opertion.
|
* Performs validation before baking. Checks factors that can prevent
|
||||||
|
* constraint from baking that could not be checked during constraint
|
||||||
|
* loading.
|
||||||
*/
|
*/
|
||||||
protected abstract void performBakingOperation();
|
public abstract boolean validate();
|
||||||
|
|
||||||
/**
|
public abstract void apply(int frame);
|
||||||
* This method prepares the tracks for both owner and parent. If either owner or parent have no track while its parent has -
|
|
||||||
* the tracks are created. The tracks will not modify the owner/target movement but will be there ready for applying constraints.
|
|
||||||
* For example if the owner is a spatial and has no animation but its parent is moving then the track is created for the owner
|
|
||||||
* that will have non modifying values for translation, rotation and scale and will have the same amount of frames as its parent has.
|
|
||||||
*/
|
|
||||||
protected abstract void prepareTracksForApplyingConstraints();
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* The method applies bone's current position to all of the traces of the
|
public int hashCode() {
|
||||||
* given animations.
|
final int prime = 31;
|
||||||
*
|
int result = 1;
|
||||||
* @param boneContext
|
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||||
* the bone context
|
result = prime * result + ((ownerOMA == null) ? 0 : ownerOMA.hashCode());
|
||||||
* @param space
|
return result;
|
||||||
* the bone's evaluation space
|
}
|
||||||
* @param referenceAnimData
|
|
||||||
* the object containing the animations
|
|
||||||
*/
|
|
||||||
protected void applyAnimData(BoneContext boneContext, Space space, AnimData referenceAnimData) {
|
|
||||||
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
|
||||||
Transform transform = constraintHelper.getBoneTransform(space, boneContext.getBone());
|
|
||||||
|
|
||||||
AnimData animData = blenderContext.getAnimData(boneContext.getBoneOma());
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
for (Animation animation : referenceAnimData.anims) {
|
if (this == obj) {
|
||||||
BoneTrack parentTrack = (BoneTrack) animation.getTracks()[0];
|
return true;
|
||||||
|
|
||||||
float[] times = parentTrack.getTimes();
|
|
||||||
Vector3f[] translations = new Vector3f[times.length];
|
|
||||||
Quaternion[] rotations = new Quaternion[times.length];
|
|
||||||
Vector3f[] scales = new Vector3f[times.length];
|
|
||||||
Arrays.fill(translations, transform.getTranslation());
|
|
||||||
Arrays.fill(rotations, transform.getRotation());
|
|
||||||
Arrays.fill(scales, transform.getScale());
|
|
||||||
for (Animation anim : animData.anims) {
|
|
||||||
anim.addTrack(new BoneTrack(animData.skeleton.getBoneIndex(boneContext.getBone()), times, translations, rotations, scales));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
blenderContext.setAnimData(boneContext.getBoneOma(), animData);
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Constraint other = (Constraint) obj;
|
||||||
|
if (name == null) {
|
||||||
|
if (other.name != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!name.equals(other.name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ownerOMA == null) {
|
||||||
|
if (other.ownerOMA != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!ownerOMA.equals(other.ownerOMA)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,51 +1,65 @@
|
|||||||
package com.jme3.scene.plugins.blender.constraints;
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import com.jme3.animation.Animation;
|
|
||||||
import com.jme3.animation.Bone;
|
import com.jme3.animation.Bone;
|
||||||
import com.jme3.animation.BoneTrack;
|
|
||||||
import com.jme3.animation.Skeleton;
|
import com.jme3.animation.Skeleton;
|
||||||
import com.jme3.animation.SpatialTrack;
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.animation.Track;
|
|
||||||
import com.jme3.math.Matrix4f;
|
import com.jme3.math.Matrix4f;
|
||||||
import com.jme3.math.Quaternion;
|
import com.jme3.math.Quaternion;
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
||||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
|
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
import com.jme3.scene.plugins.blender.animations.Ipo;
|
import com.jme3.scene.plugins.blender.animations.Ipo;
|
||||||
import com.jme3.scene.plugins.blender.animations.IpoHelper;
|
import com.jme3.scene.plugins.blender.animations.IpoHelper;
|
||||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
||||||
import com.jme3.scene.plugins.blender.file.DynamicArray;
|
|
||||||
import com.jme3.scene.plugins.blender.file.Pointer;
|
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class should be used for constraint calculations.
|
* This class should be used for constraint calculations.
|
||||||
|
*
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
public class ConstraintHelper extends AbstractBlenderHelper {
|
public class ConstraintHelper extends AbstractBlenderHelper {
|
||||||
private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(ConstraintHelper.class.getName());
|
||||||
|
|
||||||
|
private static final Quaternion POS_POSE_SPACE_QUATERNION = new Quaternion(new float[] { FastMath.PI, 0, 0 });
|
||||||
|
private static final Quaternion NEG_POSE_SPACE_QUATERNION = new Quaternion(new float[] { -FastMath.PI, 0, 0 });
|
||||||
|
private static final Quaternion POS_PARLOC_SPACE_QUATERNION = new Quaternion(new float[] { FastMath.HALF_PI, 0, 0 });
|
||||||
|
private static final Quaternion NEG_PARLOC_SPACE_QUATERNION = new Quaternion(new float[] { -FastMath.HALF_PI, 0, 0 });
|
||||||
|
|
||||||
|
private BlenderContext blenderContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper constructor. It's main task is to generate the affection functions. These functions are common to all
|
* Helper constructor. It's main task is to generate the affection
|
||||||
* ConstraintHelper instances. Unfortunately this constructor might grow large. If it becomes too large - I shall
|
* functions. These functions are common to all ConstraintHelper instances.
|
||||||
* consider refactoring. The constructor parses the given blender version and stores the result. Some
|
* Unfortunately this constructor might grow large. If it becomes too large
|
||||||
* functionalities may differ in different blender versions.
|
* - I shall consider refactoring. The constructor parses the given blender
|
||||||
|
* version and stores the result. Some functionalities may differ in
|
||||||
|
* different blender versions.
|
||||||
|
*
|
||||||
* @param blenderVersion
|
* @param blenderVersion
|
||||||
* the version read from the blend file
|
* the version read from the blend file
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
* @param fixUpAxis
|
* @param fixUpAxis
|
||||||
* a variable that indicates if the Y asxis is the UP axis or not
|
* a variable that indicates if the Y asxis is the UP axis or not
|
||||||
*/
|
*/
|
||||||
public ConstraintHelper(String blenderVersion, BlenderContext blenderContext, boolean fixUpAxis) {
|
public ConstraintHelper(String blenderVersion, BlenderContext blenderContext, boolean fixUpAxis) {
|
||||||
super(blenderVersion, fixUpAxis);
|
super(blenderVersion, fixUpAxis);
|
||||||
|
this.blenderContext = blenderContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,7 +109,8 @@ public class ConstraintHelper extends AbstractBlenderHelper {
|
|||||||
List<Constraint> constraintsList = new ArrayList<Constraint>();
|
List<Constraint> constraintsList = new ArrayList<Constraint>();
|
||||||
Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());
|
Long boneOMA = Long.valueOf(((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress());
|
||||||
|
|
||||||
// the name is read directly from structure because bone might not yet be loaded
|
// the name is read directly from structure because bone might
|
||||||
|
// not yet be loaded
|
||||||
String name = blenderContext.getFileBlock(boneOMA).getStructure(blenderContext).getFieldValue("name").toString();
|
String name = blenderContext.getFileBlock(boneOMA).getStructure(blenderContext).getFieldValue("name").toString();
|
||||||
List<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(blenderContext);
|
List<Structure> constraints = ((Structure) poseChannel.getFieldValue("constraints")).evaluateListBase(blenderContext);
|
||||||
for (Structure constraint : constraints) {
|
for (Structure constraint : constraints) {
|
||||||
@ -130,7 +145,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
|
|||||||
ipo = ipoHelper.fromValue(enforce);
|
ipo = ipoHelper.fromValue(enforce);
|
||||||
}
|
}
|
||||||
|
|
||||||
constraintsList.add(this.getConstraint(dataType, constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext));
|
constraintsList.add(this.createConstraint(dataType, constraint, objectStructure.getOldMemoryAddress(), ipo, blenderContext));
|
||||||
}
|
}
|
||||||
blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList);
|
blenderContext.addConstraints(objectStructure.getOldMemoryAddress(), constraintsList);
|
||||||
}
|
}
|
||||||
@ -155,7 +170,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
|
|||||||
* @throws BlenderFileException
|
* @throws BlenderFileException
|
||||||
* thrown when problems with blender file occured
|
* thrown when problems with blender file occured
|
||||||
*/
|
*/
|
||||||
private Constraint getConstraint(String dataType, Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
|
private Constraint createConstraint(String dataType, Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
if (dataType == null || "Mesh".equalsIgnoreCase(dataType) || "Camera".equalsIgnoreCase(dataType) || "Lamp".equalsIgnoreCase(dataType)) {
|
if (dataType == null || "Mesh".equalsIgnoreCase(dataType) || "Camera".equalsIgnoreCase(dataType) || "Lamp".equalsIgnoreCase(dataType)) {
|
||||||
return new SpatialConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext);
|
return new SpatialConstraint(constraintStructure, ownerOMA, influenceIpo, blenderContext);
|
||||||
} else if ("Armature".equalsIgnoreCase(dataType)) {
|
} else if ("Armature".equalsIgnoreCase(dataType)) {
|
||||||
@ -172,269 +187,246 @@ public class ConstraintHelper extends AbstractBlenderHelper {
|
|||||||
* the blender context
|
* the blender context
|
||||||
*/
|
*/
|
||||||
public void bakeConstraints(BlenderContext blenderContext) {
|
public void bakeConstraints(BlenderContext blenderContext) {
|
||||||
|
List<SimulationNode> simulationRootNodes = new ArrayList<SimulationNode>();
|
||||||
for (Constraint constraint : blenderContext.getAllConstraints()) {
|
for (Constraint constraint : blenderContext.getAllConstraints()) {
|
||||||
constraint.bake();
|
boolean constraintUsed = false;
|
||||||
}
|
for (SimulationNode node : simulationRootNodes) {
|
||||||
}
|
if (node.contains(constraint)) {
|
||||||
|
constraintUsed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
if (!constraintUsed) {
|
||||||
* The method returns track for bone.
|
if (constraint instanceof BoneConstraint) {
|
||||||
*
|
BoneContext boneContext = blenderContext.getBoneContext(constraint.ownerOMA);
|
||||||
* @param bone
|
simulationRootNodes.add(new SimulationNode(boneContext.getArmatureObjectOMA(), blenderContext));
|
||||||
* the bone
|
} else if (constraint instanceof SpatialConstraint) {
|
||||||
* @param skeleton
|
Spatial spatial = (Spatial) blenderContext.getLoadedFeature(constraint.ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
||||||
* the bone's skeleton
|
while (spatial.getParent() != null) {
|
||||||
* @param animation
|
spatial = spatial.getParent();
|
||||||
* the bone's animation
|
}
|
||||||
* @return track for the given bone that was found among the given
|
simulationRootNodes.add(new SimulationNode((Long) spatial.getUserData("oma"), blenderContext));
|
||||||
* animations or null if none is found
|
} else {
|
||||||
*/
|
throw new IllegalStateException("Unsupported constraint type: " + constraint);
|
||||||
/* package */BoneTrack getTrack(Bone bone, Skeleton skeleton, Animation animation) {
|
}
|
||||||
int boneIndex = skeleton.getBoneIndex(bone);
|
|
||||||
for (Track track : animation.getTracks()) {
|
|
||||||
if (((BoneTrack) track).getTargetBoneIndex() == boneIndex) {
|
|
||||||
return (BoneTrack) track;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
for (SimulationNode node : simulationRootNodes) {
|
||||||
* The method returns track for spatial.
|
node.simulate();
|
||||||
*
|
|
||||||
* @param bone
|
|
||||||
* the spatial
|
|
||||||
* @param animation
|
|
||||||
* the spatial's animation
|
|
||||||
* @return track for the given spatial that was found among the given
|
|
||||||
* animations or null if none is found
|
|
||||||
*/
|
|
||||||
/* package */SpatialTrack getTrack(Spatial spatial, Animation animation) {
|
|
||||||
Track[] tracks = animation.getTracks();
|
|
||||||
if (tracks != null && tracks.length == 1) {
|
|
||||||
return (SpatialTrack) tracks[0];
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns the transform read directly from the blender
|
* The method retreives the transform from a feature in a given space.
|
||||||
* structure. This can be used to read transforms from one of the object
|
|
||||||
* types: <li>Spatial <li>Camera <li>Light
|
|
||||||
*
|
*
|
||||||
|
* @param oma
|
||||||
|
* the OMA of the feature (spatial or armature node)
|
||||||
|
* @param subtargetName
|
||||||
|
* the feature's subtarget (bone in a case of armature's node)
|
||||||
* @param space
|
* @param space
|
||||||
* the space where transform is evaluated
|
* the space the transform is evaluated to
|
||||||
* @param spatialOMA
|
* @return thensform of a feature in a given space
|
||||||
* the OMA of the object
|
|
||||||
* @param blenderContext
|
|
||||||
* the blender context
|
|
||||||
* @return the object's transform in a given space
|
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
public Transform getTransform(Long oma, String subtargetName, Space space) {
|
||||||
/* package */Transform getNodeObjectTransform(Space space, Long spatialOMA, BlenderContext blenderContext) {
|
Spatial feature = (Spatial) blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_FEATURE);
|
||||||
switch (space) {
|
boolean isArmature = feature.getUserData(ArmatureHelper.ARMETURE_NODE_MARKER) != null;
|
||||||
case CONSTRAINT_SPACE_LOCAL:
|
if (isArmature) {
|
||||||
Structure targetStructure = (Structure) blenderContext.getLoadedFeature(spatialOMA, LoadedFeatureDataType.LOADED_STRUCTURE);
|
BoneContext targetBoneContext = blenderContext.getBoneByName(subtargetName);
|
||||||
|
Bone bone = targetBoneContext.getBone();
|
||||||
|
|
||||||
DynamicArray<Number> locArray = ((DynamicArray<Number>) targetStructure.getFieldValue("loc"));
|
switch (space) {
|
||||||
Vector3f loc = new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), locArray.get(2).floatValue());
|
case CONSTRAINT_SPACE_WORLD:
|
||||||
DynamicArray<Number> rotArray = ((DynamicArray<Number>) targetStructure.getFieldValue("rot"));
|
Transform t = new Transform(bone.getModelSpacePosition(), bone.getModelSpaceRotation(), bone.getModelSpaceScale());
|
||||||
Quaternion rot = new Quaternion(new float[] { rotArray.get(0).floatValue(), rotArray.get(1).floatValue(), rotArray.get(2).floatValue() });
|
System.out.println("A: " + Arrays.toString(t.getRotation().toAngles(null)));
|
||||||
DynamicArray<Number> sizeArray = ((DynamicArray<Number>) targetStructure.getFieldValue("size"));
|
return t;
|
||||||
Vector3f size = new Vector3f(sizeArray.get(0).floatValue(), sizeArray.get(1).floatValue(), sizeArray.get(2).floatValue());
|
case CONSTRAINT_SPACE_LOCAL:
|
||||||
|
Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
|
||||||
|
localTransform.setScale(bone.getLocalScale());
|
||||||
|
return localTransform;
|
||||||
|
case CONSTRAINT_SPACE_POSE:
|
||||||
|
Node nodeWithAnimationControl = blenderContext.getControlledNode(targetBoneContext.getSkeleton());
|
||||||
|
Matrix4f m = this.toMatrix(nodeWithAnimationControl.getWorldTransform());
|
||||||
|
Matrix4f boneAgainstModifiedNodeMatrix = this.toMatrix(bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale());
|
||||||
|
Matrix4f boneWorldMatrix = m.multLocal(boneAgainstModifiedNodeMatrix);
|
||||||
|
|
||||||
if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform()).invertLocal();
|
||||||
float y = loc.y;
|
Matrix4f r2 = armatureWorldMatrix.multLocal(boneWorldMatrix);
|
||||||
loc.y = loc.z;
|
|
||||||
loc.z = -y;
|
|
||||||
|
|
||||||
y = rot.getY();
|
Vector3f loc2 = r2.toTranslationVector();
|
||||||
float z = rot.getZ();
|
Quaternion rot2 = r2.toRotationQuat().normalizeLocal().multLocal(POS_POSE_SPACE_QUATERNION);
|
||||||
rot.set(rot.getX(), z, -y, rot.getW());
|
Vector3f scl2 = r2.toScaleVector();
|
||||||
|
|
||||||
y = size.y;
|
return new Transform(loc2, rot2, scl2);
|
||||||
size.y = size.z;
|
case CONSTRAINT_SPACE_PARLOCAL:
|
||||||
size.z = y;
|
Matrix4f parentLocalMatrix = Matrix4f.IDENTITY;
|
||||||
}
|
if (bone.getParent() != null) {
|
||||||
|
Bone parent = bone.getParent();
|
||||||
|
parentLocalMatrix = this.toMatrix(parent.getLocalPosition(), parent.getLocalRotation(), parent.getLocalScale());
|
||||||
|
} else {// we need to clone it because otherwise we could
|
||||||
|
// spoil the IDENTITY matrix
|
||||||
|
parentLocalMatrix = parentLocalMatrix.clone();
|
||||||
|
}
|
||||||
|
Matrix4f boneLocalMatrix = this.toMatrix(bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale());
|
||||||
|
Matrix4f result = parentLocalMatrix.multLocal(boneLocalMatrix);
|
||||||
|
|
||||||
Transform result = new Transform(loc, rot);
|
Vector3f loc = result.toTranslationVector();
|
||||||
result.setScale(size);
|
Quaternion rot = result.toRotationQuat().normalizeLocal().multLocal(NEG_PARLOC_SPACE_QUATERNION);
|
||||||
return result;
|
Vector3f scl = result.toScaleVector();
|
||||||
case CONSTRAINT_SPACE_WORLD:// TODO: get it from the object structure ???
|
return new Transform(loc, rot, scl);
|
||||||
Object feature = blenderContext.getLoadedFeature(spatialOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
default:
|
||||||
if (feature instanceof Spatial) {
|
throw new IllegalStateException("Unknown space type: " + space);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (space) {
|
||||||
|
case CONSTRAINT_SPACE_LOCAL:
|
||||||
|
return ((Spatial) feature).getLocalTransform();
|
||||||
|
case CONSTRAINT_SPACE_WORLD:
|
||||||
return ((Spatial) feature).getWorldTransform();
|
return ((Spatial) feature).getWorldTransform();
|
||||||
} else if (feature instanceof Skeleton) {
|
case CONSTRAINT_SPACE_PARLOCAL:
|
||||||
LOGGER.warning("Trying to get transformation for skeleton. This is not supported. Returning null.");
|
case CONSTRAINT_SPACE_POSE:
|
||||||
return null;
|
throw new IllegalStateException("Nodes can have only Local and World spaces applied!");
|
||||||
} else {
|
default:
|
||||||
throw new IllegalArgumentException("Given old memory address does not point to a valid object type (spatial, camera or light).");
|
throw new IllegalStateException("Unknown space type: " + space);
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Invalid space type for target object: " + space.toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The method returns the transform for the given bone computed in the given
|
* Applies transform to a feature (bone or spatial). Computations transform
|
||||||
|
* the given transformation from the given space to the feature's local
|
||||||
* space.
|
* space.
|
||||||
*
|
*
|
||||||
|
* @param oma
|
||||||
|
* the OMA of the feature we apply transformation to
|
||||||
|
* @param subtargetName
|
||||||
|
* the name of the feature's subtarget (bone in case of armature)
|
||||||
* @param space
|
* @param space
|
||||||
* the computation space
|
* the space in which the given transform is to be applied
|
||||||
* @param bone
|
|
||||||
* the bone we get the transform from
|
|
||||||
* @return the transform of the given bone
|
|
||||||
*/
|
|
||||||
/* package */Transform getBoneTransform(Space space, Bone 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:
|
|
||||||
Transform worldTransform = new Transform(bone.getWorldBindPosition(), bone.getWorldBindRotation());
|
|
||||||
worldTransform.setScale(bone.getWorldBindScale());
|
|
||||||
return worldTransform;
|
|
||||||
case CONSTRAINT_SPACE_POSE:
|
|
||||||
Transform poseTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
|
|
||||||
poseTransform.setScale(bone.getLocalScale());
|
|
||||||
return poseTransform;
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The method applies the transform for the given spatial, computed in the
|
|
||||||
* given space.
|
|
||||||
*
|
|
||||||
* @param spatial
|
|
||||||
* the spatial we apply the transform for
|
|
||||||
* @param space
|
|
||||||
* the computation space
|
|
||||||
* @param transform
|
* @param transform
|
||||||
* the transform being applied
|
* the transform we apply
|
||||||
*/
|
*/
|
||||||
/* package */void applyTransform(Spatial spatial, Space space, Transform transform) {
|
public void applyTransform(Long oma, String subtargetName, Space space, Transform transform) {
|
||||||
switch (space) {
|
Spatial feature = (Spatial) blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_FEATURE);
|
||||||
case CONSTRAINT_SPACE_LOCAL:
|
boolean isArmature = feature.getUserData(ArmatureHelper.ARMETURE_NODE_MARKER) != null;
|
||||||
Transform ownerLocalTransform = spatial.getLocalTransform();
|
if (isArmature) {
|
||||||
ownerLocalTransform.getTranslation().addLocal(transform.getTranslation());
|
Skeleton skeleton = blenderContext.getSkeleton(oma);
|
||||||
ownerLocalTransform.getRotation().multLocal(transform.getRotation());
|
BoneContext targetBoneContext = blenderContext.getBoneByName(subtargetName);
|
||||||
ownerLocalTransform.getScale().multLocal(transform.getScale());
|
Bone bone = targetBoneContext.getBone();
|
||||||
break;
|
|
||||||
case CONSTRAINT_SPACE_WORLD:
|
|
||||||
Matrix4f m = this.getParentWorldTransformMatrix(spatial);
|
|
||||||
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);
|
Node nodeControlledBySkeleton = blenderContext.getControlledNode(skeleton);
|
||||||
float scaleY = (float) Math.sqrt(m.m01 * m.m01 + m.m11 * m.m11 + m.m21 * m.m21);
|
Transform nodeTransform = nodeControlledBySkeleton.getWorldTransform();
|
||||||
float scaleZ = (float) Math.sqrt(m.m02 * m.m02 + m.m12 * m.m12 + m.m22 * m.m22);
|
Matrix4f invertedNodeMatrix = this.toMatrix(nodeTransform).invertLocal();
|
||||||
|
|
||||||
transform.setTranslation(m.toTranslationVector());
|
switch (space) {
|
||||||
transform.setRotation(m.toRotationQuat());
|
case CONSTRAINT_SPACE_LOCAL:
|
||||||
transform.setScale(scaleX, scaleY, scaleZ);
|
bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
|
||||||
spatial.setLocalTransform(transform);
|
break;
|
||||||
break;
|
case CONSTRAINT_SPACE_WORLD:
|
||||||
case CONSTRAINT_SPACE_PARLOCAL:
|
System.out.println("B: " + Arrays.toString(transform.getRotation().toAngles(null)));
|
||||||
case CONSTRAINT_SPACE_POSE:
|
Matrix4f boneMatrix = this.toMatrix(transform);
|
||||||
throw new IllegalStateException("Invalid space type (" + space.toString() + ") for owner object.");
|
Bone parent = bone.getParent();
|
||||||
default:
|
if (parent != null) {
|
||||||
throw new IllegalStateException("Invalid space type for target object: " + space.toString());
|
Matrix4f invertedParentWorldMatrix = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale()).invertLocal();
|
||||||
|
boneMatrix = invertedParentWorldMatrix.multLocal(boneMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
boneMatrix = invertedNodeMatrix.multLocal(boneMatrix);
|
||||||
|
bone.setBindTransforms(boneMatrix.toTranslationVector(), boneMatrix.toRotationQuat(), boneMatrix.toScaleVector());
|
||||||
|
break;
|
||||||
|
case CONSTRAINT_SPACE_POSE:
|
||||||
|
Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform());
|
||||||
|
Matrix4f bonePoseMatrix = this.toMatrix(transform);
|
||||||
|
Matrix4f boneWorldMatrix = armatureWorldMatrix.multLocal(bonePoseMatrix);
|
||||||
|
// now compute bone's local matrix
|
||||||
|
Matrix4f boneLocalMatrix = invertedNodeMatrix.multLocal(boneWorldMatrix);
|
||||||
|
|
||||||
|
Vector3f loc2 = boneLocalMatrix.toTranslationVector();
|
||||||
|
Quaternion rot2 = boneLocalMatrix.toRotationQuat().normalizeLocal().multLocal(new Quaternion(NEG_POSE_SPACE_QUATERNION));
|
||||||
|
Vector3f scl2 = boneLocalMatrix.toScaleVector();
|
||||||
|
bone.setBindTransforms(loc2, rot2, scl2);
|
||||||
|
break;
|
||||||
|
case CONSTRAINT_SPACE_PARLOCAL:
|
||||||
|
Matrix4f parentLocalMatrix = Matrix4f.IDENTITY;
|
||||||
|
if (bone.getParent() != null) {
|
||||||
|
parentLocalMatrix = this.toMatrix(bone.getParent().getLocalPosition(), bone.getParent().getLocalRotation(), bone.getParent().getLocalScale());
|
||||||
|
parentLocalMatrix.invertLocal();
|
||||||
|
} else {// we need to clone it because otherwise we could
|
||||||
|
// spoil the IDENTITY matrix
|
||||||
|
parentLocalMatrix = parentLocalMatrix.clone();
|
||||||
|
}
|
||||||
|
Matrix4f m = this.toMatrix(transform.getTranslation(), transform.getRotation(), transform.getScale());
|
||||||
|
Matrix4f result = parentLocalMatrix.multLocal(m);
|
||||||
|
Vector3f loc = result.toTranslationVector();
|
||||||
|
Quaternion rot = result.toRotationQuat().normalizeLocal().multLocal(POS_PARLOC_SPACE_QUATERNION);
|
||||||
|
Vector3f scl = result.toScaleVector();
|
||||||
|
bone.setBindTransforms(loc, rot, scl);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Invalid space type for target object: " + space.toString());
|
||||||
|
}
|
||||||
|
} else if (feature instanceof Spatial) {
|
||||||
|
Spatial spatial = (Spatial) feature;
|
||||||
|
switch (space) {
|
||||||
|
case CONSTRAINT_SPACE_LOCAL:
|
||||||
|
spatial.getLocalTransform().set(transform);
|
||||||
|
break;
|
||||||
|
case CONSTRAINT_SPACE_WORLD:
|
||||||
|
if (spatial.getParent() == null) {
|
||||||
|
spatial.setLocalTransform(transform);
|
||||||
|
} else {
|
||||||
|
Transform parentWorldTransform = spatial.getParent().getWorldTransform();
|
||||||
|
|
||||||
|
Matrix4f parentMatrix = this.toMatrix(parentWorldTransform).invertLocal();
|
||||||
|
Matrix4f m = this.toMatrix(transform);
|
||||||
|
m = m.multLocal(parentMatrix);
|
||||||
|
|
||||||
|
transform.setTranslation(m.toTranslationVector());
|
||||||
|
transform.setRotation(m.toRotationQuat());
|
||||||
|
transform.setScale(m.toScaleVector());
|
||||||
|
|
||||||
|
spatial.setLocalTransform(transform);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Invalid space type for spatial object: " + space.toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Constrained transformation can be applied only to Bone or Spatial feature!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The method applies the transform for the given bone, computed in the
|
|
||||||
* given space.
|
|
||||||
*
|
|
||||||
* @param bone
|
|
||||||
* the bone we apply the transform for
|
|
||||||
* @param space
|
|
||||||
* the computation space
|
|
||||||
* @param transform
|
|
||||||
* the transform being applied
|
|
||||||
*/
|
|
||||||
/* package */void applyTransform(Bone bone, Space space, Transform transform) {
|
|
||||||
switch (space) {
|
|
||||||
case CONSTRAINT_SPACE_LOCAL:
|
|
||||||
bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
|
|
||||||
break;
|
|
||||||
case CONSTRAINT_SPACE_WORLD:
|
|
||||||
Matrix4f m = this.getParentWorldTransformMatrix(bone);
|
|
||||||
// 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());
|
|
||||||
// float x = FastMath.HALF_PI/2;
|
|
||||||
// float y = -FastMath.HALF_PI;
|
|
||||||
// float z = -FastMath.HALF_PI/2;
|
|
||||||
// bone.setBindTransforms(new Vector3f(0,0,0), new Quaternion().fromAngles(x, y, z), new Vector3f(1,1,1));
|
|
||||||
break;
|
|
||||||
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:
|
|
||||||
bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Invalid space type for target object: " + space.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return world transform matrix of the feature's parent or identity matrix
|
|
||||||
* if the feature has no parent
|
|
||||||
*/
|
|
||||||
private Matrix4f getParentWorldTransformMatrix(Spatial spatial) {
|
|
||||||
Matrix4f result = new Matrix4f();
|
|
||||||
if (spatial.getParent() != null) {
|
|
||||||
Transform t = spatial.getParent().getWorldTransform();
|
|
||||||
result.setTransform(t.getTranslation(), t.getScale(), t.getRotation().toRotationMatrix());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return world transform matrix of the feature's parent or identity matrix
|
|
||||||
* if the feature has no parent
|
|
||||||
*/
|
|
||||||
private Matrix4f getParentWorldTransformMatrix(Bone bone) {
|
|
||||||
Matrix4f result = new Matrix4f();
|
|
||||||
Bone parent = bone.getParent();
|
|
||||||
if (parent != null) {
|
|
||||||
result.setTransform(parent.getWorldBindPosition(), parent.getWorldBindScale(), parent.getWorldBindRotation().toRotationMatrix());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts given transform to the matrix.
|
* Converts given transform to the matrix.
|
||||||
*
|
*
|
||||||
* @param transform
|
* @param transform
|
||||||
* the transform to be converted
|
* the transform to be converted
|
||||||
* @return 4x4 matri that represents the given transform
|
* @return 4x4 matrix that represents the given transform
|
||||||
*/
|
*/
|
||||||
private Matrix4f toMatrix(Transform transform) {
|
private Matrix4f toMatrix(Transform transform) {
|
||||||
Matrix4f result = Matrix4f.IDENTITY;
|
Matrix4f result = Matrix4f.IDENTITY;
|
||||||
if (transform != null) {
|
if (transform != null) {
|
||||||
result = new Matrix4f();
|
result = this.toMatrix(transform.getTranslation(), transform.getRotation(), transform.getScale());
|
||||||
result.setTranslation(transform.getTranslation());
|
|
||||||
result.setRotationQuaternion(transform.getRotation());
|
|
||||||
result.setScale(transform.getScale());
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts given transformation parameters into the matrix.
|
||||||
|
*
|
||||||
|
* @param transform
|
||||||
|
* the transform to be converted
|
||||||
|
* @return 4x4 matrix that represents the given transformation parameters
|
||||||
|
*/
|
||||||
|
private Matrix4f toMatrix(Vector3f position, Quaternion rotation, Vector3f scale) {
|
||||||
|
Matrix4f result = new Matrix4f();
|
||||||
|
result.setTranslation(position);
|
||||||
|
result.setRotationQuaternion(rotation);
|
||||||
|
result.setScale(scale);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
|
public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
|
||||||
return true;
|
return true;
|
||||||
@ -447,7 +439,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
|
|||||||
*/
|
*/
|
||||||
public static enum Space {
|
public static enum Space {
|
||||||
|
|
||||||
CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL, CONSTRAINT_SPACE_INVALID;
|
CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns the enum instance when given the appropriate
|
* This method returns the enum instance when given the appropriate
|
||||||
@ -468,7 +460,7 @@ public class ConstraintHelper extends AbstractBlenderHelper {
|
|||||||
case 3:
|
case 3:
|
||||||
return CONSTRAINT_SPACE_PARLOCAL;
|
return CONSTRAINT_SPACE_PARLOCAL;
|
||||||
default:
|
default:
|
||||||
return CONSTRAINT_SPACE_INVALID;
|
throw new IllegalArgumentException("Value: " + c + " cannot be converted to Space enum instance!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,415 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import com.jme3.animation.AnimChannel;
|
||||||
|
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.SpatialTrack;
|
||||||
|
import com.jme3.animation.Track;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Transform;
|
||||||
|
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.ArmatureHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
|
import com.jme3.util.TempVars;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A node that represents either spatial or bone in constraint simulation. The
|
||||||
|
* node is applied its translation, rotation and scale for each frame of its
|
||||||
|
* animation. Then the constraints are applied that will eventually alter it.
|
||||||
|
* After that the feature's transformation is stored in VirtualTrack which is
|
||||||
|
* converted to new bone or spatial track at the very end.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public class SimulationNode {
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(SimulationNode.class.getName());
|
||||||
|
|
||||||
|
/** The name of the node (for debugging purposes). */
|
||||||
|
private String name;
|
||||||
|
/** A list of children for the node (either bones or child spatials). */
|
||||||
|
private List<SimulationNode> children = new ArrayList<SimulationNode>();
|
||||||
|
/** A virtual track for each of the nodes. */
|
||||||
|
private Map<String, VirtualTrack> virtualTrack = new HashMap<String, VirtualTrack>();
|
||||||
|
/** A list of constraints that the current node has. */
|
||||||
|
private List<Constraint> constraints;
|
||||||
|
/** A list of node's animations. */
|
||||||
|
private List<Animation> animations;
|
||||||
|
|
||||||
|
/** The nodes spatial (if null then the boneContext should be set). */
|
||||||
|
private Spatial spatial;
|
||||||
|
/** The skeleton of the bone (not null if the node simulated the bone). */
|
||||||
|
private Skeleton skeleton;
|
||||||
|
/** Animation controller for the node's feature. */
|
||||||
|
private AnimControl animControl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The star transform of a spatial. Needed to properly reset the spatial to
|
||||||
|
* its start position.
|
||||||
|
*/
|
||||||
|
private Transform spatialStartTransform;
|
||||||
|
/** Star transformations for bones. Needed to properly reset the bones. */
|
||||||
|
private Map<Bone, Transform> boneStartTransforms;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the nodes tree for the given feature. The feature (bone or
|
||||||
|
* spatial) is found by its OMA. The feature must be a root bone or a root
|
||||||
|
* spatial.
|
||||||
|
*
|
||||||
|
* @param featureOMA
|
||||||
|
* the OMA of either bone or spatial
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
*/
|
||||||
|
public SimulationNode(Long featureOMA, BlenderContext blenderContext) {
|
||||||
|
this(featureOMA, blenderContext, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the node for the feature.
|
||||||
|
*
|
||||||
|
* @param featureOMA
|
||||||
|
* the OMA of either bone or spatial
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @param rootNode
|
||||||
|
* indicates if the feature is a root bone or root spatial or not
|
||||||
|
*/
|
||||||
|
private SimulationNode(Long featureOMA, BlenderContext blenderContext, boolean rootNode) {
|
||||||
|
Node spatial = (Node) blenderContext.getLoadedFeature(featureOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
||||||
|
if (spatial.getUserData(ArmatureHelper.ARMETURE_NODE_MARKER) != null) {
|
||||||
|
this.skeleton = blenderContext.getSkeleton(featureOMA);
|
||||||
|
|
||||||
|
Node nodeWithAnimationControl = blenderContext.getControlledNode(skeleton);
|
||||||
|
this.animControl = nodeWithAnimationControl.getControl(AnimControl.class);
|
||||||
|
|
||||||
|
boneStartTransforms = new HashMap<Bone, Transform>();
|
||||||
|
for (int i = 0; i < skeleton.getBoneCount(); ++i) {
|
||||||
|
Bone bone = skeleton.getBone(i);
|
||||||
|
boneStartTransforms.put(bone, new Transform(bone.getWorldBindPosition(), bone.getWorldBindRotation(), bone.getWorldBindScale()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (rootNode && spatial.getParent() != null) {
|
||||||
|
throw new IllegalStateException("Given spatial must be a root node!");
|
||||||
|
}
|
||||||
|
this.spatial = spatial;
|
||||||
|
this.spatialStartTransform = spatial.getLocalTransform().clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = '>' + spatial.getName() + '<';
|
||||||
|
|
||||||
|
constraints = this.findConstraints(featureOMA, blenderContext);
|
||||||
|
if (constraints == null) {
|
||||||
|
constraints = new ArrayList<Constraint>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// add children nodes
|
||||||
|
if (skeleton != null) {
|
||||||
|
// bone with index 0 is a root bone and should not be considered
|
||||||
|
// here
|
||||||
|
for (int i = 1; i < skeleton.getBoneCount(); ++i) {
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(skeleton.getBone(i));
|
||||||
|
List<Constraint> boneConstraints = this.findConstraints(boneContext.getBoneOma(), blenderContext);
|
||||||
|
if (boneConstraints != null) {
|
||||||
|
constraints.addAll(boneConstraints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// each bone of the skeleton has the same anim data applied
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(skeleton.getBone(1));
|
||||||
|
Long boneOma = boneContext.getBoneOma();
|
||||||
|
animations = blenderContext.getAnimData(boneOma) == null ? null : blenderContext.getAnimData(boneOma).anims;
|
||||||
|
} else {
|
||||||
|
animations = blenderContext.getAnimData(featureOMA) == null ? null : blenderContext.getAnimData(featureOMA).anims;
|
||||||
|
for (Spatial child : spatial.getChildren()) {
|
||||||
|
if (child instanceof Node) {
|
||||||
|
children.add(new SimulationNode((Long) child.getUserData("oma"), blenderContext, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.info("Removing invalid constraints.");
|
||||||
|
List<Constraint> validConstraints = new ArrayList<Constraint>(constraints.size());
|
||||||
|
for (Constraint constraint : this.constraints) {
|
||||||
|
if (constraint.validate()) {
|
||||||
|
validConstraints.add(constraint);
|
||||||
|
} else {
|
||||||
|
LOGGER.log(Level.WARNING, "Constraint {0} is invalid and will not be applied.", constraint.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.constraints = validConstraints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells if the node already contains the given constraint (so that it is
|
||||||
|
* not applied twice).
|
||||||
|
*
|
||||||
|
* @param constraint
|
||||||
|
* the constraint to be checked
|
||||||
|
* @return <b>true</b> if the constraint already is stored in the node and
|
||||||
|
* <b>false</b> otherwise
|
||||||
|
*/
|
||||||
|
public boolean contains(Constraint constraint) {
|
||||||
|
boolean result = false;
|
||||||
|
if (constraints != null && constraints.size() > 0) {
|
||||||
|
for (Constraint c : constraints) {
|
||||||
|
if (c.equals(constraint)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the node's feature to its starting transformation.
|
||||||
|
*/
|
||||||
|
private void reset() {
|
||||||
|
if (spatial != null) {
|
||||||
|
spatial.setLocalTransform(spatialStartTransform);
|
||||||
|
for (SimulationNode child : children) {
|
||||||
|
child.reset();
|
||||||
|
}
|
||||||
|
} else if (skeleton != null) {
|
||||||
|
for (Entry<Bone, Transform> entry : boneStartTransforms.entrySet()) {
|
||||||
|
Transform t = entry.getValue();
|
||||||
|
entry.getKey().setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale());
|
||||||
|
}
|
||||||
|
skeleton.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates the spatial node.
|
||||||
|
*/
|
||||||
|
private void simulateSpatial() {
|
||||||
|
if (constraints != null && constraints.size() > 0) {
|
||||||
|
boolean applyStaticConstraints = true;
|
||||||
|
if (animations != null) {
|
||||||
|
for (Animation animation : animations) {
|
||||||
|
float[] animationTimeBoundaries = computeAnimationTimeBoundaries(animation);
|
||||||
|
int maxFrame = (int) animationTimeBoundaries[0];
|
||||||
|
float maxTime = animationTimeBoundaries[1];
|
||||||
|
|
||||||
|
VirtualTrack vTrack = new VirtualTrack(maxFrame, maxTime);
|
||||||
|
virtualTrack.put(animation.getName(), vTrack);
|
||||||
|
for (Track track : animation.getTracks()) {
|
||||||
|
for (int frame = 0; frame < maxFrame; ++frame) {
|
||||||
|
spatial.setLocalTranslation(((SpatialTrack) track).getTranslations()[frame]);
|
||||||
|
spatial.setLocalRotation(((SpatialTrack) track).getRotations()[frame]);
|
||||||
|
spatial.setLocalScale(((SpatialTrack) track).getScales()[frame]);
|
||||||
|
|
||||||
|
for (Constraint constraint : constraints) {
|
||||||
|
constraint.apply(frame);
|
||||||
|
vTrack.setTransform(frame, spatial.getLocalTransform());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Track newTrack = vTrack.getAsSpatialTrack();
|
||||||
|
if (newTrack != null) {
|
||||||
|
animation.removeTrack(track);
|
||||||
|
animation.addTrack(newTrack);
|
||||||
|
}
|
||||||
|
applyStaticConstraints = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there are no animations then just constraint the static
|
||||||
|
// object's transformation
|
||||||
|
if (applyStaticConstraints) {
|
||||||
|
for (Constraint constraint : constraints) {
|
||||||
|
constraint.apply(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SimulationNode child : children) {
|
||||||
|
child.simulate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates the bone node.
|
||||||
|
*/
|
||||||
|
private void simulateSkeleton() {
|
||||||
|
if (constraints != null && constraints.size() > 0) {
|
||||||
|
boolean applyStaticConstraints = true;
|
||||||
|
|
||||||
|
if (animations != null) {
|
||||||
|
TempVars vars = TempVars.get();
|
||||||
|
AnimChannel animChannel = animControl.createChannel();
|
||||||
|
for (Animation animation : animations) {
|
||||||
|
float[] animationTimeBoundaries = this.computeAnimationTimeBoundaries(animation);
|
||||||
|
int maxFrame = (int) animationTimeBoundaries[0];
|
||||||
|
float maxTime = animationTimeBoundaries[1];
|
||||||
|
|
||||||
|
Map<Integer, VirtualTrack> tracks = new HashMap<Integer, VirtualTrack>();
|
||||||
|
Map<Integer, Transform> previousTransforms = new HashMap<Integer, Transform>();
|
||||||
|
for (int frame = 0; frame < maxFrame; ++frame) {
|
||||||
|
this.reset();// this MUST be done here, otherwise
|
||||||
|
// setting next frame of animation will
|
||||||
|
// lead to possible errors
|
||||||
|
// first set proper time for all bones in all the tracks
|
||||||
|
// ...
|
||||||
|
for (Track track : animation.getTracks()) {
|
||||||
|
float time = ((BoneTrack) track).getTimes()[frame];
|
||||||
|
Integer boneIndex = ((BoneTrack) track).getTargetBoneIndex();
|
||||||
|
|
||||||
|
track.setTime(time, 1, animControl, animChannel, vars);
|
||||||
|
skeleton.updateWorldVectors();
|
||||||
|
|
||||||
|
Transform previousTransform = previousTransforms.get(boneIndex);
|
||||||
|
if (previousTransform == null) {
|
||||||
|
Bone bone = skeleton.getBone(boneIndex);
|
||||||
|
previousTransform = new Transform();
|
||||||
|
previousTransform.setTranslation(bone.getLocalPosition());
|
||||||
|
previousTransform.setRotation(bone.getLocalRotation());
|
||||||
|
previousTransform.setScale(bone.getLocalScale());
|
||||||
|
previousTransforms.put(boneIndex, previousTransform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... and then apply constraints ...
|
||||||
|
for (Constraint constraint : constraints) {
|
||||||
|
constraint.apply(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... and fill in another frame in the result track
|
||||||
|
for (Track track : animation.getTracks()) {
|
||||||
|
Integer boneIndex = ((BoneTrack) track).getTargetBoneIndex();
|
||||||
|
Bone bone = skeleton.getBone(boneIndex);
|
||||||
|
|
||||||
|
// take the initial transform of a bone
|
||||||
|
Transform previousTransform = previousTransforms.get(boneIndex);
|
||||||
|
|
||||||
|
VirtualTrack vTrack = tracks.get(boneIndex);
|
||||||
|
if (vTrack == null) {
|
||||||
|
vTrack = new VirtualTrack(maxFrame, maxTime);
|
||||||
|
tracks.put(boneIndex, vTrack);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f bonePositionDifference = bone.getLocalPosition().subtract(previousTransform.getTranslation());
|
||||||
|
Quaternion boneRotationDifference = bone.getLocalRotation().mult(previousTransform.getRotation().inverse()).normalizeLocal();
|
||||||
|
Vector3f boneScaleDifference = bone.getLocalScale().divide(previousTransform.getScale());
|
||||||
|
if (frame > 0) {
|
||||||
|
bonePositionDifference = vTrack.translations.get(frame - 1).add(bonePositionDifference);
|
||||||
|
boneRotationDifference = vTrack.rotations.get(frame - 1).mult(boneRotationDifference);
|
||||||
|
boneScaleDifference = vTrack.scales.get(frame - 1).mult(boneScaleDifference);
|
||||||
|
}
|
||||||
|
vTrack.setTransform(frame, new Transform(bonePositionDifference, boneRotationDifference, boneScaleDifference));
|
||||||
|
|
||||||
|
previousTransform.setTranslation(bone.getLocalPosition());
|
||||||
|
previousTransform.setRotation(bone.getLocalRotation());
|
||||||
|
previousTransform.setScale(bone.getLocalScale());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
|
||||||
|
Track newTrack = trackEntry.getValue().getAsBoneTrack(trackEntry.getKey());
|
||||||
|
if (newTrack != null) {
|
||||||
|
for (Track track : animation.getTracks()) {
|
||||||
|
if (((BoneTrack) track).getTargetBoneIndex() == trackEntry.getKey().intValue()) {
|
||||||
|
animation.removeTrack(track);
|
||||||
|
animation.addTrack(newTrack);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
applyStaticConstraints = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vars.release();
|
||||||
|
animControl.clearChannels();
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there are no animations then just constraint the static
|
||||||
|
// object's transformation
|
||||||
|
if (applyStaticConstraints) {
|
||||||
|
for (Constraint constraint : constraints) {
|
||||||
|
constraint.apply(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates the node.
|
||||||
|
*/
|
||||||
|
public void simulate() {
|
||||||
|
this.reset();
|
||||||
|
if (spatial != null) {
|
||||||
|
this.simulateSpatial();
|
||||||
|
} else {
|
||||||
|
this.simulateSkeleton();
|
||||||
|
}
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the maximum frame and time for the animation. Different tracks
|
||||||
|
* can have different lengths so here the maximum one is being found.
|
||||||
|
*
|
||||||
|
* @param animation
|
||||||
|
* the animation
|
||||||
|
* @return maximum frame and time of the animation
|
||||||
|
*/
|
||||||
|
private float[] computeAnimationTimeBoundaries(Animation animation) {
|
||||||
|
int maxFrame = Integer.MIN_VALUE;
|
||||||
|
float maxTime = Float.MIN_VALUE;
|
||||||
|
for (Track track : animation.getTracks()) {
|
||||||
|
if (track instanceof BoneTrack) {
|
||||||
|
maxFrame = Math.max(maxFrame, ((BoneTrack) track).getTranslations().length);
|
||||||
|
maxTime = Math.max(maxTime, ((BoneTrack) track).getTimes()[((BoneTrack) track).getTimes().length - 1]);
|
||||||
|
} else if (track instanceof SpatialTrack) {
|
||||||
|
maxFrame = Math.max(maxFrame, ((SpatialTrack) track).getTranslations().length);
|
||||||
|
maxTime = Math.max(maxTime, ((SpatialTrack) track).getTimes()[((SpatialTrack) track).getTimes().length - 1]);
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Unsupported track type for simuation: " + track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new float[] { maxFrame, maxTime };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds constraints for the node's features.
|
||||||
|
*
|
||||||
|
* @param ownerOMA
|
||||||
|
* the feature's OMA
|
||||||
|
* @param blenderContext
|
||||||
|
* the blender context
|
||||||
|
* @return a list of feature's constraints or empty list if none were found
|
||||||
|
*/
|
||||||
|
private List<Constraint> findConstraints(Long ownerOMA, BlenderContext blenderContext) {
|
||||||
|
List<Constraint> result = new ArrayList<Constraint>();
|
||||||
|
for (Constraint constraint : blenderContext.getAllConstraints()) {
|
||||||
|
if (constraint.ownerOMA.longValue() == ownerOMA.longValue()) {
|
||||||
|
if (constraint.isImplemented()) {
|
||||||
|
result.add(constraint);
|
||||||
|
} else {
|
||||||
|
LOGGER.log(Level.WARNING, "Constraint named: ''{0}'' of type ''{1}'' is not implemented and will NOT be applied!", new Object[] { constraint.name, constraint.getConstraintTypeName() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.size() > 0 ? result : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
@ -23,16 +23,13 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void performBakingOperation() {
|
public boolean validate() {
|
||||||
|
LOGGER.warning("Constraints for skeleton are not supported.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(int frame) {
|
||||||
LOGGER.warning("Applying constraints to skeleton is not supported.");
|
LOGGER.warning("Applying constraints to skeleton is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean validate() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void prepareTracksForApplyingConstraints() {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,25 @@
|
|||||||
package com.jme3.scene.plugins.blender.constraints;
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
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.SpatialTrack;
|
|
||||||
import com.jme3.animation.Track;
|
|
||||||
import com.jme3.math.Quaternion;
|
|
||||||
import com.jme3.math.Transform;
|
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;
|
||||||
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
|
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
|
||||||
import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
|
|
||||||
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
|
||||||
import com.jme3.scene.plugins.blender.animations.Ipo;
|
import com.jme3.scene.plugins.blender.animations.Ipo;
|
||||||
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
|
||||||
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
import com.jme3.scene.plugins.ogre.AnimData;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constraint applied on the spatial objects.
|
* Constraint applied on the spatial objects. This includes: nodes, cameras
|
||||||
* This includes: nodes, cameras nodes and light nodes.
|
* nodes and light nodes.
|
||||||
|
*
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
/* package */class SpatialConstraint extends Constraint {
|
/* package */class SpatialConstraint extends Constraint {
|
||||||
private static final Logger LOGGER = Logger.getLogger(SpatialConstraint.class.getName());
|
|
||||||
|
|
||||||
/** The owner of the constraint. */
|
|
||||||
private Spatial owner;
|
|
||||||
/** The target of the constraint. */
|
|
||||||
private Object target;
|
|
||||||
|
|
||||||
public SpatialConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
|
public SpatialConstraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
|
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean validate() {
|
public boolean validate() {
|
||||||
if (targetOMA != null) {
|
if (targetOMA != null) {
|
||||||
return blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE) != null;
|
return blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE) != null;
|
||||||
}
|
}
|
||||||
@ -51,154 +27,10 @@ import com.jme3.scene.plugins.ogre.AnimData;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void performBakingOperation() {
|
public void apply(int frame) {
|
||||||
this.owner = (Spatial) blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
Transform ownerTransform = constraintHelper.getTransform(ownerOMA, null, ownerSpace);
|
||||||
this.target = targetOMA != null ? blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE) : null;
|
Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
|
||||||
this.prepareTracksForApplyingConstraints();
|
constraintDefinition.bake(ownerTransform, targetTransform, this.ipo.calculateValue(frame));
|
||||||
|
constraintHelper.applyTransform(ownerOMA, subtargetName, ownerSpace, ownerTransform);
|
||||||
// apply static constraint
|
|
||||||
Transform ownerTransform = constraintHelper.getNodeObjectTransform(ownerSpace, ownerOMA, blenderContext);
|
|
||||||
Transform targetTransform = targetOMA != null ? constraintHelper.getNodeObjectTransform(targetSpace, targetOMA, blenderContext) : null;
|
|
||||||
constraintDefinition.bake(ownerTransform, targetTransform, null, null, this.ipo);
|
|
||||||
constraintHelper.applyTransform(owner, ownerSpace, ownerTransform);
|
|
||||||
|
|
||||||
// apply dynamic constraint
|
|
||||||
AnimData animData = blenderContext.getAnimData(ownerOMA);
|
|
||||||
if (animData != null) {
|
|
||||||
for (Animation animation : animData.anims) {
|
|
||||||
SpatialTrack ownerTrack = constraintHelper.getTrack(owner, animation);
|
|
||||||
|
|
||||||
AnimData targetAnimData = blenderContext.getAnimData(targetOMA);
|
|
||||||
SpatialTrack targetTrack = null;
|
|
||||||
if (targetAnimData != null) {
|
|
||||||
targetTrack = constraintHelper.getTrack((Spatial) target, targetAnimData.anims.get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
constraintDefinition.bake(ownerTransform, targetTransform, ownerTrack, targetTrack, this.ipo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void prepareTracksForApplyingConstraints() {
|
|
||||||
Long[] spatialsOMAs = new Long[] { ownerOMA, targetOMA };
|
|
||||||
Space[] spaces = new Space[] { ownerSpace, targetSpace };
|
|
||||||
|
|
||||||
// creating animations for current objects if at least on of their parents have an animation
|
|
||||||
for (int i = 0; i < spatialsOMAs.length; ++i) {
|
|
||||||
Long oma = spatialsOMAs[i];
|
|
||||||
if (oma != null && oma > 0L) {
|
|
||||||
AnimData animData = blenderContext.getAnimData(oma);
|
|
||||||
if (animData == null) {
|
|
||||||
Spatial currentSpatial = (Spatial) blenderContext.getLoadedFeature(oma, LoadedFeatureDataType.LOADED_FEATURE);
|
|
||||||
if (currentSpatial != null) {
|
|
||||||
if (currentSpatial.getUserData(ArmatureHelper.ARMETURE_NODE_MARKER) == Boolean.TRUE) {// look for it among bones
|
|
||||||
BoneContext currentBoneContext = blenderContext.getBoneByName(subtargetName);
|
|
||||||
Bone currentBone = currentBoneContext.getBone();
|
|
||||||
Bone parent = currentBone.getParent();
|
|
||||||
boolean foundAnimation = false;
|
|
||||||
while (parent != null && !foundAnimation) {
|
|
||||||
BoneContext boneContext = blenderContext.getBoneByName(parent.getName());
|
|
||||||
foundAnimation = this.hasAnimation(boneContext.getBoneOma());
|
|
||||||
animData = blenderContext.getAnimData(boneContext.getBoneOma());
|
|
||||||
parent = parent.getParent();
|
|
||||||
}
|
|
||||||
if (foundAnimation) {
|
|
||||||
this.applyAnimData(currentBoneContext, spaces[i], animData);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Spatial parent = currentSpatial.getParent();
|
|
||||||
while (parent != null && animData == null) {
|
|
||||||
Structure parentStructure = (Structure) blenderContext.getLoadedFeature(parent.getName(), LoadedFeatureDataType.LOADED_STRUCTURE);
|
|
||||||
if (parentStructure == null) {
|
|
||||||
parent = null;
|
|
||||||
} else {
|
|
||||||
Long parentOma = parentStructure.getOldMemoryAddress();
|
|
||||||
animData = blenderContext.getAnimData(parentOma);
|
|
||||||
parent = parent.getParent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (animData != null) {// create anim data for the current object
|
|
||||||
this.applyAnimData(currentSpatial, oma, spaces[i], animData.anims.get(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOGGER.warning("Couldn't find target object for constraint: " + name + ". Make sure that the target is on layer that is defined to be loaded in blender key!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// creating animation for owner if it doesn't have one already and if the target has it
|
|
||||||
AnimData animData = blenderContext.getAnimData(ownerOMA);
|
|
||||||
if (animData == null) {
|
|
||||||
AnimData targetAnimData = blenderContext.getAnimData(targetOMA);
|
|
||||||
if (targetAnimData != null) {
|
|
||||||
this.applyAnimData(owner, ownerOMA, ownerSpace, targetAnimData.anims.get(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The method determines if the bone has animations.
|
|
||||||
*
|
|
||||||
* @param boneOMA
|
|
||||||
* OMA of the animation's owner
|
|
||||||
* @return <b>true</b> if the target has animations and <b>false</b> otherwise
|
|
||||||
*/
|
|
||||||
protected boolean hasAnimation(Long boneOMA) {
|
|
||||||
AnimData animData = blenderContext.getAnimData(boneOMA);
|
|
||||||
if (animData != null) {
|
|
||||||
Bone bone = blenderContext.getBoneContext(boneOMA).getBone();
|
|
||||||
int boneIndex = animData.skeleton.getBoneIndex(bone);
|
|
||||||
for (Animation animation : animData.anims) {
|
|
||||||
for (Track track : animation.getTracks()) {
|
|
||||||
if (track instanceof BoneTrack && ((BoneTrack) track).getTargetBoneIndex() == boneIndex) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method applies spatial transform on each frame of the given
|
|
||||||
* animations.
|
|
||||||
*
|
|
||||||
* @param spatial
|
|
||||||
* the spatial
|
|
||||||
* @param spatialOma
|
|
||||||
* the OMA of the given spatial
|
|
||||||
* @param space
|
|
||||||
* the space we compute the transform in
|
|
||||||
* @param referenceAnimation
|
|
||||||
* the object containing the animations
|
|
||||||
*/
|
|
||||||
private void applyAnimData(Spatial spatial, Long spatialOma, Space space, Animation referenceAnimation) {
|
|
||||||
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
|
||||||
Transform transform = constraintHelper.getNodeObjectTransform(space, spatialOma, blenderContext);
|
|
||||||
|
|
||||||
SpatialTrack parentTrack = (SpatialTrack) referenceAnimation.getTracks()[0];
|
|
||||||
|
|
||||||
HashMap<String, Animation> anims = new HashMap<String, Animation>(1);
|
|
||||||
Animation animation = new Animation(spatial.getName(), referenceAnimation.getLength());
|
|
||||||
anims.put(spatial.getName(), animation);
|
|
||||||
|
|
||||||
float[] times = parentTrack.getTimes();
|
|
||||||
Vector3f[] translations = new Vector3f[times.length];
|
|
||||||
Quaternion[] rotations = new Quaternion[times.length];
|
|
||||||
Vector3f[] scales = new Vector3f[times.length];
|
|
||||||
Arrays.fill(translations, transform.getTranslation());
|
|
||||||
Arrays.fill(rotations, transform.getRotation());
|
|
||||||
Arrays.fill(scales, transform.getScale());
|
|
||||||
animation.addTrack(new SpatialTrack(times, translations, rotations, scales));
|
|
||||||
|
|
||||||
AnimControl control = new AnimControl(null);
|
|
||||||
control.setAnimations(anims);
|
|
||||||
spatial.addControl(control);
|
|
||||||
|
|
||||||
blenderContext.setAnimData(spatialOma, new AnimData(null, new ArrayList<Animation>(anims.values())));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,146 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import com.jme3.animation.BoneTrack;
|
||||||
|
import com.jme3.animation.SpatialTrack;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Transform;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A virtual track that stores computed frames after constraints are applied.
|
||||||
|
* Not all the frames need to be inserted. If there are lacks then the class
|
||||||
|
* will fill the gaps.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
/* package */class VirtualTrack {
|
||||||
|
/** The last frame for the track. */
|
||||||
|
public int maxFrame;
|
||||||
|
/** The max time for the track. */
|
||||||
|
public float maxTime;
|
||||||
|
/** Translations of the track. */
|
||||||
|
public ArrayList<Vector3f> translations;
|
||||||
|
/** Rotations of the track. */
|
||||||
|
public ArrayList<Quaternion> rotations;
|
||||||
|
/** Scales of the track. */
|
||||||
|
public ArrayList<Vector3f> scales;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the object storing the maximum frame and time.
|
||||||
|
*
|
||||||
|
* @param maxFrame
|
||||||
|
* the last frame for the track
|
||||||
|
* @param maxTime
|
||||||
|
* the max time for the track
|
||||||
|
*/
|
||||||
|
public VirtualTrack(int maxFrame, float maxTime) {
|
||||||
|
this.maxFrame = maxFrame;
|
||||||
|
this.maxTime = maxTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the transform for the given frame.
|
||||||
|
*
|
||||||
|
* @param frameIndex
|
||||||
|
* the frame for which the transform will be set
|
||||||
|
* @param transform
|
||||||
|
* the transformation to be set
|
||||||
|
*/
|
||||||
|
public void setTransform(int frameIndex, Transform transform) {
|
||||||
|
if (translations == null) {
|
||||||
|
translations = this.createList(Vector3f.ZERO, frameIndex);
|
||||||
|
}
|
||||||
|
this.append(translations, Vector3f.ZERO, frameIndex - translations.size());
|
||||||
|
translations.add(transform.getTranslation().clone());
|
||||||
|
|
||||||
|
if (rotations == null) {
|
||||||
|
rotations = this.createList(Quaternion.IDENTITY, frameIndex);
|
||||||
|
}
|
||||||
|
this.append(rotations, Quaternion.IDENTITY, frameIndex - rotations.size());
|
||||||
|
rotations.add(transform.getRotation().clone());
|
||||||
|
|
||||||
|
if (scales == null) {
|
||||||
|
scales = this.createList(Vector3f.UNIT_XYZ, frameIndex);
|
||||||
|
}
|
||||||
|
this.append(scales, Vector3f.UNIT_XYZ, frameIndex - scales.size());
|
||||||
|
scales.add(transform.getScale().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the track as a bone track.
|
||||||
|
*
|
||||||
|
* @param targetBoneIndex
|
||||||
|
* the bone index
|
||||||
|
* @return the bone track
|
||||||
|
*/
|
||||||
|
public BoneTrack getAsBoneTrack(int targetBoneIndex) {
|
||||||
|
if (translations == null && rotations == null && scales == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new BoneTrack(targetBoneIndex, this.createTimes(), translations.toArray(new Vector3f[maxFrame]), rotations.toArray(new Quaternion[maxFrame]), scales.toArray(new Vector3f[maxFrame]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the track as a spatial track.
|
||||||
|
*
|
||||||
|
* @return the spatial track
|
||||||
|
*/
|
||||||
|
public SpatialTrack getAsSpatialTrack() {
|
||||||
|
if (translations == null && rotations == null && scales == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new SpatialTrack(this.createTimes(), translations.toArray(new Vector3f[maxFrame]), rotations.toArray(new Quaternion[maxFrame]), scales.toArray(new Vector3f[maxFrame]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method creates times for the track based on the given maximum values.
|
||||||
|
*
|
||||||
|
* @return the times for the track
|
||||||
|
*/
|
||||||
|
private float[] createTimes() {
|
||||||
|
float[] times = new float[maxFrame];
|
||||||
|
float dT = maxTime / (float) maxFrame;
|
||||||
|
float t = 0;
|
||||||
|
for (int i = 0; i < maxFrame; ++i) {
|
||||||
|
times[i] = t;
|
||||||
|
t += dT;
|
||||||
|
}
|
||||||
|
return times;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method that creates a list of a given size filled with given
|
||||||
|
* elements.
|
||||||
|
*
|
||||||
|
* @param element
|
||||||
|
* the element to be put into the list
|
||||||
|
* @param count
|
||||||
|
* the list size
|
||||||
|
* @return the list
|
||||||
|
*/
|
||||||
|
private <T> ArrayList<T> createList(T element, int count) {
|
||||||
|
ArrayList<T> result = new ArrayList<T>(count);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
result.add(element);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the element to the given list.
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* the list where the element will be appended
|
||||||
|
* @param element
|
||||||
|
* the element to be appended
|
||||||
|
* @param count
|
||||||
|
* how many times the element will be appended
|
||||||
|
*/
|
||||||
|
private <T> void append(ArrayList<T> list, T element, int count) {
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
list.add(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,243 +1,49 @@
|
|||||||
package com.jme3.scene.plugins.blender.constraints.definitions;
|
package com.jme3.scene.plugins.blender.constraints.definitions;
|
||||||
|
|
||||||
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.FastMath;
|
|
||||||
import com.jme3.math.Quaternion;
|
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
import com.jme3.scene.plugins.blender.animations.Ipo;
|
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
import com.jme3.util.TempVars;
|
|
||||||
|
|
||||||
public abstract class ConstraintDefinition {
|
public abstract class ConstraintDefinition {
|
||||||
protected int flag;
|
protected int flag;
|
||||||
|
private Object owner;
|
||||||
|
private BlenderContext blenderContext;
|
||||||
|
private Long ownerOMA;
|
||||||
|
|
||||||
public ConstraintDefinition(Structure constraintData, BlenderContext blenderContext) {
|
public ConstraintDefinition(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
if (constraintData != null) {// Null constraint has no data
|
if (constraintData != null) {// Null constraint has no data
|
||||||
Number flag = (Number) constraintData.getFieldValue("flag");
|
Number flag = (Number) constraintData.getFieldValue("flag");
|
||||||
if (flag != null) {
|
if (flag != null) {
|
||||||
this.flag = flag.intValue();
|
this.flag = flag.intValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
this.blenderContext = blenderContext;
|
||||||
|
this.ownerOMA = ownerOMA;
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, Track ownerTrack, Track targetTrack, Ipo influenceIpo) {
|
|
||||||
TrackWrapper ownerWrapperTrack = ownerTrack != null ? new TrackWrapper(ownerTrack) : null;
|
|
||||||
TrackWrapper targetWrapperTrack = targetTrack != null ? new TrackWrapper(targetTrack) : null;
|
|
||||||
|
|
||||||
// uruchamiamy bake dla transformat zalenie od tego, ktre argumenty s nullami, a ktre - nie
|
|
||||||
this.bake(ownerTransform, targetTransform, influenceIpo.calculateValue(0));
|
|
||||||
if (ownerWrapperTrack != null) {
|
|
||||||
float[] ownerTimes = ownerWrapperTrack.getTimes();
|
|
||||||
Vector3f[] translations = ownerWrapperTrack.getTranslations();
|
|
||||||
Quaternion[] rotations = ownerWrapperTrack.getRotations();
|
|
||||||
Vector3f[] scales = ownerWrapperTrack.getScales();
|
|
||||||
|
|
||||||
float[] targetTimes = targetWrapperTrack == null ? null : targetWrapperTrack.getTimes();
|
|
||||||
Vector3f[] targetTranslations = targetWrapperTrack == null ? null : targetWrapperTrack.getTranslations();
|
|
||||||
Quaternion[] targetRotations = targetWrapperTrack == null ? null : targetWrapperTrack.getRotations();
|
|
||||||
Vector3f[] targetScales = targetWrapperTrack == null ? null : targetWrapperTrack.getScales();
|
|
||||||
Vector3f translation = new Vector3f(), scale = new Vector3f();
|
|
||||||
Quaternion rotation = new Quaternion();
|
|
||||||
|
|
||||||
Transform ownerTemp = new Transform(), targetTemp = new Transform();
|
|
||||||
for (int i = 0; i < ownerTimes.length; ++i) {
|
|
||||||
float t = ownerTimes[i];
|
|
||||||
ownerTemp.setTranslation(translations[i]);
|
|
||||||
ownerTemp.setRotation(rotations[i]);
|
|
||||||
ownerTemp.setScale(scales[i]);
|
|
||||||
if (targetWrapperTrack == null) {
|
|
||||||
this.bake(ownerTemp, targetTransform, influenceIpo.calculateValue(i));
|
|
||||||
} else {
|
|
||||||
// getting the values that are the interpolation of the target track for the time 't'
|
|
||||||
this.interpolate(targetTranslations, targetTimes, t, translation);
|
|
||||||
this.interpolate(targetRotations, targetTimes, t, rotation);
|
|
||||||
this.interpolate(targetScales, targetTimes, t, scale);
|
|
||||||
|
|
||||||
targetTemp.setTranslation(translation);
|
|
||||||
targetTemp.setRotation(rotation);
|
|
||||||
targetTemp.setScale(scale);
|
|
||||||
|
|
||||||
this.bake(ownerTemp, targetTemp, influenceIpo.calculateValue(i));
|
|
||||||
}
|
|
||||||
// need to clone here because each of the arrays will reference the same instance if they hold the same value in the compact array
|
|
||||||
translations[i] = ownerTemp.getTranslation().clone();
|
|
||||||
rotations[i] = ownerTemp.getRotation().clone();
|
|
||||||
scales[i] = ownerTemp.getScale().clone();
|
|
||||||
}
|
|
||||||
ownerWrapperTrack.setKeyframes(ownerTimes, translations, rotations, scales);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void bake(Transform ownerTransform, Transform targetTransform, float influence);
|
|
||||||
|
|
||||||
private void interpolate(Vector3f[] targetVectors, float[] targetTimes, float currentTime, Vector3f result) {
|
|
||||||
int index = 0;
|
|
||||||
for (int i = 1; i < targetTimes.length; ++i) {
|
|
||||||
if (targetTimes[i] < currentTime) {
|
|
||||||
++index;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (index >= targetTimes.length - 1) {
|
|
||||||
result.set(targetVectors[targetTimes.length - 1]);
|
|
||||||
} else {
|
|
||||||
float delta = targetTimes[index + 1] - targetTimes[index];
|
|
||||||
if (delta == 0.0f) {
|
|
||||||
result.set(targetVectors[index + 1]);
|
|
||||||
} else {
|
|
||||||
float scale = (currentTime - targetTimes[index]) / (targetTimes[index + 1] - targetTimes[index]);
|
|
||||||
FastMath.interpolateLinear(scale, targetVectors[index], targetVectors[index + 1], result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void interpolate(Quaternion[] targetQuaternions, float[] targetTimes, float currentTime, Quaternion result) {
|
|
||||||
int index = 0;
|
|
||||||
for (int i = 1; i < targetTimes.length; ++i) {
|
|
||||||
if (targetTimes[i] < currentTime) {
|
|
||||||
++index;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (index >= targetTimes.length - 1) {
|
|
||||||
result.set(targetQuaternions[targetTimes.length - 1]);
|
|
||||||
} else {
|
|
||||||
float delta = targetTimes[index + 1] - targetTimes[index];
|
|
||||||
if (delta == 0.0f) {
|
|
||||||
result.set(targetQuaternions[index + 1]);
|
|
||||||
} else {
|
|
||||||
float scale = (currentTime - targetTimes[index]) / (targetTimes[index + 1] - targetTimes[index]);
|
|
||||||
result.slerp(targetQuaternions[index], targetQuaternions[index + 1], scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class holds either the bone track or spatial track. Is made to improve
|
* This method is here because we have no guarantee that the owner is loaded
|
||||||
* code readability.
|
* when constraint is being created. So use it to get the owner when it is
|
||||||
|
* needed for computations.
|
||||||
*
|
*
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @return the owner of the constraint or null if none is set
|
||||||
*/
|
*/
|
||||||
private static class TrackWrapper implements Track {
|
public Object getOwner() {
|
||||||
/** The spatial track. */
|
if (ownerOMA != null && owner == null) {
|
||||||
private SpatialTrack spatialTrack;
|
owner = blenderContext.getLoadedFeature(ownerOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
||||||
/** The bone track. */
|
if (owner == null) {
|
||||||
private BoneTrack boneTrack;
|
throw new IllegalStateException("Cannot load constraint's owner for constraint type: " + this.getClass().getName());
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the object using the given track. The track must be of one of the types: <li>BoneTrack <li>SpatialTrack
|
|
||||||
*
|
|
||||||
* @param track
|
|
||||||
* the animation track
|
|
||||||
*/
|
|
||||||
public TrackWrapper(Track track) {
|
|
||||||
if (track instanceof SpatialTrack) {
|
|
||||||
this.spatialTrack = (SpatialTrack) track;
|
|
||||||
} else if (track instanceof BoneTrack) {
|
|
||||||
this.boneTrack = (BoneTrack) track;
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Unknown track type!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return owner;
|
||||||
/**
|
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void write(JmeExporter ex) throws IOException {
|
|
||||||
// no need to implement this one (the TrackWrapper is used internally and never serialized)
|
|
||||||
}
|
|
||||||
|
|
||||||
public void read(JmeImporter im) throws IOException {
|
|
||||||
// no need to implement this one (the TrackWrapper is used internally and never serialized)
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getLength() {
|
|
||||||
return spatialTrack == null ? boneTrack.getLength() : spatialTrack.getLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TrackWrapper clone() {
|
|
||||||
if (boneTrack != null) {
|
|
||||||
return new TrackWrapper(boneTrack.clone());
|
|
||||||
}
|
|
||||||
return new TrackWrapper(spatialTrack.clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isImplemented() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String getConstraintTypeName();
|
||||||
|
|
||||||
|
public abstract void bake(Transform ownerTransform, Transform targetTransform, float influence);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.jme3.scene.plugins.blender.constraints.definitions;
|
package com.jme3.scene.plugins.blender.constraints.definitions;
|
||||||
|
|
||||||
|
import com.jme3.animation.Bone;
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
@ -7,6 +8,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents 'Dist limit' constraint type in blender.
|
* This class represents 'Dist limit' constraint type in blender.
|
||||||
|
*
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
/* package */class ConstraintDefinitionDistLimit extends ConstraintDefinition {
|
/* package */class ConstraintDefinitionDistLimit extends ConstraintDefinition {
|
||||||
@ -17,14 +19,19 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
protected int mode;
|
protected int mode;
|
||||||
protected float dist;
|
protected float dist;
|
||||||
|
|
||||||
public ConstraintDefinitionDistLimit(Structure constraintData, BlenderContext blenderContext) {
|
public ConstraintDefinitionDistLimit(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
super(constraintData, blenderContext);
|
super(constraintData, ownerOMA, blenderContext);
|
||||||
mode = ((Number) constraintData.getFieldValue("mode")).intValue();
|
mode = ((Number) constraintData.getFieldValue("mode")).intValue();
|
||||||
dist = ((Number) constraintData.getFieldValue("dist")).floatValue();
|
dist = ((Number) constraintData.getFieldValue("dist")).floatValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
||||||
|
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
|
||||||
|
// distance limit does not work on bones who have parent
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Vector3f v = ownerTransform.getTranslation().subtract(targetTransform.getTranslation());
|
Vector3f v = ownerTransform.getTranslation().subtract(targetTransform.getTranslation());
|
||||||
float currentDistance = v.length();
|
float currentDistance = v.length();
|
||||||
|
|
||||||
@ -56,4 +63,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
|
throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConstraintTypeName() {
|
||||||
|
return "Limit distance";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
|||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
public class ConstraintDefinitionFactory {
|
public class ConstraintDefinitionFactory {
|
||||||
private static final Map<String, Class<? extends ConstraintDefinition>> CONSTRAINT_CLASSES = new HashMap<String, Class<? extends ConstraintDefinition>>();
|
private static final Map<String, Class<? extends ConstraintDefinition>> CONSTRAINT_CLASSES = new HashMap<String, Class<? extends ConstraintDefinition>>();
|
||||||
static {
|
static {
|
||||||
CONSTRAINT_CLASSES.put("bDistLimitConstraint", ConstraintDefinitionDistLimit.class);
|
CONSTRAINT_CLASSES.put("bDistLimitConstraint", ConstraintDefinitionDistLimit.class);
|
||||||
CONSTRAINT_CLASSES.put("bLocateLikeConstraint", ConstraintDefinitionLocLike.class);
|
CONSTRAINT_CLASSES.put("bLocateLikeConstraint", ConstraintDefinitionLocLike.class);
|
||||||
@ -52,7 +52,7 @@ public class ConstraintDefinitionFactory {
|
|||||||
CONSTRAINT_CLASSES.put("bSizeLimitConstraint", ConstraintDefinitionSizeLimit.class);
|
CONSTRAINT_CLASSES.put("bSizeLimitConstraint", ConstraintDefinitionSizeLimit.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Map<String, String> UNSUPPORTED_CONSTRAINTS = new HashMap<String, String>();
|
private static final Map<String, String> UNSUPPORTED_CONSTRAINTS = new HashMap<String, String>();
|
||||||
static {
|
static {
|
||||||
UNSUPPORTED_CONSTRAINTS.put("bActionConstraint", "Action");
|
UNSUPPORTED_CONSTRAINTS.put("bActionConstraint", "Action");
|
||||||
UNSUPPORTED_CONSTRAINTS.put("bChildOfConstraint", "Child of");
|
UNSUPPORTED_CONSTRAINTS.put("bChildOfConstraint", "Child of");
|
||||||
@ -84,22 +84,23 @@ public class ConstraintDefinitionFactory {
|
|||||||
* This method creates the constraint instance.
|
* This method creates the constraint instance.
|
||||||
*
|
*
|
||||||
* @param constraintStructure
|
* @param constraintStructure
|
||||||
* the constraint's structure (bConstraint clss in blender 2.49). If the value is null the NullConstraint is created.
|
* the constraint's structure (bConstraint clss in blender 2.49).
|
||||||
|
* If the value is null the NullConstraint is created.
|
||||||
* @param blenderContext
|
* @param blenderContext
|
||||||
* the blender context
|
* the blender context
|
||||||
* @throws BlenderFileException
|
* @throws BlenderFileException
|
||||||
* this exception is thrown when the blender file is somehow
|
* this exception is thrown when the blender file is somehow
|
||||||
* corrupted
|
* corrupted
|
||||||
*/
|
*/
|
||||||
public static ConstraintDefinition createConstraintDefinition(Structure constraintStructure, BlenderContext blenderContext) throws BlenderFileException {
|
public static ConstraintDefinition createConstraintDefinition(Structure constraintStructure, Long ownerOMA, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
if (constraintStructure == null) {
|
if (constraintStructure == null) {
|
||||||
return new ConstraintDefinitionNull(null, blenderContext);
|
return new ConstraintDefinitionNull(null, ownerOMA, blenderContext);
|
||||||
}
|
}
|
||||||
String constraintClassName = constraintStructure.getType();
|
String constraintClassName = constraintStructure.getType();
|
||||||
Class<? extends ConstraintDefinition> constraintDefinitionClass = CONSTRAINT_CLASSES.get(constraintClassName);
|
Class<? extends ConstraintDefinition> constraintDefinitionClass = CONSTRAINT_CLASSES.get(constraintClassName);
|
||||||
if (constraintDefinitionClass != null) {
|
if (constraintDefinitionClass != null) {
|
||||||
try {
|
try {
|
||||||
return (ConstraintDefinition) constraintDefinitionClass.getDeclaredConstructors()[0].newInstance(constraintStructure, blenderContext);
|
return (ConstraintDefinition) constraintDefinitionClass.getDeclaredConstructors()[0].newInstance(constraintStructure, ownerOMA, blenderContext);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new BlenderFileException(e.getLocalizedMessage(), e);
|
throw new BlenderFileException(e.getLocalizedMessage(), e);
|
||||||
} catch (SecurityException e) {
|
} catch (SecurityException e) {
|
||||||
@ -113,7 +114,7 @@ public class ConstraintDefinitionFactory {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String constraintName = UNSUPPORTED_CONSTRAINTS.get(constraintClassName);
|
String constraintName = UNSUPPORTED_CONSTRAINTS.get(constraintClassName);
|
||||||
if(constraintName != null) {
|
if (constraintName != null) {
|
||||||
return new UnsupportedConstraintDefinition(constraintName);
|
return new UnsupportedConstraintDefinition(constraintName);
|
||||||
} else {
|
} else {
|
||||||
throw new BlenderFileException("Unknown constraint type: " + constraintClassName);
|
throw new BlenderFileException("Unknown constraint type: " + constraintClassName);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.jme3.scene.plugins.blender.constraints.definitions;
|
package com.jme3.scene.plugins.blender.constraints.definitions;
|
||||||
|
|
||||||
|
import com.jme3.animation.Bone;
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
@ -7,27 +8,32 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents 'Loc like' constraint type in blender.
|
* This class represents 'Loc like' constraint type in blender.
|
||||||
|
*
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
/* package */class ConstraintDefinitionLocLike extends ConstraintDefinition {
|
/* package */class ConstraintDefinitionLocLike extends ConstraintDefinition {
|
||||||
private static final int LOCLIKE_X = 0x01;
|
private static final int LOCLIKE_X = 0x01;
|
||||||
private static final int LOCLIKE_Y = 0x02;
|
private static final int LOCLIKE_Y = 0x02;
|
||||||
private static final int LOCLIKE_Z = 0x04;
|
private static final int LOCLIKE_Z = 0x04;
|
||||||
// protected static final int LOCLIKE_TIP = 0x08;//this is deprecated in blender
|
// 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_X_INVERT = 0x10;
|
||||||
private static final int LOCLIKE_Y_INVERT = 0x20;
|
private static final int LOCLIKE_Y_INVERT = 0x20;
|
||||||
private static final int LOCLIKE_Z_INVERT = 0x40;
|
private static final int LOCLIKE_Z_INVERT = 0x40;
|
||||||
private static final int LOCLIKE_OFFSET = 0x80;
|
private static final int LOCLIKE_OFFSET = 0x80;
|
||||||
|
|
||||||
public ConstraintDefinitionLocLike(Structure constraintData, BlenderContext blenderContext) {
|
public ConstraintDefinitionLocLike(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
super(constraintData, blenderContext);
|
super(constraintData, ownerOMA, blenderContext);
|
||||||
if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
||||||
// swapping Y and X limits flag in the bitwise flag
|
// swapping Y and X limits flag in the bitwise flag
|
||||||
int y = flag & LOCLIKE_Y;
|
int y = flag & LOCLIKE_Y;
|
||||||
int invY = flag & LOCLIKE_Y_INVERT;
|
int invY = flag & LOCLIKE_Y_INVERT;
|
||||||
int z = flag & LOCLIKE_Z;
|
int z = flag & LOCLIKE_Z;
|
||||||
int invZ = flag & LOCLIKE_Z_INVERT;
|
int invZ = flag & LOCLIKE_Z_INVERT;
|
||||||
flag &= LOCLIKE_X | LOCLIKE_X_INVERT | LOCLIKE_OFFSET;// clear the other flags to swap them
|
flag &= LOCLIKE_X | LOCLIKE_X_INVERT | LOCLIKE_OFFSET;// clear the
|
||||||
|
// other flags
|
||||||
|
// to swap
|
||||||
|
// them
|
||||||
flag |= y << 1;
|
flag |= y << 1;
|
||||||
flag |= invY << 1;
|
flag |= invY << 1;
|
||||||
flag |= z >> 1;
|
flag |= z >> 1;
|
||||||
@ -37,12 +43,18 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
||||||
|
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
|
||||||
|
// cannot copy the location of a bone attached to its parent,
|
||||||
|
// Blender forbids that
|
||||||
|
return;
|
||||||
|
}
|
||||||
Vector3f ownerLocation = ownerTransform.getTranslation();
|
Vector3f ownerLocation = ownerTransform.getTranslation();
|
||||||
Vector3f targetLocation = targetTransform.getTranslation();
|
Vector3f targetLocation = targetTransform.getTranslation();
|
||||||
|
|
||||||
Vector3f startLocation = ownerTransform.getTranslation().clone();
|
Vector3f startLocation = ownerTransform.getTranslation().clone();
|
||||||
Vector3f offset = Vector3f.ZERO;
|
Vector3f offset = Vector3f.ZERO;
|
||||||
if ((flag & LOCLIKE_OFFSET) != 0) {// we add the original location to the copied location
|
if ((flag & LOCLIKE_OFFSET) != 0) {// we add the original location to
|
||||||
|
// the copied location
|
||||||
offset = startLocation;
|
offset = startLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,4 +83,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
ownerLocation.addLocal(startLocation);
|
ownerLocation.addLocal(startLocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConstraintTypeName() {
|
||||||
|
return "Copy location";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.jme3.scene.plugins.blender.constraints.definitions;
|
package com.jme3.scene.plugins.blender.constraints.definitions;
|
||||||
|
|
||||||
|
import com.jme3.animation.Bone;
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
@ -7,6 +8,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents 'Loc limit' constraint type in blender.
|
* This class represents 'Loc limit' constraint type in blender.
|
||||||
|
*
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
/* package */class ConstraintDefinitionLocLimit extends ConstraintDefinition {
|
/* package */class ConstraintDefinitionLocLimit extends ConstraintDefinition {
|
||||||
@ -19,8 +21,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
|
|
||||||
protected float[][] limits = new float[3][2];
|
protected float[][] limits = new float[3][2];
|
||||||
|
|
||||||
public ConstraintDefinitionLocLimit(Structure constraintData, BlenderContext blenderContext) {
|
public ConstraintDefinitionLocLimit(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
super(constraintData, blenderContext);
|
super(constraintData, ownerOMA, blenderContext);
|
||||||
if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
||||||
limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
|
limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
|
||||||
limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
|
limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
|
||||||
@ -34,7 +36,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
int ymax = flag & LIMIT_YMAX;
|
int ymax = flag & LIMIT_YMAX;
|
||||||
int zmin = flag & LIMIT_ZMIN;
|
int zmin = flag & LIMIT_ZMIN;
|
||||||
int zmax = flag & LIMIT_ZMAX;
|
int zmax = flag & LIMIT_ZMAX;
|
||||||
flag &= LIMIT_XMIN | LIMIT_XMAX;// clear the other flags to swap them
|
flag &= LIMIT_XMIN | LIMIT_XMAX;// clear the other flags to swap
|
||||||
|
// them
|
||||||
flag |= ymin << 2;
|
flag |= ymin << 2;
|
||||||
flag |= ymax << 2;
|
flag |= ymax << 2;
|
||||||
flag |= zmin >> 2;
|
flag |= zmin >> 2;
|
||||||
@ -51,6 +54,11 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
||||||
|
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
|
||||||
|
// location limit does not work on bones who have parent
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Vector3f translation = ownerTransform.getTranslation();
|
Vector3f translation = ownerTransform.getTranslation();
|
||||||
|
|
||||||
if ((flag & LIMIT_XMIN) != 0 && translation.x < limits[0][0]) {
|
if ((flag & LIMIT_XMIN) != 0 && translation.x < limits[0][0]) {
|
||||||
@ -72,4 +80,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
translation.z -= (translation.z - limits[2][1]) * influence;
|
translation.z -= (translation.z - limits[2][1]) * influence;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConstraintTypeName() {
|
||||||
|
return "Limit location";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,22 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents 'Null' constraint type in blender.
|
* This class represents 'Null' constraint type in blender.
|
||||||
|
*
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
/* package */class ConstraintDefinitionNull extends ConstraintDefinition {
|
/* package */class ConstraintDefinitionNull extends ConstraintDefinition {
|
||||||
|
|
||||||
public ConstraintDefinitionNull(Structure constraintData, BlenderContext blenderContext) {
|
public ConstraintDefinitionNull(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
super(constraintData, blenderContext);
|
super(constraintData, ownerOMA, blenderContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
||||||
// null constraint does nothing so no need to implement this one
|
// null constraint does nothing so no need to implement this one
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConstraintTypeName() {
|
||||||
|
return "Null";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents 'Rot like' constraint type in blender.
|
* This class represents 'Rot like' constraint type in blender.
|
||||||
|
*
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
/* package */class ConstraintDefinitionRotLike extends ConstraintDefinition {
|
/* package */class ConstraintDefinitionRotLike extends ConstraintDefinition {
|
||||||
@ -21,8 +22,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
private transient float[] ownerAngles = new float[3];
|
private transient float[] ownerAngles = new float[3];
|
||||||
private transient float[] targetAngles = new float[3];
|
private transient float[] targetAngles = new float[3];
|
||||||
|
|
||||||
public ConstraintDefinitionRotLike(Structure constraintData, BlenderContext blenderContext) {
|
public ConstraintDefinitionRotLike(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
super(constraintData, blenderContext);
|
super(constraintData, ownerOMA, blenderContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -33,7 +34,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
|
|
||||||
Quaternion startRotation = ownerRotation.clone();
|
Quaternion startRotation = ownerRotation.clone();
|
||||||
Quaternion offset = Quaternion.IDENTITY;
|
Quaternion offset = Quaternion.IDENTITY;
|
||||||
if ((flag & ROTLIKE_OFFSET) != 0) {// we add the original rotation to the copied rotation
|
if ((flag & ROTLIKE_OFFSET) != 0) {// we add the original rotation to
|
||||||
|
// the copied rotation
|
||||||
offset = startRotation;
|
offset = startRotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,10 +60,14 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
ownerRotation.fromAngles(ownerAngles).multLocal(offset);
|
ownerRotation.fromAngles(ownerAngles).multLocal(offset);
|
||||||
|
|
||||||
if (influence < 1.0f) {
|
if (influence < 1.0f) {
|
||||||
|
|
||||||
// startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
|
// startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
|
||||||
// ownerLocation.addLocal(startLocation);
|
// ownerLocation.addLocal(startLocation);
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConstraintTypeName() {
|
||||||
|
return "Copy rotation";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,13 +18,13 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
private transient float[][] limits = new float[3][2];
|
private transient float[][] limits = new float[3][2];
|
||||||
private transient float[] angles = new float[3];
|
private transient float[] angles = new float[3];
|
||||||
|
|
||||||
public ConstraintDefinitionRotLimit(Structure constraintData, BlenderContext blenderContext) {
|
public ConstraintDefinitionRotLimit(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
super(constraintData, blenderContext);
|
super(constraintData, ownerOMA, blenderContext);
|
||||||
if (blenderContext.getBlenderKey().isFixUpAxis()/* && owner.spatial != null */) {// FIXME: !!!!!!!!
|
if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
||||||
limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
|
limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
|
||||||
limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
|
limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
|
||||||
limits[2][0] = -((Number) constraintData.getFieldValue("ymin")).floatValue();
|
limits[2][0] = ((Number) constraintData.getFieldValue("ymin")).floatValue();
|
||||||
limits[2][1] = -((Number) constraintData.getFieldValue("ymax")).floatValue();
|
limits[2][1] = ((Number) constraintData.getFieldValue("ymax")).floatValue();
|
||||||
limits[1][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
|
limits[1][0] = ((Number) constraintData.getFieldValue("zmin")).floatValue();
|
||||||
limits[1][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
|
limits[1][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
|
||||||
|
|
||||||
@ -45,15 +45,38 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
|
|
||||||
// until blender 2.49 the rotations values were stored in degrees
|
// until blender 2.49 the rotations values were stored in degrees
|
||||||
if (blenderContext.getBlenderVersion() <= 249) {
|
if (blenderContext.getBlenderVersion() <= 249) {
|
||||||
for (int i = 0; i < limits.length; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
limits[i][0] *= FastMath.DEG_TO_RAD;
|
limits[i][0] *= FastMath.DEG_TO_RAD;
|
||||||
limits[i][1] *= FastMath.DEG_TO_RAD;
|
limits[i][1] *= FastMath.DEG_TO_RAD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure that the limits are always in range [0, 2PI)
|
||||||
|
// TODO: left it here because it is essential to make sure all cases
|
||||||
|
// work poperly
|
||||||
|
// but will do it a little bit later ;)
|
||||||
|
/*
|
||||||
|
* for (int i = 0; i < 3; ++i) { for (int j = 0; j < 2; ++j) { int
|
||||||
|
* multFactor = (int)Math.abs(limits[i][j] / FastMath.TWO_PI) ; if
|
||||||
|
* (limits[i][j] < 0) { limits[i][j] += FastMath.TWO_PI * (multFactor +
|
||||||
|
* 1); } else { limits[i][j] -= FastMath.TWO_PI * multFactor; } } //make
|
||||||
|
* sure the lower limit is not greater than the upper one
|
||||||
|
* if(limits[i][0] > limits[i][1]) { float temp = limits[i][0];
|
||||||
|
* limits[i][0] = limits[i][1]; limits[i][1] = temp; } }
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
||||||
|
ownerTransform.getRotation().toAngles(angles);
|
||||||
|
// make sure that the rotations are always in range [0, 2PI)
|
||||||
|
// TODO: same comment as in constructor
|
||||||
|
/*
|
||||||
|
* for (int i = 0; i < 3; ++i) { int multFactor =
|
||||||
|
* (int)Math.abs(angles[i] / FastMath.TWO_PI) ; if(angles[i] < 0) {
|
||||||
|
* angles[i] += FastMath.TWO_PI * (multFactor + 1); } else { angles[i]
|
||||||
|
* -= FastMath.TWO_PI * multFactor; } }
|
||||||
|
*/
|
||||||
if ((flag & LIMIT_XROT) != 0) {
|
if ((flag & LIMIT_XROT) != 0) {
|
||||||
float difference = 0.0f;
|
float difference = 0.0f;
|
||||||
if (angles[0] < limits[0][0]) {
|
if (angles[0] < limits[0][0]) {
|
||||||
@ -81,5 +104,11 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
}
|
}
|
||||||
angles[2] -= difference;
|
angles[2] -= difference;
|
||||||
}
|
}
|
||||||
|
ownerTransform.getRotation().fromAngles(angles);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConstraintTypeName() {
|
||||||
|
return "Limit rotation";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents 'Size like' constraint type in blender.
|
* This class represents 'Size like' constraint type in blender.
|
||||||
|
*
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
/* package */class ConstraintDefinitionSizeLike extends ConstraintDefinition {
|
/* package */class ConstraintDefinitionSizeLike extends ConstraintDefinition {
|
||||||
@ -15,13 +16,14 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
private static final int SIZELIKE_Z = 0x04;
|
private static final int SIZELIKE_Z = 0x04;
|
||||||
private static final int LOCLIKE_OFFSET = 0x80;
|
private static final int LOCLIKE_OFFSET = 0x80;
|
||||||
|
|
||||||
public ConstraintDefinitionSizeLike(Structure constraintData, BlenderContext blenderContext) {
|
public ConstraintDefinitionSizeLike(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
super(constraintData, blenderContext);
|
super(constraintData, ownerOMA, blenderContext);
|
||||||
if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
||||||
// swapping Y and X limits flag in the bitwise flag
|
// swapping Y and X limits flag in the bitwise flag
|
||||||
int y = flag & SIZELIKE_Y;
|
int y = flag & SIZELIKE_Y;
|
||||||
int z = flag & SIZELIKE_Z;
|
int z = flag & SIZELIKE_Z;
|
||||||
flag &= SIZELIKE_X | LOCLIKE_OFFSET;// clear the other flags to swap them
|
flag &= SIZELIKE_X | LOCLIKE_OFFSET;// clear the other flags to swap
|
||||||
|
// them
|
||||||
flag |= y << 1;
|
flag |= y << 1;
|
||||||
flag |= z >> 1;
|
flag |= z >> 1;
|
||||||
}
|
}
|
||||||
@ -33,7 +35,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
Vector3f targetScale = targetTransform.getScale();
|
Vector3f targetScale = targetTransform.getScale();
|
||||||
|
|
||||||
Vector3f offset = Vector3f.ZERO;
|
Vector3f offset = Vector3f.ZERO;
|
||||||
if ((flag & LOCLIKE_OFFSET) != 0) {// we add the original scale to the copied scale
|
if ((flag & LOCLIKE_OFFSET) != 0) {// we add the original scale to the
|
||||||
|
// copied scale
|
||||||
offset = ownerScale.clone();
|
offset = ownerScale.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,4 +51,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
}
|
}
|
||||||
ownerScale.addLocal(offset);
|
ownerScale.addLocal(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConstraintTypeName() {
|
||||||
|
return "Copy scale";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents 'Size limit' constraint type in blender.
|
* This class represents 'Size limit' constraint type in blender.
|
||||||
|
*
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
/* package */class ConstraintDefinitionSizeLimit extends ConstraintDefinition {
|
/* package */class ConstraintDefinitionSizeLimit extends ConstraintDefinition {
|
||||||
@ -19,8 +20,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
|
|
||||||
protected transient float[][] limits = new float[3][2];
|
protected transient float[][] limits = new float[3][2];
|
||||||
|
|
||||||
public ConstraintDefinitionSizeLimit(Structure constraintData, BlenderContext blenderContext) {
|
public ConstraintDefinitionSizeLimit(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
super(constraintData, blenderContext);
|
super(constraintData, ownerOMA, blenderContext);
|
||||||
if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
||||||
limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
|
limits[0][0] = ((Number) constraintData.getFieldValue("xmin")).floatValue();
|
||||||
limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
|
limits[0][1] = ((Number) constraintData.getFieldValue("xmax")).floatValue();
|
||||||
@ -34,7 +35,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
int ymax = flag & LIMIT_YMAX;
|
int ymax = flag & LIMIT_YMAX;
|
||||||
int zmin = flag & LIMIT_ZMIN;
|
int zmin = flag & LIMIT_ZMIN;
|
||||||
int zmax = flag & LIMIT_ZMAX;
|
int zmax = flag & LIMIT_ZMAX;
|
||||||
flag &= LIMIT_XMIN | LIMIT_XMAX;// clear the other flags to swap them
|
flag &= LIMIT_XMIN | LIMIT_XMAX;// clear the other flags to swap
|
||||||
|
// them
|
||||||
flag |= ymin << 2;
|
flag |= ymin << 2;
|
||||||
flag |= ymax << 2;
|
flag |= ymax << 2;
|
||||||
flag |= zmin >> 2;
|
flag |= zmin >> 2;
|
||||||
@ -72,4 +74,9 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
scale.z -= (scale.z - limits[2][1]) * influence;
|
scale.z -= (scale.z - limits[2][1]) * influence;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConstraintTypeName() {
|
||||||
|
return "Limit scale";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,33 @@
|
|||||||
package com.jme3.scene.plugins.blender.constraints.definitions;
|
package com.jme3.scene.plugins.blender.constraints.definitions;
|
||||||
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a constraint that is defined by blender but not supported by either importer
|
* This class represents a constraint that is defined by blender but not
|
||||||
* ot jme. It only wirtes down a warning when baking is called.
|
* supported by either importer ot jme. It only wirtes down a warning when
|
||||||
|
* baking is called.
|
||||||
*
|
*
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
/* package */ class UnsupportedConstraintDefinition extends ConstraintDefinition {
|
/* package */class UnsupportedConstraintDefinition extends ConstraintDefinition {
|
||||||
private static final Logger LOGGER = Logger.getLogger(UnsupportedConstraintDefinition.class.getName());
|
private String typeName;
|
||||||
|
|
||||||
private String name;
|
public UnsupportedConstraintDefinition(String typeName) {
|
||||||
|
super(null, null, null);
|
||||||
public UnsupportedConstraintDefinition(String name) {
|
this.typeName = typeName;
|
||||||
super(null, null);
|
|
||||||
this.name = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
||||||
LOGGER.log(Level.WARNING, "'{0}' constraint NOT implemented!", name);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isImplemented() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConstraintTypeName() {
|
||||||
|
return typeName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,8 @@ import com.jme3.util.BufferUtils;
|
|||||||
*/
|
*/
|
||||||
/* package */class ArmatureModifier extends Modifier {
|
/* package */class ArmatureModifier extends Modifier {
|
||||||
private static final Logger LOGGER = Logger.getLogger(ArmatureModifier.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(ArmatureModifier.class.getName());
|
||||||
private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4; // JME limitation
|
private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4; // JME
|
||||||
|
// limitation
|
||||||
|
|
||||||
private Skeleton skeleton;
|
private Skeleton skeleton;
|
||||||
private Structure objectStructure;
|
private Structure objectStructure;
|
||||||
@ -71,7 +72,9 @@ import com.jme3.util.BufferUtils;
|
|||||||
*/
|
*/
|
||||||
public ArmatureModifier(Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
|
public ArmatureModifier(Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
|
Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
|
||||||
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
|
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert
|
||||||
|
// =
|
||||||
|
// DeformVERTices
|
||||||
|
|
||||||
// if pDvert==null then there are not vertex groups and no need to load
|
// if pDvert==null then there are not vertex groups and no need to load
|
||||||
// skeleton (untill bone envelopes are supported)
|
// skeleton (untill bone envelopes are supported)
|
||||||
@ -109,7 +112,8 @@ import com.jme3.util.BufferUtils;
|
|||||||
// read animations
|
// read animations
|
||||||
ArrayList<Animation> animations = new ArrayList<Animation>();
|
ArrayList<Animation> animations = new ArrayList<Animation>();
|
||||||
List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
|
List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
|
||||||
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) {
|
for (FileBlockHeader header : actionHeaders) {
|
||||||
Structure actionStructure = header.getStructure(blenderContext);
|
Structure actionStructure = header.getStructure(blenderContext);
|
||||||
String actionName = actionStructure.getName();
|
String actionName = actionStructure.getName();
|
||||||
@ -202,7 +206,8 @@ import com.jme3.util.BufferUtils;
|
|||||||
if (bindPoseBuffer != null) {
|
if (bindPoseBuffer != null) {
|
||||||
mesh.setBuffer(bindPoseBuffer);
|
mesh.setBuffer(bindPoseBuffer);
|
||||||
}
|
}
|
||||||
// change the usage type of vertex and normal buffers from Static to Stream
|
// change the usage type of vertex and normal buffers from
|
||||||
|
// Static to Stream
|
||||||
mesh.getBuffer(Type.Position).setUsage(Usage.Stream);
|
mesh.getBuffer(Type.Position).setUsage(Usage.Stream);
|
||||||
mesh.getBuffer(Type.Normal).setUsage(Usage.Stream);
|
mesh.getBuffer(Type.Normal).setUsage(Usage.Stream);
|
||||||
}
|
}
|
||||||
@ -227,6 +232,8 @@ import com.jme3.util.BufferUtils;
|
|||||||
node.addControl(control);
|
node.addControl(control);
|
||||||
node.addControl(new SkeletonControl(animData.skeleton));
|
node.addControl(new SkeletonControl(animData.skeleton));
|
||||||
|
|
||||||
|
blenderContext.setNodeForSkeleton(skeleton, node);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,23 +289,50 @@ import com.jme3.util.BufferUtils;
|
|||||||
* this exception is thrown when the blend file structure is
|
* this exception is thrown when the blend file structure is
|
||||||
* somehow invalid or corrupted
|
* somehow invalid or corrupted
|
||||||
*/
|
*/
|
||||||
private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups, Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, BlenderContext blenderContext) throws BlenderFileException {
|
private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups, Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, BlenderContext blenderContext)
|
||||||
|
throws BlenderFileException {
|
||||||
bonesGroups[0] = 0;
|
bonesGroups[0] = 0;
|
||||||
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
|
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert
|
||||||
|
// =
|
||||||
|
// DeformVERTices
|
||||||
FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
|
FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
|
||||||
ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
|
ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
|
||||||
|
|
||||||
if (pDvert.isNotNull()) {// assigning weights and bone indices
|
if (pDvert.isNotNull()) {// assigning weights and bone indices
|
||||||
boolean warnAboutTooManyVertexWeights = false;
|
boolean warnAboutTooManyVertexWeights = false;
|
||||||
List<Structure> dverts = pDvert.fetchData(blenderContext.getInputStream());// dverts.size() == verticesAmount (one dvert per vertex in blender)
|
List<Structure> dverts = pDvert.fetchData(blenderContext.getInputStream());// dverts.size()
|
||||||
|
// ==
|
||||||
|
// verticesAmount
|
||||||
|
// (one
|
||||||
|
// dvert
|
||||||
|
// per
|
||||||
|
// vertex
|
||||||
|
// in
|
||||||
|
// blender)
|
||||||
int vertexIndex = 0;
|
int vertexIndex = 0;
|
||||||
// use tree map to sort weights from the lowest to the highest ones
|
// use tree map to sort weights from the lowest to the highest ones
|
||||||
TreeMap<Float, Integer> weightToIndexMap = new TreeMap<Float, Integer>();
|
TreeMap<Float, Integer> weightToIndexMap = new TreeMap<Float, Integer>();
|
||||||
|
|
||||||
for (Structure dvert : dverts) {
|
for (Structure dvert : dverts) {
|
||||||
List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here
|
List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we
|
||||||
|
// fetch
|
||||||
|
// the
|
||||||
|
// referenced
|
||||||
|
// vertices
|
||||||
|
// here
|
||||||
if (vertexIndices != null) {
|
if (vertexIndices != null) {
|
||||||
int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex (max. 4 in JME)
|
int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total
|
||||||
|
// amount
|
||||||
|
// of
|
||||||
|
// weights
|
||||||
|
// assignet
|
||||||
|
// to
|
||||||
|
// the
|
||||||
|
// vertex
|
||||||
|
// (max.
|
||||||
|
// 4
|
||||||
|
// in
|
||||||
|
// JME)
|
||||||
Pointer pDW = (Pointer) dvert.getFieldValue("dw");
|
Pointer pDW = (Pointer) dvert.getFieldValue("dw");
|
||||||
if (totweight > 0 && groupToBoneIndexMap != null) {
|
if (totweight > 0 && groupToBoneIndexMap != null) {
|
||||||
weightToIndexMap.clear();
|
weightToIndexMap.clear();
|
||||||
@ -307,25 +341,32 @@ import com.jme3.util.BufferUtils;
|
|||||||
for (Structure deformWeight : dw) {
|
for (Structure deformWeight : dw) {
|
||||||
Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
|
Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
|
||||||
float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
|
float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
|
||||||
// boneIndex == null: it here means that we came accross group that has no bone attached to, so simply ignore it
|
// boneIndex == null: it here means that we came
|
||||||
// if weight == 0 and weightIndex == 0 then ignore the weight (do not set weight = 0 as a first weight)
|
// accross group that has no bone attached to, so
|
||||||
|
// simply ignore it
|
||||||
|
// if weight == 0 and weightIndex == 0 then ignore
|
||||||
|
// the weight (do not set weight = 0 as a first
|
||||||
|
// weight)
|
||||||
if (boneIndex != null && (weight > 0.0f || weightIndex > 0)) {
|
if (boneIndex != null && (weight > 0.0f || weightIndex > 0)) {
|
||||||
if (weightIndex < MAXIMUM_WEIGHTS_PER_VERTEX) {
|
if (weightIndex < MAXIMUM_WEIGHTS_PER_VERTEX) {
|
||||||
if (weight == 0.0f) {
|
if (weight == 0.0f) {
|
||||||
boneIndex = Integer.valueOf(0);
|
boneIndex = Integer.valueOf(0);
|
||||||
}
|
}
|
||||||
// we apply the weight to all referenced vertices
|
// we apply the weight to all referenced
|
||||||
|
// vertices
|
||||||
for (Integer index : vertexIndices) {
|
for (Integer index : vertexIndices) {
|
||||||
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
|
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
|
||||||
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
|
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
|
||||||
}
|
}
|
||||||
weightToIndexMap.put(weight, weightIndex);
|
weightToIndexMap.put(weight, weightIndex);
|
||||||
bonesGroups[0] = Math.max(bonesGroups[0], weightIndex + 1);
|
bonesGroups[0] = Math.max(bonesGroups[0], weightIndex + 1);
|
||||||
} else if (weight > 0) {// if weight is zero the simply ignore it
|
} else if (weight > 0) {// if weight is zero the
|
||||||
|
// simply ignore it
|
||||||
warnAboutTooManyVertexWeights = true;
|
warnAboutTooManyVertexWeights = true;
|
||||||
Entry<Float, Integer> lowestWeightAndIndex = weightToIndexMap.firstEntry();
|
Entry<Float, Integer> lowestWeightAndIndex = weightToIndexMap.firstEntry();
|
||||||
if (lowestWeightAndIndex != null && lowestWeightAndIndex.getKey() < weight) {
|
if (lowestWeightAndIndex != null && lowestWeightAndIndex.getKey() < weight) {
|
||||||
// we apply the weight to all referenced vertices
|
// we apply the weight to all referenced
|
||||||
|
// vertices
|
||||||
for (Integer index : vertexIndices) {
|
for (Integer index : vertexIndices) {
|
||||||
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + lowestWeightAndIndex.getValue(), weight);
|
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + lowestWeightAndIndex.getValue(), weight);
|
||||||
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + lowestWeightAndIndex.getValue(), boneIndex.byteValue());
|
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + lowestWeightAndIndex.getValue(), boneIndex.byteValue());
|
||||||
@ -338,7 +379,8 @@ import com.jme3.util.BufferUtils;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 0.0 weight indicates, do not transform this vertex, but keep it in bind pose.
|
// 0.0 weight indicates, do not transform this vertex,
|
||||||
|
// but keep it in bind pose.
|
||||||
for (Integer index : vertexIndices) {
|
for (Integer index : vertexIndices) {
|
||||||
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 0.0f);
|
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 0.0f);
|
||||||
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
|
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
|
||||||
@ -354,7 +396,8 @@ import com.jme3.util.BufferUtils;
|
|||||||
} else {
|
} else {
|
||||||
// always bind all vertices to 0-indexed bone
|
// always bind all vertices to 0-indexed bone
|
||||||
// this bone makes the model look normally if vertices have no 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
|
// assigned and it is used in object animation, so if we come
|
||||||
|
// accross object
|
||||||
// animation we can use the 0-indexed bone for this
|
// animation we can use the 0-indexed bone for this
|
||||||
for (List<Integer> vertexIndexList : vertexReferenceMap.values()) {
|
for (List<Integer> vertexIndexList : vertexReferenceMap.values()) {
|
||||||
// we apply the weight to all referenced vertices
|
// we apply the weight to all referenced vertices
|
||||||
|
@ -37,6 +37,7 @@ import java.util.logging.Level;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Matrix4f;
|
import com.jme3.math.Matrix4f;
|
||||||
import com.jme3.math.Quaternion;
|
import com.jme3.math.Quaternion;
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
@ -65,6 +66,7 @@ import com.jme3.scene.plugins.blender.modifiers.ModifierHelper;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that is used in object calculations.
|
* A class that is used in object calculations.
|
||||||
|
*
|
||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
public class ObjectHelper extends AbstractBlenderHelper {
|
public class ObjectHelper extends AbstractBlenderHelper {
|
||||||
@ -83,8 +85,9 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
protected static final int OBJECT_TYPE_ARMATURE = 25;
|
protected static final int OBJECT_TYPE_ARMATURE = 25;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor parses the given blender version and stores the result. Some functionalities may differ in
|
* This constructor parses the given blender version and stores the result.
|
||||||
* different blender versions.
|
* Some functionalities may differ in different blender versions.
|
||||||
|
*
|
||||||
* @param blenderVersion
|
* @param blenderVersion
|
||||||
* the version read from the blend file
|
* the version read from the blend file
|
||||||
* @param fixUpAxis
|
* @param fixUpAxis
|
||||||
@ -95,7 +98,9 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method reads the given structure and createn an object that represents the data.
|
* This method reads the given structure and createn an object that
|
||||||
|
* represents the data.
|
||||||
|
*
|
||||||
* @param objectStructure
|
* @param objectStructure
|
||||||
* the object's structure
|
* the object's structure
|
||||||
* @param blenderContext
|
* @param blenderContext
|
||||||
@ -205,7 +210,8 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OBJECT_TYPE_ARMATURE:
|
case OBJECT_TYPE_ARMATURE:
|
||||||
// need to create an empty node to properly create parent-children relationships between nodes
|
// need to create an empty node to properly create
|
||||||
|
// parent-children relationships between nodes
|
||||||
Node armature = new Node(name);
|
Node armature = new Node(name);
|
||||||
armature.setLocalTransform(t);
|
armature.setLocalTransform(t);
|
||||||
armature.setUserData(ArmatureHelper.ARMETURE_NODE_MARKER, Boolean.TRUE);
|
armature.setUserData(ArmatureHelper.ARMETURE_NODE_MARKER, Boolean.TRUE);
|
||||||
@ -223,9 +229,13 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
result.updateModelBound();// I prefer do compute bounding box here than read it from the file
|
result.updateModelBound();// I prefer do compute bounding box here
|
||||||
|
// than read it from the file
|
||||||
|
|
||||||
blenderContext.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result);
|
blenderContext.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result);
|
||||||
|
// TODO: this data is only to help during loading, shall I remove it
|
||||||
|
// after all the loading is done ???
|
||||||
|
result.setUserData("oma", objectStructure.getOldMemoryAddress());
|
||||||
|
|
||||||
// applying modifiers
|
// applying modifiers
|
||||||
LOGGER.log(Level.FINE, "Reading and applying object's modifiers.");
|
LOGGER.log(Level.FINE, "Reading and applying object's modifiers.");
|
||||||
@ -242,7 +252,8 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
// reading custom properties
|
// reading custom properties
|
||||||
if (blenderContext.getBlenderKey().isLoadObjectProperties()) {
|
if (blenderContext.getBlenderKey().isLoadObjectProperties()) {
|
||||||
Properties properties = this.loadProperties(objectStructure, blenderContext);
|
Properties properties = this.loadProperties(objectStructure, blenderContext);
|
||||||
// the loaded property is a group property, so we need to get each value and set it to Spatial
|
// the loaded property is a group property, so we need to get
|
||||||
|
// each value and set it to Spatial
|
||||||
if (result instanceof Spatial && properties != null && properties.getValue() != null) {
|
if (result instanceof Spatial && properties != null && properties.getValue() != null) {
|
||||||
this.applyProperties(result, properties);
|
this.applyProperties(result, properties);
|
||||||
}
|
}
|
||||||
@ -252,7 +263,9 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method calculates local transformation for the object. Parentage is taken under consideration.
|
* This method calculates local transformation for the object. Parentage is
|
||||||
|
* taken under consideration.
|
||||||
|
*
|
||||||
* @param objectStructure
|
* @param objectStructure
|
||||||
* the object's structure
|
* the object's structure
|
||||||
* @return objects transformation relative to its parent
|
* @return objects transformation relative to its parent
|
||||||
@ -277,16 +290,16 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
|
|
||||||
Vector3f translation = localMatrix.toTranslationVector();
|
Vector3f translation = localMatrix.toTranslationVector();
|
||||||
Quaternion rotation = localMatrix.toRotationQuat();
|
Quaternion rotation = localMatrix.toRotationQuat();
|
||||||
Vector3f scale = this.getScale(parentInv).multLocal(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
|
Vector3f scale = parentInv.toScaleVector().multLocal(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue());
|
||||||
|
|
||||||
if (fixUpAxis) {
|
if (fixUpAxis) {
|
||||||
float y = translation.y;
|
float y = translation.y;
|
||||||
translation.y = translation.z;
|
translation.y = translation.z;
|
||||||
translation.z = -y;
|
translation.z = y == 0 ? 0 : -y;
|
||||||
|
|
||||||
y = rotation.getY();
|
y = rotation.getY();
|
||||||
float z = rotation.getZ();
|
float z = rotation.getZ();
|
||||||
rotation.set(rotation.getX(), z, -y, rotation.getW());
|
rotation.set(rotation.getX(), z, y == 0 ? 0 : -y, rotation.getW());
|
||||||
|
|
||||||
y = scale.y;
|
y = scale.y;
|
||||||
scale.y = scale.z;
|
scale.y = scale.z;
|
||||||
@ -301,7 +314,9 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns the matrix of a given name for the given structure.
|
* 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.
|
* The matrix is NOT transformed if Y axis is up - the raw data is loaded
|
||||||
|
* from the blender file.
|
||||||
|
*
|
||||||
* @param structure
|
* @param structure
|
||||||
* the structure with matrix data
|
* the structure with matrix data
|
||||||
* @param matrixName
|
* @param matrixName
|
||||||
@ -315,6 +330,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
/**
|
/**
|
||||||
* This method returns the matrix of a given name for the given structure.
|
* This method returns the matrix of a given name for the given structure.
|
||||||
* It takes up axis into consideration.
|
* It takes up axis into consideration.
|
||||||
|
*
|
||||||
* @param structure
|
* @param structure
|
||||||
* the structure with matrix data
|
* the structure with matrix data
|
||||||
* @param matrixName
|
* @param matrixName
|
||||||
@ -325,7 +341,11 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
public Matrix4f getMatrix(Structure structure, String matrixName, boolean applyFixUpAxis) {
|
public Matrix4f getMatrix(Structure structure, String matrixName, boolean applyFixUpAxis) {
|
||||||
Matrix4f result = new Matrix4f();
|
Matrix4f result = new Matrix4f();
|
||||||
DynamicArray<Number> obmat = (DynamicArray<Number>) structure.getFieldValue(matrixName);
|
DynamicArray<Number> obmat = (DynamicArray<Number>) structure.getFieldValue(matrixName);
|
||||||
int rowAndColumnSize = Math.abs((int) Math.sqrt(obmat.getTotalSize()));// the matrix must be square
|
int rowAndColumnSize = Math.abs((int) Math.sqrt(obmat.getTotalSize()));// the
|
||||||
|
// matrix
|
||||||
|
// must
|
||||||
|
// be
|
||||||
|
// square
|
||||||
for (int i = 0; i < rowAndColumnSize; ++i) {
|
for (int i = 0; i < rowAndColumnSize; ++i) {
|
||||||
for (int j = 0; j < rowAndColumnSize; ++j) {
|
for (int j = 0; j < rowAndColumnSize; ++j) {
|
||||||
result.set(i, j, obmat.get(j, i).floatValue());
|
result.set(i, j, obmat.get(j, i).floatValue());
|
||||||
@ -334,15 +354,15 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
if (applyFixUpAxis && fixUpAxis) {
|
if (applyFixUpAxis && fixUpAxis) {
|
||||||
Vector3f translation = result.toTranslationVector();
|
Vector3f translation = result.toTranslationVector();
|
||||||
Quaternion rotation = result.toRotationQuat();
|
Quaternion rotation = result.toRotationQuat();
|
||||||
Vector3f scale = this.getScale(result);
|
Vector3f scale = result.toScaleVector();
|
||||||
|
|
||||||
float y = translation.y;
|
float y = translation.y;
|
||||||
translation.y = translation.z;
|
translation.y = translation.z;
|
||||||
translation.z = -y;
|
translation.z = y == 0 ? 0 : -y;
|
||||||
|
|
||||||
y = rotation.getY();
|
y = rotation.getY();
|
||||||
float z = rotation.getZ();
|
float z = rotation.getZ();
|
||||||
rotation.set(rotation.getX(), z, -y, rotation.getW());
|
rotation.set(rotation.getX(), z, y == 0 ? 0 : -y, rotation.getW());
|
||||||
|
|
||||||
y = scale.y;
|
y = scale.y;
|
||||||
scale.y = scale.z;
|
scale.y = scale.z;
|
||||||
@ -353,21 +373,17 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
|||||||
result.setRotationQuaternion(rotation);
|
result.setRotationQuaternion(rotation);
|
||||||
result.setScale(scale);
|
result.setScale(scale);
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
for (int i = 0; i < 4; ++i) {
|
||||||
* This method returns the scale from the given matrix.
|
for (int j = 0; j < 4; ++j) {
|
||||||
*
|
float value = result.get(i, j);
|
||||||
* @param matrix
|
if (Math.abs(value) <= FastMath.FLT_EPSILON) {
|
||||||
* the transformation matrix
|
result.set(i, j, 0);
|
||||||
* @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);
|
return result;
|
||||||
float scaleZ = (float) Math.sqrt(matrix.m02 * matrix.m02 + matrix.m12 * matrix.m12 + matrix.m22 * matrix.m22);
|
|
||||||
return new Vector3f(scaleX, scaleY, scaleZ);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user