Bugfixes:
- fixed issues with bones transformation applying and reading in world space - fixed issue with lack of animation track for some bones whose constraints affected the animation Refactoring: - improved (and simplified) bone loading Features: - added basic implementation of the IK constraint (still a lot to be done but works for simplest cases) git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10845 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
0ab43e0649
commit
0921540124
@ -34,6 +34,8 @@ public class BoneContext {
|
|||||||
private BlenderContext blenderContext;
|
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 OMA of the model that owns the bone's skeleton. */
|
||||||
|
private Long skeletonOwnerOma;
|
||||||
/** The structure of the bone. */
|
/** The structure of the bone. */
|
||||||
private Structure boneStructure;
|
private Structure boneStructure;
|
||||||
/** Bone's name. */
|
/** Bone's name. */
|
||||||
@ -128,6 +130,7 @@ public class BoneContext {
|
|||||||
* @return newly created bone
|
* @return newly created bone
|
||||||
*/
|
*/
|
||||||
public Bone buildBone(List<Bone> bones, Long skeletonOwnerOma, BlenderContext blenderContext) {
|
public Bone buildBone(List<Bone> bones, Long skeletonOwnerOma, BlenderContext blenderContext) {
|
||||||
|
this.skeletonOwnerOma = skeletonOwnerOma;
|
||||||
Long boneOMA = boneStructure.getOldMemoryAddress();
|
Long boneOMA = boneStructure.getOldMemoryAddress();
|
||||||
bone = new Bone(boneName);
|
bone = new Bone(boneName);
|
||||||
bones.add(bone);
|
bones.add(bone);
|
||||||
@ -184,6 +187,13 @@ public class BoneContext {
|
|||||||
return armatureObjectOMA;
|
return armatureObjectOMA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OMA of the model that owns the bone's skeleton
|
||||||
|
*/
|
||||||
|
public Long getSkeletonOwnerOma() {
|
||||||
|
return skeletonOwnerOma;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the skeleton the bone of this context belongs to
|
* @return the skeleton the bone of this context belongs to
|
||||||
*/
|
*/
|
||||||
@ -200,4 +210,9 @@ public class BoneContext {
|
|||||||
private boolean is(int flagMask) {
|
private boolean is(int flagMask) {
|
||||||
return (flag & flagMask) != 0;
|
return (flag & flagMask) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "BoneContext: " + bone.getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +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.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;
|
||||||
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
|
import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
|
||||||
@ -21,8 +20,6 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
/* package */class BoneConstraint extends Constraint {
|
/* package */class BoneConstraint extends Constraint {
|
||||||
private static final Logger LOGGER = Logger.getLogger(BoneConstraint.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(BoneConstraint.class.getName());
|
||||||
|
|
||||||
protected boolean isNodeTarget;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The bone constraint constructor.
|
* The bone constraint constructor.
|
||||||
*
|
*
|
||||||
@ -45,14 +42,14 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
public 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);
|
||||||
if(nodeTarget == null) {
|
if (nodeTarget == null) {
|
||||||
LOGGER.log(Level.WARNING, "Cannot find target for constraint: {0}.", name);
|
LOGGER.log(Level.WARNING, "Cannot find target for constraint: {0}.", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// the second part of the if expression verifies if the found node
|
// the second part of the if expression verifies if the found node
|
||||||
// (if any) is an armature node
|
// (if any) is an armature node
|
||||||
if (blenderContext.getMarkerValue(ArmatureHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) {
|
if (blenderContext.getMarkerValue(ArmatureHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) {
|
||||||
if(subtargetName.trim().isEmpty()) {
|
if (subtargetName.trim().isEmpty()) {
|
||||||
LOGGER.log(Level.WARNING, "No bone target specified for constraint: {0}.", name);
|
LOGGER.log(Level.WARNING, "No bone target specified for constraint: {0}.", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -63,28 +60,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
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);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
isNodeTarget = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void apply(int frame) {
|
|
||||||
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
|
||||||
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
|
|
||||||
if (targetOMA != null) {
|
|
||||||
if (isNodeTarget) {
|
|
||||||
Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
|
|
||||||
constraintDefinition.bake(ownerTransform, targetTransform, this.ipo.calculateValue(frame));
|
|
||||||
} else {
|
|
||||||
Transform targetTransform = constraintHelper.getTransform(targetOMA, subtargetName, targetSpace);
|
|
||||||
constraintDefinition.bake(ownerTransform, targetTransform, this.ipo.calculateValue(frame));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
constraintDefinition.bake(ownerTransform, null, this.ipo.calculateValue(frame));
|
|
||||||
}
|
|
||||||
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package com.jme3.scene.plugins.blender.constraints;
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import com.jme3.math.Transform;
|
||||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
import com.jme3.scene.plugins.blender.animations.Ipo;
|
import com.jme3.scene.plugins.blender.animations.Ipo;
|
||||||
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
@ -58,15 +60,15 @@ public abstract class Constraint {
|
|||||||
*/
|
*/
|
||||||
public Constraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
|
public Constraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
|
||||||
this.blenderContext = blenderContext;
|
this.blenderContext = blenderContext;
|
||||||
this.name = constraintStructure.getFieldValue("name").toString();
|
name = constraintStructure.getFieldValue("name").toString();
|
||||||
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, ownerOMA, 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();
|
targetOMA = pTar.getOldMemoryAddress();
|
||||||
this.targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
|
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
|
if (subtargetValue != null) {// not all constraint data have the
|
||||||
// subtarget field
|
// subtarget field
|
||||||
@ -77,10 +79,10 @@ public abstract class Constraint {
|
|||||||
// Null constraint has no data, so create it here
|
// Null constraint has no data, so create it here
|
||||||
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, null, blenderContext);
|
constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, null, blenderContext);
|
||||||
}
|
}
|
||||||
this.ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
|
ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
|
||||||
this.ipo = influenceIpo;
|
ipo = influenceIpo;
|
||||||
this.ownerOMA = ownerOMA;
|
this.ownerOMA = ownerOMA;
|
||||||
this.constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
||||||
LOGGER.log(Level.INFO, "Created constraint: {0} with definition: {1}", new Object[] { name, constraintDefinition });
|
LOGGER.log(Level.INFO, "Created constraint: {0} with definition: {1}", new Object[] { name, constraintDefinition });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +102,13 @@ public abstract class Constraint {
|
|||||||
return constraintDefinition.getConstraintTypeName();
|
return constraintDefinition.getConstraintTypeName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the OMAs of the features whose transform had been altered beside the constraint owner
|
||||||
|
*/
|
||||||
|
public Set<Long> getAlteredOmas() {
|
||||||
|
return constraintDefinition.getAlteredOmas();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs validation before baking. Checks factors that can prevent
|
* Performs validation before baking. Checks factors that can prevent
|
||||||
* constraint from baking that could not be checked during constraint
|
* constraint from baking that could not be checked during constraint
|
||||||
@ -107,14 +116,22 @@ public abstract class Constraint {
|
|||||||
*/
|
*/
|
||||||
public abstract boolean validate();
|
public abstract boolean validate();
|
||||||
|
|
||||||
public abstract void apply(int frame);
|
/**
|
||||||
|
* Applies the constraint to owner (and in some cases can alter other bones of the skeleton).
|
||||||
|
* @param frame
|
||||||
|
* the frame of the animation
|
||||||
|
*/
|
||||||
|
public void apply(int frame) {
|
||||||
|
Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
|
||||||
|
constraintDefinition.bake(ownerSpace, targetSpace, targetTransform, ipo.calculateValue(frame));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = 1;
|
int result = 1;
|
||||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
result = prime * result + (name == null ? 0 : name.hashCode());
|
||||||
result = prime * result + ((ownerOMA == null) ? 0 : ownerOMA.hashCode());
|
result = prime * result + (ownerOMA == null ? 0 : ownerOMA.hashCode());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +143,7 @@ public abstract class Constraint {
|
|||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (getClass() != obj.getClass()) {
|
if (this.getClass() != obj.getClass()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Constraint other = (Constraint) obj;
|
Constraint other = (Constraint) obj;
|
||||||
|
@ -228,11 +228,14 @@ public class ConstraintHelper extends AbstractBlenderHelper {
|
|||||||
|
|
||||||
switch (space) {
|
switch (space) {
|
||||||
case CONSTRAINT_SPACE_WORLD:
|
case CONSTRAINT_SPACE_WORLD:
|
||||||
return new Transform(bone.getModelSpacePosition(), bone.getModelSpaceRotation(), bone.getModelSpaceScale());
|
Spatial model = (Spatial) blenderContext.getLoadedFeature(targetBoneContext.getSkeletonOwnerOma(), LoadedFeatureDataType.LOADED_FEATURE);
|
||||||
|
Transform worldTransform = new Transform(bone.getModelSpacePosition(), bone.getModelSpaceRotation(), bone.getModelSpaceScale());
|
||||||
|
worldTransform.getTranslation().addLocal(model.getWorldTranslation());
|
||||||
|
worldTransform.getRotation().multLocal(model.getWorldRotation());
|
||||||
|
worldTransform.getScale().multLocal(model.getWorldScale());
|
||||||
|
return worldTransform;
|
||||||
case CONSTRAINT_SPACE_LOCAL:
|
case CONSTRAINT_SPACE_LOCAL:
|
||||||
Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
|
return new Transform(bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale());
|
||||||
localTransform.setScale(bone.getLocalScale());
|
|
||||||
return localTransform;
|
|
||||||
case CONSTRAINT_SPACE_POSE:
|
case CONSTRAINT_SPACE_POSE:
|
||||||
Node nodeWithAnimationControl = blenderContext.getControlledNode(targetBoneContext.getSkeleton());
|
Node nodeWithAnimationControl = blenderContext.getControlledNode(targetBoneContext.getSkeleton());
|
||||||
Matrix4f m = this.toMatrix(nodeWithAnimationControl.getWorldTransform());
|
Matrix4f m = this.toMatrix(nodeWithAnimationControl.getWorldTransform());
|
||||||
@ -313,15 +316,16 @@ public class ConstraintHelper extends AbstractBlenderHelper {
|
|||||||
bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
|
bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
|
||||||
break;
|
break;
|
||||||
case CONSTRAINT_SPACE_WORLD:
|
case CONSTRAINT_SPACE_WORLD:
|
||||||
Matrix4f boneMatrix = this.toMatrix(transform);
|
Matrix4f boneMatrixInWorldSpace = this.toMatrix(transform);
|
||||||
|
Matrix4f invertedModelMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD)).invertLocal();
|
||||||
|
Matrix4f boneMatrixInModelSpace = invertedModelMatrix.mult(boneMatrixInWorldSpace);
|
||||||
Bone parent = bone.getParent();
|
Bone parent = bone.getParent();
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
Matrix4f invertedParentWorldMatrix = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale()).invertLocal();
|
Matrix4f invertedParentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale()).invertLocal();
|
||||||
boneMatrix = invertedParentWorldMatrix.multLocal(boneMatrix);
|
|
||||||
}
|
|
||||||
|
|
||||||
boneMatrix = invertedNodeMatrix.multLocal(boneMatrix);
|
boneMatrixInModelSpace = invertedParentMatrixInModelSpace.mult(boneMatrixInModelSpace);
|
||||||
bone.setBindTransforms(boneMatrix.toTranslationVector(), boneMatrix.toRotationQuat(), boneMatrix.toScaleVector());
|
}
|
||||||
|
bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector());
|
||||||
break;
|
break;
|
||||||
case CONSTRAINT_SPACE_POSE:
|
case CONSTRAINT_SPACE_POSE:
|
||||||
Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform());
|
Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform());
|
||||||
@ -391,11 +395,10 @@ public class ConstraintHelper extends AbstractBlenderHelper {
|
|||||||
* @return 4x4 matrix that represents the given transform
|
* @return 4x4 matrix that represents the given transform
|
||||||
*/
|
*/
|
||||||
public Matrix4f toMatrix(Transform transform) {
|
public Matrix4f toMatrix(Transform transform) {
|
||||||
Matrix4f result = Matrix4f.IDENTITY;
|
|
||||||
if (transform != null) {
|
if (transform != null) {
|
||||||
result = this.toMatrix(transform.getTranslation(), transform.getRotation(), transform.getScale());
|
return this.toMatrix(transform.getTranslation(), transform.getRotation(), transform.getScale());
|
||||||
}
|
}
|
||||||
return result;
|
return Matrix4f.IDENTITY.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,9 +2,11 @@ package com.jme3.scene.plugins.blender.constraints;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -38,31 +40,33 @@ import com.jme3.util.TempVars;
|
|||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
public class SimulationNode {
|
public class SimulationNode {
|
||||||
private static final Logger LOGGER = Logger.getLogger(SimulationNode.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(SimulationNode.class.getName());
|
||||||
|
|
||||||
|
/** The blender context. */
|
||||||
|
private BlenderContext blenderContext;
|
||||||
/** The name of the node (for debugging purposes). */
|
/** The name of the node (for debugging purposes). */
|
||||||
private String name;
|
private String name;
|
||||||
/** A list of children for the node (either bones or child spatials). */
|
/** A list of children for the node (either bones or child spatials). */
|
||||||
private List<SimulationNode> children = new ArrayList<SimulationNode>();
|
private List<SimulationNode> children = new ArrayList<SimulationNode>();
|
||||||
/** A list of constraints that the current node has. */
|
/** A list of constraints that the current node has. */
|
||||||
private List<Constraint> constraints;
|
private List<Constraint> constraints;
|
||||||
/** A list of node's animations. */
|
/** A list of node's animations. */
|
||||||
private List<Animation> animations;
|
private List<Animation> animations;
|
||||||
|
|
||||||
/** The nodes spatial (if null then the boneContext should be set). */
|
/** The nodes spatial (if null then the boneContext should be set). */
|
||||||
private Spatial spatial;
|
private Spatial spatial;
|
||||||
/** The skeleton of the bone (not null if the node simulated the bone). */
|
/** The skeleton of the bone (not null if the node simulated the bone). */
|
||||||
private Skeleton skeleton;
|
private Skeleton skeleton;
|
||||||
/** Animation controller for the node's feature. */
|
/** Animation controller for the node's feature. */
|
||||||
private AnimControl animControl;
|
private AnimControl animControl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The star transform of a spatial. Needed to properly reset the spatial to
|
* The star transform of a spatial. Needed to properly reset the spatial to
|
||||||
* its start position.
|
* its start position.
|
||||||
*/
|
*/
|
||||||
private Transform spatialStartTransform;
|
private Transform spatialStartTransform;
|
||||||
/** Star transformations for bones. Needed to properly reset the bones. */
|
/** Star transformations for bones. Needed to properly reset the bones. */
|
||||||
private Map<Bone, Transform> boneStartTransforms;
|
private Map<Bone, Transform> boneStartTransforms;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the nodes tree for the given feature. The feature (bone or
|
* Builds the nodes tree for the given feature. The feature (bone or
|
||||||
@ -89,12 +93,13 @@ public class SimulationNode {
|
|||||||
* indicates if the feature is a root bone or root spatial or not
|
* indicates if the feature is a root bone or root spatial or not
|
||||||
*/
|
*/
|
||||||
private SimulationNode(Long featureOMA, BlenderContext blenderContext, boolean rootNode) {
|
private SimulationNode(Long featureOMA, BlenderContext blenderContext, boolean rootNode) {
|
||||||
|
this.blenderContext = blenderContext;
|
||||||
Node spatial = (Node) blenderContext.getLoadedFeature(featureOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
Node spatial = (Node) blenderContext.getLoadedFeature(featureOMA, LoadedFeatureDataType.LOADED_FEATURE);
|
||||||
if (blenderContext.getMarkerValue(ArmatureHelper.ARMATURE_NODE_MARKER, spatial) != null) {
|
if (blenderContext.getMarkerValue(ArmatureHelper.ARMATURE_NODE_MARKER, spatial) != null) {
|
||||||
this.skeleton = blenderContext.getSkeleton(featureOMA);
|
skeleton = blenderContext.getSkeleton(featureOMA);
|
||||||
|
|
||||||
Node nodeWithAnimationControl = blenderContext.getControlledNode(skeleton);
|
Node nodeWithAnimationControl = blenderContext.getControlledNode(skeleton);
|
||||||
this.animControl = nodeWithAnimationControl.getControl(AnimControl.class);
|
animControl = nodeWithAnimationControl.getControl(AnimControl.class);
|
||||||
|
|
||||||
boneStartTransforms = new HashMap<Bone, Transform>();
|
boneStartTransforms = new HashMap<Bone, Transform>();
|
||||||
for (int i = 0; i < skeleton.getBoneCount(); ++i) {
|
for (int i = 0; i < skeleton.getBoneCount(); ++i) {
|
||||||
@ -106,10 +111,10 @@ public class SimulationNode {
|
|||||||
throw new IllegalStateException("Given spatial must be a root node!");
|
throw new IllegalStateException("Given spatial must be a root node!");
|
||||||
}
|
}
|
||||||
this.spatial = spatial;
|
this.spatial = spatial;
|
||||||
this.spatialStartTransform = spatial.getLocalTransform().clone();
|
spatialStartTransform = spatial.getLocalTransform().clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.name = '>' + spatial.getName() + '<';
|
name = '>' + spatial.getName() + '<';
|
||||||
|
|
||||||
constraints = this.findConstraints(featureOMA, blenderContext);
|
constraints = this.findConstraints(featureOMA, blenderContext);
|
||||||
if (constraints == null) {
|
if (constraints == null) {
|
||||||
@ -143,14 +148,14 @@ public class SimulationNode {
|
|||||||
|
|
||||||
LOGGER.info("Removing invalid constraints.");
|
LOGGER.info("Removing invalid constraints.");
|
||||||
List<Constraint> validConstraints = new ArrayList<Constraint>(constraints.size());
|
List<Constraint> validConstraints = new ArrayList<Constraint>(constraints.size());
|
||||||
for (Constraint constraint : this.constraints) {
|
for (Constraint constraint : constraints) {
|
||||||
if (constraint.validate()) {
|
if (constraint.validate()) {
|
||||||
validConstraints.add(constraint);
|
validConstraints.add(constraint);
|
||||||
} else {
|
} else {
|
||||||
LOGGER.log(Level.WARNING, "Constraint {0} is invalid and will not be applied.", constraint.name);
|
LOGGER.log(Level.WARNING, "Constraint {0} is invalid and will not be applied.", constraint.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.constraints = validConstraints;
|
constraints = validConstraints;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -246,6 +251,7 @@ public class SimulationNode {
|
|||||||
private void simulateSkeleton() {
|
private void simulateSkeleton() {
|
||||||
if (constraints != null && constraints.size() > 0) {
|
if (constraints != null && constraints.size() > 0) {
|
||||||
boolean applyStaticConstraints = true;
|
boolean applyStaticConstraints = true;
|
||||||
|
Set<Long> alteredOmas = new HashSet<Long>();
|
||||||
|
|
||||||
if (animations != null) {
|
if (animations != null) {
|
||||||
TempVars vars = TempVars.get();
|
TempVars vars = TempVars.get();
|
||||||
@ -256,7 +262,7 @@ public class SimulationNode {
|
|||||||
float maxTime = animationTimeBoundaries[1];
|
float maxTime = animationTimeBoundaries[1];
|
||||||
|
|
||||||
Map<Integer, VirtualTrack> tracks = new HashMap<Integer, VirtualTrack>();
|
Map<Integer, VirtualTrack> tracks = new HashMap<Integer, VirtualTrack>();
|
||||||
Map<Integer, Transform> previousTransforms = new HashMap<Integer, Transform>();
|
Map<Integer, Transform> previousTransforms = this.getInitialTransforms();
|
||||||
for (int frame = 0; frame < maxFrame; ++frame) {
|
for (int frame = 0; frame < maxFrame; ++frame) {
|
||||||
// this MUST be done here, otherwise setting next frame of animation will
|
// this MUST be done here, otherwise setting next frame of animation will
|
||||||
// lead to possible errors
|
// lead to possible errors
|
||||||
@ -265,40 +271,36 @@ public class SimulationNode {
|
|||||||
// first set proper time for all bones in all the tracks ...
|
// first set proper time for all bones in all the tracks ...
|
||||||
for (Track track : animation.getTracks()) {
|
for (Track track : animation.getTracks()) {
|
||||||
float time = ((BoneTrack) track).getTimes()[frame];
|
float time = ((BoneTrack) track).getTimes()[frame];
|
||||||
Integer boneIndex = ((BoneTrack) track).getTargetBoneIndex();
|
|
||||||
|
|
||||||
track.setTime(time, 1, animControl, animChannel, vars);
|
track.setTime(time, 1, animControl, animChannel, vars);
|
||||||
skeleton.updateWorldVectors();
|
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 ...
|
// ... and then apply constraints ...
|
||||||
for (Constraint constraint : constraints) {
|
for (Constraint constraint : constraints) {
|
||||||
constraint.apply(frame);
|
constraint.apply(frame);
|
||||||
|
if (constraint.getAlteredOmas() != null) {
|
||||||
|
alteredOmas.addAll(constraint.getAlteredOmas());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ... add virtual tracks if neccessary, for bones that were altered but had no tracks before ...
|
||||||
|
for (Long boneOMA : alteredOmas) {
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(boneOMA);
|
||||||
|
int boneIndex = skeleton.getBoneIndex(boneContext.getBone());
|
||||||
|
if (!tracks.containsKey(boneIndex)) {
|
||||||
|
tracks.put(boneIndex, new VirtualTrack(maxFrame, maxTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alteredOmas.clear();
|
||||||
|
|
||||||
// ... and fill in another frame in the result track
|
// ... and fill in another frame in the result track
|
||||||
for (Track track : animation.getTracks()) {
|
for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
|
||||||
Integer boneIndex = ((BoneTrack) track).getTargetBoneIndex();
|
Integer boneIndex = trackEntry.getKey();
|
||||||
Bone bone = skeleton.getBone(boneIndex);
|
Bone bone = skeleton.getBone(boneIndex);
|
||||||
|
|
||||||
// take the initial transform of a bone
|
// take the initial transform of a bone and its virtual track
|
||||||
Transform previousTransform = previousTransforms.get(boneIndex);
|
Transform previousTransform = previousTransforms.get(boneIndex);
|
||||||
|
VirtualTrack vTrack = trackEntry.getValue();
|
||||||
VirtualTrack vTrack = tracks.get(boneIndex);
|
|
||||||
if (vTrack == null) {
|
|
||||||
vTrack = new VirtualTrack(maxFrame, maxTime);
|
|
||||||
tracks.put(boneIndex, vTrack);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3f bonePositionDifference = bone.getLocalPosition().subtract(previousTransform.getTranslation());
|
Vector3f bonePositionDifference = bone.getLocalPosition().subtract(previousTransform.getTranslation());
|
||||||
Quaternion boneRotationDifference = bone.getLocalRotation().mult(previousTransform.getRotation().inverse()).normalizeLocal();
|
Quaternion boneRotationDifference = bone.getLocalRotation().mult(previousTransform.getRotation().inverse()).normalizeLocal();
|
||||||
@ -319,13 +321,18 @@ public class SimulationNode {
|
|||||||
for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
|
for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
|
||||||
Track newTrack = trackEntry.getValue().getAsBoneTrack(trackEntry.getKey());
|
Track newTrack = trackEntry.getValue().getAsBoneTrack(trackEntry.getKey());
|
||||||
if (newTrack != null) {
|
if (newTrack != null) {
|
||||||
|
boolean trackReplaced = false;
|
||||||
for (Track track : animation.getTracks()) {
|
for (Track track : animation.getTracks()) {
|
||||||
if (((BoneTrack) track).getTargetBoneIndex() == trackEntry.getKey().intValue()) {
|
if (((BoneTrack) track).getTargetBoneIndex() == trackEntry.getKey().intValue()) {
|
||||||
animation.removeTrack(track);
|
animation.removeTrack(track);
|
||||||
animation.addTrack(newTrack);
|
animation.addTrack(newTrack);
|
||||||
|
trackReplaced = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!trackReplaced) {
|
||||||
|
animation.addTrack(newTrack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
applyStaticConstraints = false;
|
applyStaticConstraints = false;
|
||||||
}
|
}
|
||||||
@ -341,6 +348,7 @@ public class SimulationNode {
|
|||||||
for (Constraint constraint : constraints) {
|
for (Constraint constraint : constraints) {
|
||||||
constraint.apply(0);
|
constraint.apply(0);
|
||||||
}
|
}
|
||||||
|
skeleton.updateWorldVectors();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,7 +363,6 @@ public class SimulationNode {
|
|||||||
} else {
|
} else {
|
||||||
this.simulateSkeleton();
|
this.simulateSkeleton();
|
||||||
}
|
}
|
||||||
this.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -406,6 +413,19 @@ public class SimulationNode {
|
|||||||
return result.size() > 0 ? result : null;
|
return result.size() > 0 ? result : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the initial transforms for all bones in the skelketon.
|
||||||
|
* @return the map where the key is the bone index and the value us the bone's initial transformation
|
||||||
|
*/
|
||||||
|
private Map<Integer, Transform> getInitialTransforms() {
|
||||||
|
Map<Integer, Transform> result = new HashMap<Integer, Transform>();
|
||||||
|
for (int i = 0; i < skeleton.getBoneCount(); ++i) {
|
||||||
|
Bone bone = skeleton.getBone(i);
|
||||||
|
result.put(i, new Transform(bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale()));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return name;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.jme3.scene.plugins.blender.constraints;
|
package com.jme3.scene.plugins.blender.constraints;
|
||||||
|
|
||||||
import com.jme3.math.Transform;
|
|
||||||
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.Ipo;
|
import com.jme3.scene.plugins.blender.animations.Ipo;
|
||||||
@ -25,12 +24,4 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void apply(int frame) {
|
|
||||||
Transform ownerTransform = constraintHelper.getTransform(ownerOMA, null, ownerSpace);
|
|
||||||
Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
|
|
||||||
constraintDefinition.bake(ownerTransform, targetTransform, this.ipo.calculateValue(frame));
|
|
||||||
constraintHelper.applyTransform(ownerOMA, subtargetName, ownerSpace, ownerTransform);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
package com.jme3.scene.plugins.blender.constraints.definitions;
|
package com.jme3.scene.plugins.blender.constraints.definitions;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
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.constraints.ConstraintHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -11,14 +15,17 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
* @author Marcin Roguski (Kaelthas)
|
* @author Marcin Roguski (Kaelthas)
|
||||||
*/
|
*/
|
||||||
public abstract class ConstraintDefinition {
|
public abstract class ConstraintDefinition {
|
||||||
|
protected ConstraintHelper constraintHelper;
|
||||||
/** Constraints flag. Used to load user's options applied to the constraint. */
|
/** Constraints flag. Used to load user's options applied to the constraint. */
|
||||||
protected int flag;
|
protected int flag;
|
||||||
/** The constraint's owner. Loaded during runtime. */
|
/** The constraint's owner. Loaded during runtime. */
|
||||||
private Object owner;
|
private Object owner;
|
||||||
/** The blender context. */
|
/** The blender context. */
|
||||||
private BlenderContext blenderContext;
|
protected BlenderContext blenderContext;
|
||||||
/** The constraint's owner OMA. */
|
/** The constraint's owner OMA. */
|
||||||
private Long ownerOMA;
|
protected Long ownerOMA;
|
||||||
|
/** Stores the OMA addresses of all features whose transform had been altered beside the constraint owner. */
|
||||||
|
protected Set<Long> alteredOmas;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a constraint definition based on the constraint definition
|
* Loads a constraint definition based on the constraint definition
|
||||||
@ -39,6 +46,7 @@ public abstract class ConstraintDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.blenderContext = blenderContext;
|
this.blenderContext = blenderContext;
|
||||||
|
constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
||||||
this.ownerOMA = ownerOMA;
|
this.ownerOMA = ownerOMA;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,6 +75,13 @@ public abstract class ConstraintDefinition {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a list of all OMAs of the features that the constraint had altered beside its owner
|
||||||
|
*/
|
||||||
|
public Set<Long> getAlteredOmas() {
|
||||||
|
return alteredOmas;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the type name of the constraint
|
* @return the type name of the constraint
|
||||||
*/
|
*/
|
||||||
@ -75,12 +90,14 @@ public abstract class ConstraintDefinition {
|
|||||||
/**
|
/**
|
||||||
* Bakes the constraint for the current feature (bone or spatial) position.
|
* Bakes the constraint for the current feature (bone or spatial) position.
|
||||||
*
|
*
|
||||||
* @param ownerTransform
|
* @param ownerSpace
|
||||||
* the input transform (here the result is stored)
|
* the space where owner transform will be evaluated in
|
||||||
|
* @param targetSpace
|
||||||
|
* the space where target transform will be evaluated in
|
||||||
* @param targetTransform
|
* @param targetTransform
|
||||||
* the target transform used by some of the constraints
|
* the target transform used by some of the constraints
|
||||||
* @param influence
|
* @param influence
|
||||||
* the influence of the constraint (from range <0; 1>)
|
* the influence of the constraint (from range <0; 1>)
|
||||||
*/
|
*/
|
||||||
public abstract void bake(Transform ownerTransform, Transform targetTransform, float influence);
|
public abstract void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ 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;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,15 +28,17 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
|
||||||
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
|
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
|
||||||
// distance limit does not work on bones who have parent
|
// distance limit does not work on bones who have parent
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
||||||
|
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
|
||||||
|
|
||||||
Vector3f v = ownerTransform.getTranslation().subtract(targetTransform.getTranslation());
|
Vector3f v = ownerTransform.getTranslation().subtract(targetTransform.getTranslation());
|
||||||
float currentDistance = v.length();
|
float currentDistance = v.length();
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case LIMITDIST_INSIDE:
|
case LIMITDIST_INSIDE:
|
||||||
if (currentDistance >= dist) {
|
if (currentDistance >= dist) {
|
||||||
@ -62,6 +66,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
|
throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -50,6 +50,7 @@ public class ConstraintDefinitionFactory {
|
|||||||
CONSTRAINT_CLASSES.put("bRotLimitConstraint", ConstraintDefinitionRotLimit.class);
|
CONSTRAINT_CLASSES.put("bRotLimitConstraint", ConstraintDefinitionRotLimit.class);
|
||||||
CONSTRAINT_CLASSES.put("bSizeLikeConstraint", ConstraintDefinitionSizeLike.class);
|
CONSTRAINT_CLASSES.put("bSizeLikeConstraint", ConstraintDefinitionSizeLike.class);
|
||||||
CONSTRAINT_CLASSES.put("bSizeLimitConstraint", ConstraintDefinitionSizeLimit.class);
|
CONSTRAINT_CLASSES.put("bSizeLimitConstraint", ConstraintDefinitionSizeLimit.class);
|
||||||
|
CONSTRAINT_CLASSES.put("bKinematicConstraint", ConstraintDefinitionIK.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>();
|
||||||
@ -58,7 +59,6 @@ public class ConstraintDefinitionFactory {
|
|||||||
UNSUPPORTED_CONSTRAINTS.put("bChildOfConstraint", "Child of");
|
UNSUPPORTED_CONSTRAINTS.put("bChildOfConstraint", "Child of");
|
||||||
UNSUPPORTED_CONSTRAINTS.put("bClampToConstraint", "Clamp to");
|
UNSUPPORTED_CONSTRAINTS.put("bClampToConstraint", "Clamp to");
|
||||||
UNSUPPORTED_CONSTRAINTS.put("bFollowPathConstraint", "Follow path");
|
UNSUPPORTED_CONSTRAINTS.put("bFollowPathConstraint", "Follow path");
|
||||||
UNSUPPORTED_CONSTRAINTS.put("bKinematicConstraint", "Inverse kinematic");
|
|
||||||
UNSUPPORTED_CONSTRAINTS.put("bLockTrackConstraint", "Lock track");
|
UNSUPPORTED_CONSTRAINTS.put("bLockTrackConstraint", "Lock track");
|
||||||
UNSUPPORTED_CONSTRAINTS.put("bMinMaxConstraint", "Min max");
|
UNSUPPORTED_CONSTRAINTS.put("bMinMaxConstraint", "Min max");
|
||||||
UNSUPPORTED_CONSTRAINTS.put("bPythonConstraint", "Python/Script");
|
UNSUPPORTED_CONSTRAINTS.put("bPythonConstraint", "Python/Script");
|
||||||
|
@ -0,0 +1,115 @@
|
|||||||
|
package com.jme3.scene.plugins.blender.constraints.definitions;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.jme3.animation.Bone;
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
|
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.animations.BoneContext;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
|
public class ConstraintDefinitionIK extends ConstraintDefinition {
|
||||||
|
|
||||||
|
private static final int FLAG_POSITION = 0x20;
|
||||||
|
|
||||||
|
/** The number of affected bones. Zero means that all parent bones of the current bone should take part in baking. */
|
||||||
|
private int bonesAffected;
|
||||||
|
private float chainLength;
|
||||||
|
private BoneContext[] bones;
|
||||||
|
private boolean needToCompute = true;
|
||||||
|
|
||||||
|
public ConstraintDefinitionIK(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
|
||||||
|
super(constraintData, ownerOMA, blenderContext);
|
||||||
|
bonesAffected = ((Number) constraintData.getFieldValue("rootbone")).intValue();
|
||||||
|
|
||||||
|
if ((flag & FLAG_POSITION) == 0) {
|
||||||
|
needToCompute = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needToCompute) {
|
||||||
|
alteredOmas = new HashSet<Long>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
|
||||||
|
if (needToCompute) {
|
||||||
|
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
|
||||||
|
BoneContext[] boneContexts = this.getBones();
|
||||||
|
float b = chainLength;
|
||||||
|
Quaternion boneWorldRotation = new Quaternion();
|
||||||
|
|
||||||
|
for (int i = 0; i < boneContexts.length; ++i) {
|
||||||
|
Bone bone = boneContexts[i].getBone();
|
||||||
|
|
||||||
|
bone.updateWorldVectors();
|
||||||
|
Transform boneWorldTransform = constraintHelper.getTransform(boneContexts[i].getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD);
|
||||||
|
|
||||||
|
Vector3f head = boneWorldTransform.getTranslation();
|
||||||
|
Vector3f tail = head.add(bone.getModelSpaceRotation().mult(Vector3f.UNIT_Y.mult(boneContexts[i].getLength())));
|
||||||
|
|
||||||
|
Vector3f vectorA = tail.subtract(head);
|
||||||
|
float a = vectorA.length();
|
||||||
|
vectorA.normalizeLocal();
|
||||||
|
|
||||||
|
Vector3f vectorC = targetTransform.getTranslation().subtract(head);
|
||||||
|
float c = vectorC.length();
|
||||||
|
vectorC.normalizeLocal();
|
||||||
|
|
||||||
|
b -= a;
|
||||||
|
float theta = 0;
|
||||||
|
|
||||||
|
if (c >= a + b) {
|
||||||
|
theta = vectorA.angleBetween(vectorC);
|
||||||
|
} else if (c <= FastMath.abs(a - b) && i < boneContexts.length - 1) {
|
||||||
|
theta = vectorA.angleBetween(vectorC) - FastMath.HALF_PI;
|
||||||
|
} else {
|
||||||
|
theta = vectorA.angleBetween(vectorC) - FastMath.acos(-(b * b - a * a - c * c) / (2 * a * c));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theta != 0) {
|
||||||
|
Vector3f vectorR = vectorA.cross(vectorC);
|
||||||
|
boneWorldRotation.fromAngleAxis(theta, vectorR);
|
||||||
|
boneWorldTransform.getRotation().multLocal(boneWorldRotation);
|
||||||
|
constraintHelper.applyTransform(boneContexts[i].getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneWorldTransform);
|
||||||
|
}
|
||||||
|
|
||||||
|
bone.updateWorldVectors();
|
||||||
|
alteredOmas.add(boneContexts[i].getBoneOma());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConstraintTypeName() {
|
||||||
|
return "Inverse kinematics";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the bone contexts of all bones that will be used in this constraint computations
|
||||||
|
*/
|
||||||
|
private BoneContext[] getBones() {
|
||||||
|
if (bones == null) {
|
||||||
|
List<BoneContext> bones = new ArrayList<BoneContext>();
|
||||||
|
Bone bone = (Bone) this.getOwner();
|
||||||
|
while (bone != null) {
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(bone);
|
||||||
|
bones.add(0, boneContext);
|
||||||
|
chainLength += boneContext.getLength();
|
||||||
|
if (bonesAffected != 0 && bones.size() >= bonesAffected) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bone = bone.getParent();
|
||||||
|
}
|
||||||
|
this.bones = bones.toArray(new BoneContext[bones.size()]);
|
||||||
|
}
|
||||||
|
return bones;
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,8 @@ 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;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,12 +44,16 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
|
||||||
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
|
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
|
||||||
// cannot copy the location of a bone attached to its parent,
|
// cannot copy the location of a bone attached to its parent,
|
||||||
// Blender forbids that
|
// Blender forbids that
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
||||||
|
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
|
||||||
|
|
||||||
Vector3f ownerLocation = ownerTransform.getTranslation();
|
Vector3f ownerLocation = ownerTransform.getTranslation();
|
||||||
Vector3f targetLocation = targetTransform.getTranslation();
|
Vector3f targetLocation = targetTransform.getTranslation();
|
||||||
|
|
||||||
@ -82,6 +88,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
|
startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
|
||||||
ownerLocation.addLocal(startLocation);
|
ownerLocation.addLocal(startLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -4,6 +4,8 @@ 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;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,12 +55,15 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
|
||||||
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
|
if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
|
||||||
// location limit does not work on bones who have parent
|
// location limit does not work on bones who have parent
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
||||||
|
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
|
||||||
|
|
||||||
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]) {
|
||||||
@ -79,6 +84,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
if ((flag & LIMIT_ZMAX) != 0 && translation.z > limits[2][1]) {
|
if ((flag & LIMIT_ZMAX) != 0 && translation.z > limits[2][1]) {
|
||||||
translation.z -= (translation.z - limits[2][1]) * influence;
|
translation.z -= (translation.z - limits[2][1]) * influence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -2,6 +2,7 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
|
|||||||
|
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,7 +17,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Space ownerSpace, Space targetSpace, 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
|
|||||||
import com.jme3.math.Quaternion;
|
import com.jme3.math.Quaternion;
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
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.constraints.ConstraintHelper.Space;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,7 +29,10 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
||||||
|
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
|
||||||
|
|
||||||
Quaternion ownerRotation = ownerTransform.getRotation();
|
Quaternion ownerRotation = ownerTransform.getRotation();
|
||||||
ownerAngles = ownerRotation.toAngles(ownerAngles);
|
ownerAngles = ownerRotation.toAngles(ownerAngles);
|
||||||
targetAngles = targetTransform.getRotation().toAngles(targetAngles);
|
targetAngles = targetTransform.getRotation().toAngles(targetAngles);
|
||||||
@ -64,6 +69,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
// ownerLocation.addLocal(startLocation);
|
// ownerLocation.addLocal(startLocation);
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -3,6 +3,8 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
|
|||||||
import com.jme3.math.FastMath;
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
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.constraints.ConstraintHelper.Space;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,7 +69,10 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
||||||
|
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
|
||||||
|
|
||||||
ownerTransform.getRotation().toAngles(angles);
|
ownerTransform.getRotation().toAngles(angles);
|
||||||
// make sure that the rotations are always in range [0, 2PI)
|
// make sure that the rotations are always in range [0, 2PI)
|
||||||
// TODO: same comment as in constructor
|
// TODO: same comment as in constructor
|
||||||
@ -105,6 +110,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
angles[2] -= difference;
|
angles[2] -= difference;
|
||||||
}
|
}
|
||||||
ownerTransform.getRotation().fromAngles(angles);
|
ownerTransform.getRotation().fromAngles(angles);
|
||||||
|
|
||||||
|
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -3,6 +3,8 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
|
|||||||
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;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,7 +32,10 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
|
||||||
|
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
||||||
|
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
|
||||||
|
|
||||||
Vector3f ownerScale = ownerTransform.getScale();
|
Vector3f ownerScale = ownerTransform.getScale();
|
||||||
Vector3f targetScale = targetTransform.getScale();
|
Vector3f targetScale = targetTransform.getScale();
|
||||||
|
|
||||||
@ -50,6 +55,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
ownerScale.z = targetScale.z * influence + (1.0f - influence) * ownerScale.z;
|
ownerScale.z = targetScale.z * influence + (1.0f - influence) * ownerScale.z;
|
||||||
}
|
}
|
||||||
ownerScale.addLocal(offset);
|
ownerScale.addLocal(offset);
|
||||||
|
|
||||||
|
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -3,6 +3,8 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
|
|||||||
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;
|
||||||
|
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
import com.jme3.scene.plugins.blender.file.Structure;
|
import com.jme3.scene.plugins.blender.file.Structure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,9 +54,11 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
|
||||||
Vector3f scale = ownerTransform.getScale();
|
BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
|
||||||
|
Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
|
||||||
|
|
||||||
|
Vector3f scale = ownerTransform.getScale();
|
||||||
if ((flag & LIMIT_XMIN) != 0 && scale.x < limits[0][0]) {
|
if ((flag & LIMIT_XMIN) != 0 && scale.x < limits[0][0]) {
|
||||||
scale.x -= (scale.x - limits[0][0]) * influence;
|
scale.x -= (scale.x - limits[0][0]) * influence;
|
||||||
}
|
}
|
||||||
@ -73,6 +77,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
|||||||
if ((flag & LIMIT_ZMAX) != 0 && scale.z > limits[2][1]) {
|
if ((flag & LIMIT_ZMAX) != 0 && scale.z > limits[2][1]) {
|
||||||
scale.z -= (scale.z - limits[2][1]) * influence;
|
scale.z -= (scale.z - limits[2][1]) * influence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.jme3.scene.plugins.blender.constraints.definitions;
|
package com.jme3.scene.plugins.blender.constraints.definitions;
|
||||||
|
|
||||||
import com.jme3.math.Transform;
|
import com.jme3.math.Transform;
|
||||||
|
import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a constraint that is defined by blender but not
|
* This class represents a constraint that is defined by blender but not
|
||||||
@ -18,7 +19,7 @@ import com.jme3.math.Transform;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
|
public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user