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