• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2010 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package com.jme3.scene.plugins.blender.animations;
33 
34 import java.util.ArrayList;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.logging.Level;
39 import java.util.logging.Logger;
40 
41 import com.jme3.animation.Bone;
42 import com.jme3.animation.BoneTrack;
43 import com.jme3.animation.Skeleton;
44 import com.jme3.math.Matrix4f;
45 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
46 import com.jme3.scene.plugins.blender.BlenderContext;
47 import com.jme3.scene.plugins.blender.curves.BezierCurve;
48 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
49 import com.jme3.scene.plugins.blender.file.Pointer;
50 import com.jme3.scene.plugins.blender.file.Structure;
51 
52 /**
53  * This class defines the methods to calculate certain aspects of animation and
54  * armature functionalities.
55  *
56  * @author Marcin Roguski (Kaelthas)
57  */
58 public class ArmatureHelper extends AbstractBlenderHelper {
59 	private static final Logger	LOGGER		= Logger.getLogger(ArmatureHelper.class.getName());
60 
61 	/** A map of bones and their old memory addresses. */
62 	private Map<Bone, Long>		bonesOMAs	= new HashMap<Bone, Long>();
63 
64 	/**
65 	 * This constructor parses the given blender version and stores the result.
66 	 * Some functionalities may differ in different blender versions.
67 	 *
68 	 * @param blenderVersion
69 	 *            the version read from the blend file
70 	 * @param fixUpAxis
71 	 *            a variable that indicates if the Y asxis is the UP axis or not
72 	 */
ArmatureHelper(String blenderVersion, boolean fixUpAxis)73 	public ArmatureHelper(String blenderVersion, boolean fixUpAxis) {
74 		super(blenderVersion, fixUpAxis);
75 	}
76 
77 	/**
78 	 * This method builds the object's bones structure.
79 	 *
80 	 * @param boneStructure
81 	 *            the structure containing the bones' data
82 	 * @param parent
83 	 *            the parent bone
84 	 * @param result
85 	 *            the list where the newly created bone will be added
86 	 * @param bonesPoseChannels
87 	 *            a map of bones poses channels
88 	 * @param blenderContext
89 	 *            the blender context
90 	 * @throws BlenderFileException
91 	 *             an exception is thrown when there is problem with the blender
92 	 *             file
93 	 */
buildBones(Structure boneStructure, Bone parent, List<Bone> result, Matrix4f arbt, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext)94 	public void buildBones(Structure boneStructure, Bone parent, List<Bone> result, Matrix4f arbt, final Map<Long, Structure> bonesPoseChannels, BlenderContext blenderContext) throws BlenderFileException {
95 		BoneContext bc = new BoneContext(boneStructure, arbt, bonesPoseChannels, blenderContext);
96 		bc.buildBone(result, bonesOMAs, blenderContext);
97 	}
98 
99 	/**
100 	 * This method returns the old memory address of a bone. If the bone does
101 	 * not exist in the blend file - zero is returned.
102 	 *
103 	 * @param bone
104 	 *            the bone whose old memory address we seek
105 	 * @return the old memory address of the given bone
106 	 */
getBoneOMA(Bone bone)107 	public Long getBoneOMA(Bone bone) {
108 		Long result = bonesOMAs.get(bone);
109 		if (result == null) {
110 			result = Long.valueOf(0);
111 		}
112 		return result;
113 	}
114 
115 	/**
116 	 * This method returns a map where the key is the object's group index that
117 	 * is used by a bone and the key is the bone index in the armature.
118 	 *
119 	 * @param defBaseStructure
120 	 *            a bPose structure of the object
121 	 * @return bone group-to-index map
122 	 * @throws BlenderFileException
123 	 *             this exception is thrown when the blender file is somehow
124 	 *             corrupted
125 	 */
getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton, BlenderContext blenderContext)126 	public Map<Integer, Integer> getGroupToBoneIndexMap(Structure defBaseStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
127 		Map<Integer, Integer> result = null;
128 		if (skeleton.getBoneCount() != 0) {
129 			result = new HashMap<Integer, Integer>();
130 			List<Structure> deformGroups = defBaseStructure.evaluateListBase(blenderContext);// bDeformGroup
131 			int groupIndex = 0;
132 			for (Structure deformGroup : deformGroups) {
133 				String deformGroupName = deformGroup.getFieldValue("name").toString();
134 				Integer boneIndex = this.getBoneIndex(skeleton, deformGroupName);
135 				if (boneIndex != null) {
136 					result.put(Integer.valueOf(groupIndex), boneIndex);
137 				}
138 				++groupIndex;
139 			}
140 		}
141 		return result;
142 	}
143 
144 	@Override
shouldBeLoaded(Structure structure, BlenderContext blenderContext)145 	public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
146 		return true;
147 	}
148 
149 	/**
150 	 * This method retuns the bone tracks for animation.
151 	 *
152 	 * @param actionStructure
153 	 *            the structure containing the tracks
154 	 * @param blenderContext
155 	 *            the blender context
156 	 * @return a list of tracks for the specified animation
157 	 * @throws BlenderFileException
158 	 *             an exception is thrown when there are problems with the blend
159 	 *             file
160 	 */
getTracks(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext)161 	public BoneTrack[] getTracks(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
162 		if (blenderVersion < 250) {
163 			return this.getTracks249(actionStructure, skeleton, blenderContext);
164 		} else {
165 			return this.getTracks250(actionStructure, skeleton, blenderContext);
166 		}
167 	}
168 
169 	/**
170 	 * This method retuns the bone tracks for animation for blender version 2.50
171 	 * and higher.
172 	 *
173 	 * @param actionStructure
174 	 *            the structure containing the tracks
175 	 * @param blenderContext
176 	 *            the blender context
177 	 * @return a list of tracks for the specified animation
178 	 * @throws BlenderFileException
179 	 *             an exception is thrown when there are problems with the blend
180 	 *             file
181 	 */
getTracks250(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext)182 	private BoneTrack[] getTracks250(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
183 		LOGGER.log(Level.INFO, "Getting tracks!");
184 		IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
185 		int fps = blenderContext.getBlenderKey().getFps();
186 		Structure groups = (Structure) actionStructure.getFieldValue("groups");
187 		List<Structure> actionGroups = groups.evaluateListBase(blenderContext);// bActionGroup
188 		List<BoneTrack> tracks = new ArrayList<BoneTrack>();
189 		for (Structure actionGroup : actionGroups) {
190 			String name = actionGroup.getFieldValue("name").toString();
191 			int boneIndex = this.getBoneIndex(skeleton, name);
192 			if (boneIndex >= 0) {
193 				List<Structure> channels = ((Structure) actionGroup.getFieldValue("channels")).evaluateListBase(blenderContext);
194 				BezierCurve[] bezierCurves = new BezierCurve[channels.size()];
195 				int channelCounter = 0;
196 				for (Structure c : channels) {
197 					int type = ipoHelper.getCurveType(c, blenderContext);
198 					Pointer pBezTriple = (Pointer) c.getFieldValue("bezt");
199 					List<Structure> bezTriples = pBezTriple.fetchData(blenderContext.getInputStream());
200 					bezierCurves[channelCounter++] = new BezierCurve(type, bezTriples, 2);
201 				}
202 
203 				Ipo ipo = new Ipo(bezierCurves, fixUpAxis);
204 				tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, 0, ipo.getLastFrame(), fps, false));
205 			}
206 		}
207 		return tracks.toArray(new BoneTrack[tracks.size()]);
208 	}
209 
210 	/**
211 	 * This method retuns the bone tracks for animation for blender version 2.49
212 	 * (and probably several lower versions too).
213 	 *
214 	 * @param actionStructure
215 	 *            the structure containing the tracks
216 	 * @param blenderContext
217 	 *            the blender context
218 	 * @return a list of tracks for the specified animation
219 	 * @throws BlenderFileException
220 	 *             an exception is thrown when there are problems with the blend
221 	 *             file
222 	 */
getTracks249(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext)223 	private BoneTrack[] getTracks249(Structure actionStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
224 		LOGGER.log(Level.INFO, "Getting tracks!");
225 		IpoHelper ipoHelper = blenderContext.getHelper(IpoHelper.class);
226 		int fps = blenderContext.getBlenderKey().getFps();
227 		Structure chanbase = (Structure) actionStructure.getFieldValue("chanbase");
228 		List<Structure> actionChannels = chanbase.evaluateListBase(blenderContext);// bActionChannel
229 		List<BoneTrack> tracks = new ArrayList<BoneTrack>();
230 		for (Structure bActionChannel : actionChannels) {
231 			String name = bActionChannel.getFieldValue("name").toString();
232 			int boneIndex = this.getBoneIndex(skeleton, name);
233 			if (boneIndex >= 0) {
234 				Pointer p = (Pointer) bActionChannel.getFieldValue("ipo");
235 				if (!p.isNull()) {
236 					Structure ipoStructure = p.fetchData(blenderContext.getInputStream()).get(0);
237 					Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);
238 					tracks.add((BoneTrack) ipo.calculateTrack(boneIndex, 0, ipo.getLastFrame(), fps, false));
239 				}
240 			}
241 		}
242 		return tracks.toArray(new BoneTrack[tracks.size()]);
243 	}
244 
245 	/**
246 	 * This method returns the index of the bone in the given skeleton.
247 	 *
248 	 * @param skeleton
249 	 *            the skeleton
250 	 * @param boneName
251 	 *            the name of the bone
252 	 * @return the index of the bone
253 	 */
getBoneIndex(Skeleton skeleton, String boneName)254 	private int getBoneIndex(Skeleton skeleton, String boneName) {
255 		int result = -1;
256 		for (int i = 0; i < skeleton.getBoneCount() && result == -1; ++i) {
257 			if (boneName.equals(skeleton.getBone(i).getName())) {
258 				result = i;
259 			}
260 		}
261 		return result;
262 	}
263 }
264