blob: 90994ccf1b4d4461802916cceefb632cdebfee83 [file] [log] [blame]
package com.jme3.scene.plugins.blender.animations;
import com.jme3.animation.Bone;
import com.jme3.animation.BoneTrack;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import java.util.Arrays;
/**
* The purpose of this class is to imitate bone's movement when calculating inverse kinematics.
* @author Marcin Roguski (Kaelthas)
*/
public class CalculationBone extends Node {
private Bone bone;
/** The bone's tracks. Will be altered at the end of calculation process. */
private BoneTrack track;
/** The starting position of the bone. */
private Vector3f startTranslation;
/** The starting rotation of the bone. */
private Quaternion startRotation;
/** The starting scale of the bone. */
private Vector3f startScale;
private Vector3f[] translations;
private Quaternion[] rotations;
private Vector3f[] scales;
public CalculationBone(Bone bone, int boneFramesCount) {
this.bone = bone;
this.startRotation = bone.getModelSpaceRotation().clone();
this.startTranslation = bone.getModelSpacePosition().clone();
this.startScale = bone.getModelSpaceScale().clone();
this.reset();
if(boneFramesCount > 0) {
this.translations = new Vector3f[boneFramesCount];
this.rotations = new Quaternion[boneFramesCount];
this.scales = new Vector3f[boneFramesCount];
Arrays.fill(this.translations, 0, boneFramesCount, this.startTranslation);
Arrays.fill(this.rotations, 0, boneFramesCount, this.startRotation);
Arrays.fill(this.scales, 0, boneFramesCount, this.startScale);
}
}
/**
* Constructor. Stores the track, starting transformation and sets the transformation to the starting positions.
* @param bone
* the bone this class will imitate
* @param track
* the bone's tracks
*/
public CalculationBone(Bone bone, BoneTrack track) {
this(bone, 0);
this.track = track;
this.translations = track.getTranslations();
this.rotations = track.getRotations();
this.scales = track.getScales();
}
public int getBoneFramesCount() {
return this.translations==null ? 0 : this.translations.length;
}
/**
* This method returns the end point of the bone. If the bone has parent it is calculated from the start point
* of parent to the start point of this bone. If the bone doesn't have a parent the end location is considered
* to be 1 point up along Y axis (scale is applied if set to != 1.0);
* @return the end point of this bone
*/
//TODO: set to Z axis if user defined it this way
public Vector3f getEndPoint() {
if (this.getParent() == null) {
return new Vector3f(0, this.getLocalScale().y, 0);
} else {
Node parent = this.getParent();
return parent.getWorldTranslation().subtract(this.getWorldTranslation()).multLocal(this.getWorldScale());
}
}
/**
* This method resets the calculation bone to the starting position.
*/
public void reset() {
this.setLocalTranslation(startTranslation);
this.setLocalRotation(startRotation);
this.setLocalScale(startScale);
}
@Override
public int attachChild(Spatial child) {
if (this.getChildren() != null && this.getChildren().size() > 1) {
throw new IllegalStateException(this.getClass().getName() + " class instance can only have one child!");
}
return super.attachChild(child);
}
public Spatial rotate(Quaternion rot, int frame) {
Spatial spatial = super.rotate(rot);
this.updateWorldTransforms();
if (this.getChildren() != null && this.getChildren().size() > 0) {
CalculationBone child = (CalculationBone) this.getChild(0);
child.updateWorldTransforms();
}
rotations[frame].set(this.getLocalRotation());
translations[frame].set(this.getLocalTranslation());
if (scales != null) {
scales[frame].set(this.getLocalScale());
}
return spatial;
}
public void applyCalculatedTracks() {
if(track != null) {
track.setKeyframes(track.getTimes(), translations, rotations, scales);
} else {
bone.setUserControl(true);
bone.setUserTransforms(translations[0], rotations[0], scales[0]);
bone.setUserControl(false);
bone.updateWorldVectors();
}
}
@Override
public String toString() {
return bone.getName() + ": " + this.getLocalRotation() + " " + this.getLocalTranslation();
}
}