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 }