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; } } } } }