Home | History | Annotate | Download | only in animations
      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 	 */
     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 	 */
     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 	 */
    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")
    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 	 */
    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 	 */
    194 	public Transform getPoseTransform() {
    195 		return poseTransform;
    196 	}
    197 
    198 	/**
    199 	 * @return built bone (available after calling 'buildBone' method)
    200 	 */
    201 	public Bone getBone() {
    202 		return bone;
    203 	}
    204 }
    205