1 package com.jme3.scene.plugins.blender.animations; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 import java.util.Map; 6 7 import com.jme3.animation.Bone; 8 import com.jme3.math.Matrix4f; 9 import com.jme3.math.Quaternion; 10 import com.jme3.math.Transform; 11 import com.jme3.math.Vector3f; 12 import com.jme3.scene.plugins.blender.BlenderContext; 13 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; 14 import com.jme3.scene.plugins.blender.file.DynamicArray; 15 import com.jme3.scene.plugins.blender.file.Structure; 16 import com.jme3.scene.plugins.blender.objects.ObjectHelper; 17 18 /** 19 * This class holds the basic data that describes a bone. 20 * 21 * @author Marcin Roguski (Kaelthas) 22 */ 23 public class BoneContext { 24 /** The structure of the bone. */ 25 private Structure boneStructure; 26 /** Bone's pose channel structure. */ 27 private Structure poseChannel; 28 /** Bone's name. */ 29 private String boneName; 30 /** This variable indicates if the Y axis should be the UP axis. */ 31 private boolean fixUpAxis; 32 /** The bone's armature matrix. */ 33 private Matrix4f armatureMatrix; 34 /** The parent context. */ 35 private BoneContext parent; 36 /** The children of this context. */ 37 private List<BoneContext> children = new ArrayList<BoneContext>(); 38 /** Created bone (available after calling 'buildBone' method). */ 39 private Bone bone; 40 /** Bone's pose transform (available after calling 'buildBone' method). */ 41 private Transform poseTransform = new Transform(); 42 /** The bone's rest matrix. */ 43 private Matrix4f restMatrix; 44 /** Bone's total inverse transformation. */ 45 private Matrix4f inverseTotalTransformation; 46 /** Bone's parent inverse matrix. */ 47 private Matrix4f inverseParentMatrix; 48 49 /** 50 * Constructor. Creates the basic set of bone's data. 51 * 52 * @param boneStructure 53 * the bone's structure 54 * @param objectToArmatureMatrix 55 * object-to-armature transformation matrix 56 * @param bonesPoseChannels 57 * a map of pose channels for each bone OMA 58 * @param blenderContext 59 * the blender context 60 * @throws BlenderFileException 61 * an exception is thrown when problem with blender data reading 62 * occurs 63 */ BoneContext(Structure boneStructure, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext)64 public BoneContext(Structure boneStructure, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException { 65 this(boneStructure, null, objectToArmatureMatrix, bonesPoseChannels, blenderContext); 66 } 67 68 /** 69 * Constructor. Creates the basic set of bone's data. 70 * 71 * @param boneStructure 72 * the bone's structure 73 * @param parent 74 * bone's parent (null if the bone is the root bone) 75 * @param objectToArmatureMatrix 76 * object-to-armature transformation matrix 77 * @param bonesPoseChannels 78 * a map of pose channels for each bone OMA 79 * @param blenderContext 80 * the blender context 81 * @throws BlenderFileException 82 * an exception is thrown when problem with blender data reading 83 * occurs 84 */ BoneContext(Structure boneStructure, BoneContext parent, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext)85 private BoneContext(Structure boneStructure, BoneContext parent, Matrix4f objectToArmatureMatrix, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException { 86 this.parent = parent; 87 this.boneStructure = boneStructure; 88 boneName = boneStructure.getFieldValue("name").toString(); 89 ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); 90 armatureMatrix = objectHelper.getMatrix(boneStructure, "arm_mat", true); 91 92 fixUpAxis = blenderContext.getBlenderKey().isFixUpAxis(); 93 this.computeRestMatrix(objectToArmatureMatrix); 94 List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase(blenderContext); 95 for (Structure child : childbase) { 96 this.children.add(new BoneContext(child, this, objectToArmatureMatrix, bonesPoseChannels, blenderContext)); 97 } 98 99 poseChannel = bonesPoseChannels.get(boneStructure.getOldMemoryAddress()); 100 101 blenderContext.setBoneContext(boneStructure.getOldMemoryAddress(), this); 102 } 103 104 /** 105 * This method computes the rest matrix for the bone. 106 * 107 * @param objectToArmatureMatrix 108 * object-to-armature transformation matrix 109 */ computeRestMatrix(Matrix4f objectToArmatureMatrix)110 private void computeRestMatrix(Matrix4f objectToArmatureMatrix) { 111 if (parent != null) { 112 inverseParentMatrix = parent.inverseTotalTransformation.clone(); 113 } else if (fixUpAxis) { 114 inverseParentMatrix = objectToArmatureMatrix.clone(); 115 } else { 116 inverseParentMatrix = Matrix4f.IDENTITY.clone(); 117 } 118 119 restMatrix = armatureMatrix.clone(); 120 inverseTotalTransformation = restMatrix.invert(); 121 122 restMatrix = inverseParentMatrix.mult(restMatrix); 123 124 for (BoneContext child : this.children) { 125 child.computeRestMatrix(objectToArmatureMatrix); 126 } 127 } 128 129 /** 130 * This method computes the pose transform for the bone. 131 */ 132 @SuppressWarnings("unchecked") computePoseTransform()133 private void computePoseTransform() { 134 DynamicArray<Number> loc = (DynamicArray<Number>) poseChannel.getFieldValue("loc"); 135 DynamicArray<Number> size = (DynamicArray<Number>) poseChannel.getFieldValue("size"); 136 DynamicArray<Number> quat = (DynamicArray<Number>) poseChannel.getFieldValue("quat"); 137 if (fixUpAxis) { 138 poseTransform.setTranslation(loc.get(0).floatValue(), -loc.get(2).floatValue(), loc.get(1).floatValue()); 139 poseTransform.setRotation(new Quaternion(quat.get(1).floatValue(), quat.get(3).floatValue(), -quat.get(2).floatValue(), quat.get(0).floatValue())); 140 poseTransform.setScale(size.get(0).floatValue(), size.get(2).floatValue(), size.get(1).floatValue()); 141 } else { 142 poseTransform.setTranslation(loc.get(0).floatValue(), loc.get(1).floatValue(), loc.get(2).floatValue()); 143 poseTransform.setRotation(new Quaternion(quat.get(0).floatValue(), quat.get(1).floatValue(), quat.get(2).floatValue(), quat.get(3).floatValue())); 144 poseTransform.setScale(size.get(0).floatValue(), size.get(1).floatValue(), size.get(2).floatValue()); 145 } 146 147 Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation()); 148 localTransform.setScale(bone.getLocalScale()); 149 localTransform.getTranslation().addLocal(poseTransform.getTranslation()); 150 localTransform.getRotation().multLocal(poseTransform.getRotation()); 151 localTransform.getScale().multLocal(poseTransform.getScale()); 152 153 poseTransform.set(localTransform); 154 } 155 156 /** 157 * This method builds the bone. It recursively builds the bone's children. 158 * 159 * @param bones 160 * a list of bones where the newly created bone will be added 161 * @param boneOMAs 162 * the map between bone and its old memory address 163 * @param blenderContext 164 * the blender context 165 * @return newly created bone 166 */ buildBone(List<Bone> bones, Map<Bone, Long> boneOMAs, BlenderContext blenderContext)167 public Bone buildBone(List<Bone> bones, Map<Bone, Long> boneOMAs, BlenderContext blenderContext) { 168 Long boneOMA = boneStructure.getOldMemoryAddress(); 169 bone = new Bone(boneName); 170 bones.add(bone); 171 boneOMAs.put(bone, boneOMA); 172 blenderContext.addLoadedFeatures(boneOMA, boneName, boneStructure, bone); 173 174 Matrix4f pose = this.restMatrix.clone(); 175 ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); 176 177 Vector3f poseLocation = pose.toTranslationVector(); 178 Quaternion rotation = pose.toRotationQuat(); 179 Vector3f scale = objectHelper.getScale(pose); 180 181 bone.setBindTransforms(poseLocation, rotation, scale); 182 for (BoneContext child : children) { 183 bone.addChild(child.buildBone(bones, boneOMAs, blenderContext)); 184 } 185 186 this.computePoseTransform(); 187 188 return bone; 189 } 190 191 /** 192 * @return bone's pose transformation 193 */ getPoseTransform()194 public Transform getPoseTransform() { 195 return poseTransform; 196 } 197 198 /** 199 * @return built bone (available after calling 'buildBone' method) 200 */ getBone()201 public Bone getBone() { 202 return bone; 203 } 204 } 205