blob: d63f342debb4f9e807ec53ba8bc31728e0f1c865 [file] [log] [blame]
package com.jme3.scene.plugins.blender.constraints;
import com.jme3.animation.Animation;
import com.jme3.animation.Bone;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.animations.Ipo;
import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.ogre.AnimData;
/**
* This class represents 'Rot limit' constraint type in blender.
*
* @author Marcin Roguski (Kaelthas)
*/
/* package */class ConstraintRotLimit extends Constraint {
private static final int LIMIT_XROT = 0x01;
private static final int LIMIT_YROT = 0x02;
private static final int LIMIT_ZROT = 0x04;
protected float[][] limits = new float[3][2];
protected int flag;
protected boolean updated;
/**
* This constructor creates the constraint instance.
*
* @param constraintStructure
* the constraint's structure (bConstraint clss in blender 2.49).
* @param ownerOMA
* the old memory address of the constraint owner
* @param influenceIpo
* the ipo curve of the influence factor
* @param blenderContext
* the blender context
* @throws BlenderFileException
* this exception is thrown when the blender file is somehow
* corrupted
*/
public ConstraintRotLimit(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
super(constraintStructure, ownerOMA, influenceIpo, blenderContext);
flag = ((Number) data.getFieldValue("flag")).intValue();
if (blenderContext.getBlenderKey().isFixUpAxis() && owner.spatial != null) {
limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
limits[2][0] = -((Number) data.getFieldValue("ymin")).floatValue();
limits[2][1] = -((Number) data.getFieldValue("ymax")).floatValue();
limits[1][0] = ((Number) data.getFieldValue("zmin")).floatValue();
limits[1][1] = ((Number) data.getFieldValue("zmax")).floatValue();
// swapping Y and X limits flag in the bitwise flag
int limitY = flag & LIMIT_YROT;
int limitZ = flag & LIMIT_ZROT;
flag &= LIMIT_XROT;// clear the other flags to swap them
flag |= limitY << 1;
flag |= limitZ >> 1;
} else {
limits[0][0] = ((Number) data.getFieldValue("xmin")).floatValue();
limits[0][1] = ((Number) data.getFieldValue("xmax")).floatValue();
limits[1][0] = ((Number) data.getFieldValue("ymin")).floatValue();
limits[1][1] = ((Number) data.getFieldValue("ymax")).floatValue();
limits[2][0] = ((Number) data.getFieldValue("zmin")).floatValue();
limits[2][1] = ((Number) data.getFieldValue("zmax")).floatValue();
}
// until blender 2.49 the rotations values were stored in degrees
if (blenderContext.getBlenderVersion() <= 249) {
for (int i = 0; i < limits.length; ++i) {
limits[i][0] *= FastMath.DEG_TO_RAD;
limits[i][1] *= FastMath.DEG_TO_RAD;
}
}
}
@Override
protected void bakeConstraint() {
this.update();
Object owner = this.owner.getObject();
AnimData animData = blenderContext.getAnimData(this.owner.getOma());
if (animData != null) {
for (Animation animation : animData.anims) {
BlenderTrack track = this.getTrack(owner, animData.skeleton, animation);
Quaternion[] rotations = track.getRotations();
float[] angles = new float[3];
int maxFrames = rotations.length;
for (int frame = 0; frame < maxFrames; ++frame) {
rotations[frame].toAngles(angles);
this.rotLimit(angles, ipo.calculateValue(frame));
rotations[frame].fromAngles(angles);
}
track.setKeyframes(track.getTimes(), track.getTranslations(), rotations, track.getScales());
}
}
if (owner instanceof Spatial) {
Transform ownerTransform = this.owner.getTransform();
float[] angles = ownerTransform.getRotation().toAngles(null);
this.rotLimit(angles, ipo.calculateValue(0));
ownerTransform.getRotation().fromAngles(angles);
this.owner.applyTransform(ownerTransform);
}
}
/**
* This method computes new constrained angles.
*
* @param angles
* angles to be altered
* @param influence
* the alteration influence
*/
private void rotLimit(float[] angles, float influence) {
if ((flag & LIMIT_XROT) != 0) {
float difference = 0.0f;
if (angles[0] < limits[0][0]) {
difference = (angles[0] - limits[0][0]) * influence;
} else if (angles[0] > limits[0][1]) {
difference = (angles[0] - limits[0][1]) * influence;
}
angles[0] -= difference;
}
if ((flag & LIMIT_YROT) != 0) {
float difference = 0.0f;
if (angles[1] < limits[1][0]) {
difference = (angles[1] - limits[1][0]) * influence;
} else if (angles[1] > limits[1][1]) {
difference = (angles[1] - limits[1][1]) * influence;
}
angles[1] -= difference;
}
if ((flag & LIMIT_ZROT) != 0) {
float difference = 0.0f;
if (angles[2] < limits[2][0]) {
difference = (angles[2] - limits[2][0]) * influence;
} else if (angles[2] > limits[2][1]) {
difference = (angles[2] - limits[2][1]) * influence;
}
angles[2] -= difference;
}
}
/**
* This method is called before baking (performes its operations only once).
* It is important to update the state of the limits and owner/target before
* baking the constraint.
*/
private void update() {
if (!updated) {
updated = true;
if (owner != null) {
owner.update();
}
if (target != null) {
target.update();
}
if (this.owner.getObject() instanceof Bone) {// for bones we need to
// change the sign
// of the limits
for (int i = 0; i < limits.length; ++i) {
limits[i][0] *= -1;
limits[i][1] *= -1;
}
}
// sorting the limits (lower is always first)
for (int i = 0; i < limits.length; ++i) {
if (limits[i][0] > limits[i][1]) {
float temp = limits[i][0];
limits[i][0] = limits[i][1];
limits[i][1] = temp;
}
}
}
}
}