159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapackage com.jme3.scene.plugins.blender.animations;
259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.animation.Bone;
459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.animation.BoneTrack;
559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Quaternion;
659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Vector3f;
759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Node;
859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Spatial;
959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.Arrays;
1059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
1159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/**
1259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * The purpose of this class is to imitate bone's movement when calculating inverse kinematics.
1359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @author Marcin Roguski (Kaelthas)
1459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */
1559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapublic class CalculationBone extends Node {
1659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	private Bone bone;
1759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	/** The bone's tracks. Will be altered at the end of calculation process. */
1859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	private BoneTrack track;
1959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	/** The starting position of the bone. */
2059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	private Vector3f startTranslation;
2159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	/** The starting rotation of the bone. */
2259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	private Quaternion startRotation;
2359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	/** The starting scale of the bone. */
2459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	private Vector3f startScale;
2559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	private Vector3f[] translations;
2659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	private Quaternion[] rotations;
2759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	private Vector3f[] scales;
2859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
2959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	public CalculationBone(Bone bone, int boneFramesCount) {
3059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this.bone = bone;
3159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this.startRotation = bone.getModelSpaceRotation().clone();
3259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this.startTranslation = bone.getModelSpacePosition().clone();
3359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this.startScale = bone.getModelSpaceScale().clone();
3459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this.reset();
3559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		if(boneFramesCount > 0) {
3659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			this.translations = new Vector3f[boneFramesCount];
3759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			this.rotations = new Quaternion[boneFramesCount];
3859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			this.scales = new Vector3f[boneFramesCount];
3959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
4059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			Arrays.fill(this.translations, 0, boneFramesCount, this.startTranslation);
4159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			Arrays.fill(this.rotations, 0, boneFramesCount, this.startRotation);
4259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			Arrays.fill(this.scales, 0, boneFramesCount, this.startScale);
4359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		}
4459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	}
4559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
4659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	/**
4759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.
4859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * @param bone
4959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 *        the bone this class will imitate
5059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * @param track
5159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 *        the bone's tracks
5259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 */
5359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	public CalculationBone(Bone bone, BoneTrack track) {
5459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this(bone, 0);
5559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this.track = track;
5659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this.translations = track.getTranslations();
5759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this.rotations = track.getRotations();
5859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this.scales = track.getScales();
5959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	}
6059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
6159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	public int getBoneFramesCount() {
6259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		return this.translations==null ? 0 : this.translations.length;
6359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	}
6459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
6559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	/**
6659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * This method returns the end point of the bone. If the bone has parent it is calculated from the start point
6759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered
6859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * to be 1 point up along Y axis (scale is applied if set to != 1.0);
6959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * @return the end point of this bone
7059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 */
7159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	//TODO: set to Z axis if user defined it this way
7259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	public Vector3f getEndPoint() {
7359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		if (this.getParent() == null) {
7459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			return new Vector3f(0, this.getLocalScale().y, 0);
7559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		} else {
7659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			Node parent = this.getParent();
7759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());
7859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		}
7959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	}
8059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
8159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	/**
8259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 * This method resets the calculation bone to the starting position.
8359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	 */
8459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	public void reset() {
8559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this.setLocalTranslation(startTranslation);
8659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this.setLocalRotation(startRotation);
8759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this.setLocalScale(startScale);
8859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	}
8959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
9059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	@Override
9159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	public int attachChild(Spatial child) {
9259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		if (this.getChildren() != null && this.getChildren().size() > 1) {
9359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");
9459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		}
9559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		return super.attachChild(child);
9659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	}
9759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
9859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	public Spatial rotate(Quaternion rot, int frame) {
9959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		Spatial spatial = super.rotate(rot);
10059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		this.updateWorldTransforms();
10159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		if (this.getChildren() != null && this.getChildren().size() > 0) {
10259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			CalculationBone child = (CalculationBone) this.getChild(0);
10359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			child.updateWorldTransforms();
10459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		}
10559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		rotations[frame].set(this.getLocalRotation());
10659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		translations[frame].set(this.getLocalTranslation());
10759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		if (scales != null) {
10859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			scales[frame].set(this.getLocalScale());
10959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		}
11059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		return spatial;
11159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	}
11259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
11359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	public void applyCalculatedTracks() {
11459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		if(track != null) {
11559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			track.setKeyframes(track.getTimes(), translations, rotations, scales);
11659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		} else {
11759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			bone.setUserControl(true);
11859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			bone.setUserTransforms(translations[0], rotations[0], scales[0]);
11959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			bone.setUserControl(false);
12059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta			bone.updateWorldVectors();
12159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		}
12259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	}
12359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
12459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	@Override
12559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	public String toString() {
12659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta		return bone.getName() + ": " + this.getLocalRotation() + " " + this.getLocalTranslation();
12759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	}
12859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta}