Home | History | Annotate | Download | only in animations
      1 package com.jme3.scene.plugins.blender.animations;
      2 
      3 import com.jme3.animation.Bone;
      4 import com.jme3.animation.BoneTrack;
      5 import com.jme3.math.Quaternion;
      6 import com.jme3.math.Vector3f;
      7 import com.jme3.scene.Node;
      8 import com.jme3.scene.Spatial;
      9 import java.util.Arrays;
     10 
     11 /**
     12  * The purpose of this class is to imitate bone's movement when calculating inverse kinematics.
     13  * @author Marcin Roguski (Kaelthas)
     14  */
     15 public class CalculationBone extends Node {
     16 	private Bone bone;
     17 	/** The bone's tracks. Will be altered at the end of calculation process. */
     18 	private BoneTrack track;
     19 	/** The starting position of the bone. */
     20 	private Vector3f startTranslation;
     21 	/** The starting rotation of the bone. */
     22 	private Quaternion startRotation;
     23 	/** The starting scale of the bone. */
     24 	private Vector3f startScale;
     25 	private Vector3f[] translations;
     26 	private Quaternion[] rotations;
     27 	private Vector3f[] scales;
     28 
     29 	public CalculationBone(Bone bone, int boneFramesCount) {
     30 		this.bone = bone;
     31 		this.startRotation = bone.getModelSpaceRotation().clone();
     32 		this.startTranslation = bone.getModelSpacePosition().clone();
     33 		this.startScale = bone.getModelSpaceScale().clone();
     34 		this.reset();
     35 		if(boneFramesCount > 0) {
     36 			this.translations = new Vector3f[boneFramesCount];
     37 			this.rotations = new Quaternion[boneFramesCount];
     38 			this.scales = new Vector3f[boneFramesCount];
     39 
     40 			Arrays.fill(this.translations, 0, boneFramesCount, this.startTranslation);
     41 			Arrays.fill(this.rotations, 0, boneFramesCount, this.startRotation);
     42 			Arrays.fill(this.scales, 0, boneFramesCount, this.startScale);
     43 		}
     44 	}
     45 
     46 	/**
     47 	 * Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.
     48 	 * @param bone
     49 	 *        the bone this class will imitate
     50 	 * @param track
     51 	 *        the bone's tracks
     52 	 */
     53 	public CalculationBone(Bone bone, BoneTrack track) {
     54 		this(bone, 0);
     55 		this.track = track;
     56 		this.translations = track.getTranslations();
     57 		this.rotations = track.getRotations();
     58 		this.scales = track.getScales();
     59 	}
     60 
     61 	public int getBoneFramesCount() {
     62 		return this.translations==null ? 0 : this.translations.length;
     63 	}
     64 
     65 	/**
     66 	 * This method returns the end point of the bone. If the bone has parent it is calculated from the start point
     67 	 * of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered
     68 	 * to be 1 point up along Y axis (scale is applied if set to != 1.0);
     69 	 * @return the end point of this bone
     70 	 */
     71 	//TODO: set to Z axis if user defined it this way
     72 	public Vector3f getEndPoint() {
     73 		if (this.getParent() == null) {
     74 			return new Vector3f(0, this.getLocalScale().y, 0);
     75 		} else {
     76 			Node parent = this.getParent();
     77 			return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());
     78 		}
     79 	}
     80 
     81 	/**
     82 	 * This method resets the calculation bone to the starting position.
     83 	 */
     84 	public void reset() {
     85 		this.setLocalTranslation(startTranslation);
     86 		this.setLocalRotation(startRotation);
     87 		this.setLocalScale(startScale);
     88 	}
     89 
     90 	@Override
     91 	public int attachChild(Spatial child) {
     92 		if (this.getChildren() != null && this.getChildren().size() > 1) {
     93 			throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");
     94 		}
     95 		return super.attachChild(child);
     96 	}
     97 
     98 	public Spatial rotate(Quaternion rot, int frame) {
     99 		Spatial spatial = super.rotate(rot);
    100 		this.updateWorldTransforms();
    101 		if (this.getChildren() != null && this.getChildren().size() > 0) {
    102 			CalculationBone child = (CalculationBone) this.getChild(0);
    103 			child.updateWorldTransforms();
    104 		}
    105 		rotations[frame].set(this.getLocalRotation());
    106 		translations[frame].set(this.getLocalTranslation());
    107 		if (scales != null) {
    108 			scales[frame].set(this.getLocalScale());
    109 		}
    110 		return spatial;
    111 	}
    112 
    113 	public void applyCalculatedTracks() {
    114 		if(track != null) {
    115 			track.setKeyframes(track.getTimes(), translations, rotations, scales);
    116 		} else {
    117 			bone.setUserControl(true);
    118 			bone.setUserTransforms(translations[0], rotations[0], scales[0]);
    119 			bone.setUserControl(false);
    120 			bone.updateWorldVectors();
    121 		}
    122 	}
    123 
    124 	@Override
    125 	public String toString() {
    126 		return bone.getName() + ": " + this.getLocalRotation() + " " + this.getLocalTranslation();
    127 	}
    128 }