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