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.animation; 33 34 import com.jme3.export.*; 35 import com.jme3.math.Matrix4f; 36 import com.jme3.util.TempVars; 37 import java.io.IOException; 38 import java.util.ArrayList; 39 import java.util.List; 40 41 /** 42 * <code>Skeleton</code> is a convenience class for managing a bone hierarchy. 43 * Skeleton updates the world transforms to reflect the current local 44 * animated matrixes. 45 * 46 * @author Kirill Vainer 47 */ 48 public final class Skeleton implements Savable { 49 50 private Bone[] rootBones; 51 private Bone[] boneList; 52 53 /** 54 * Contains the skinning matrices, multiplying it by a vertex effected by a bone 55 * will cause it to go to the animated position. 56 */ 57 private transient Matrix4f[] skinningMatrixes; 58 59 /** 60 * Creates a skeleton from a bone list. 61 * The root bones are found automatically. 62 * <p> 63 * Note that using this constructor will cause the bones in the list 64 * to have their bind pose recomputed based on their local transforms. 65 * 66 * @param boneList The list of bones to manage by this Skeleton 67 */ Skeleton(Bone[] boneList)68 public Skeleton(Bone[] boneList) { 69 this.boneList = boneList; 70 71 List<Bone> rootBoneList = new ArrayList<Bone>(); 72 for (int i = boneList.length - 1; i >= 0; i--) { 73 Bone b = boneList[i]; 74 if (b.getParent() == null) { 75 rootBoneList.add(b); 76 } 77 } 78 rootBones = rootBoneList.toArray(new Bone[rootBoneList.size()]); 79 80 createSkinningMatrices(); 81 82 for (int i = rootBones.length - 1; i >= 0; i--) { 83 Bone rootBone = rootBones[i]; 84 rootBone.update(); 85 rootBone.setBindingPose(); 86 } 87 } 88 89 /** 90 * Special-purpose copy constructor. 91 * <p> 92 * Shallow copies bind pose data from the source skeleton, does not 93 * copy any other data. 94 * 95 * @param source The source Skeleton to copy from 96 */ Skeleton(Skeleton source)97 public Skeleton(Skeleton source) { 98 Bone[] sourceList = source.boneList; 99 boneList = new Bone[sourceList.length]; 100 for (int i = 0; i < sourceList.length; i++) { 101 boneList[i] = new Bone(sourceList[i]); 102 } 103 104 rootBones = new Bone[source.rootBones.length]; 105 for (int i = 0; i < rootBones.length; i++) { 106 rootBones[i] = recreateBoneStructure(source.rootBones[i]); 107 } 108 createSkinningMatrices(); 109 110 for (int i = rootBones.length - 1; i >= 0; i--) { 111 rootBones[i].update(); 112 } 113 } 114 115 /** 116 * Serialization only. Do not use. 117 */ Skeleton()118 public Skeleton() { 119 } 120 createSkinningMatrices()121 private void createSkinningMatrices() { 122 skinningMatrixes = new Matrix4f[boneList.length]; 123 for (int i = 0; i < skinningMatrixes.length; i++) { 124 skinningMatrixes[i] = new Matrix4f(); 125 } 126 } 127 recreateBoneStructure(Bone sourceRoot)128 private Bone recreateBoneStructure(Bone sourceRoot) { 129 Bone targetRoot = getBone(sourceRoot.getName()); 130 List<Bone> children = sourceRoot.getChildren(); 131 for (int i = 0; i < children.size(); i++) { 132 Bone sourceChild = children.get(i); 133 // find my version of the child 134 Bone targetChild = getBone(sourceChild.getName()); 135 targetRoot.addChild(targetChild); 136 recreateBoneStructure(sourceChild); 137 } 138 139 return targetRoot; 140 } 141 142 /** 143 * Updates world transforms for all bones in this skeleton. 144 * Typically called after setting local animation transforms. 145 */ updateWorldVectors()146 public void updateWorldVectors() { 147 for (int i = rootBones.length - 1; i >= 0; i--) { 148 rootBones[i].update(); 149 } 150 } 151 152 /** 153 * Saves the current skeleton state as it's binding pose. 154 */ setBindingPose()155 public void setBindingPose() { 156 for (int i = rootBones.length - 1; i >= 0; i--) { 157 rootBones[i].setBindingPose(); 158 } 159 } 160 161 /** 162 * Reset the skeleton to bind pose. 163 */ reset()164 public final void reset() { 165 for (int i = rootBones.length - 1; i >= 0; i--) { 166 rootBones[i].reset(); 167 } 168 } 169 170 /** 171 * Reset the skeleton to bind pose and updates the bones 172 */ resetAndUpdate()173 public final void resetAndUpdate() { 174 for (int i = rootBones.length - 1; i >= 0; i--) { 175 Bone rootBone = rootBones[i]; 176 rootBone.reset(); 177 rootBone.update(); 178 } 179 } 180 181 /** 182 * returns the array of all root bones of this skeleton 183 * @return 184 */ getRoots()185 public Bone[] getRoots() { 186 return rootBones; 187 } 188 189 /** 190 * return a bone for the given index 191 * @param index 192 * @return 193 */ getBone(int index)194 public Bone getBone(int index) { 195 return boneList[index]; 196 } 197 198 /** 199 * returns the bone with the given name 200 * @param name 201 * @return 202 */ getBone(String name)203 public Bone getBone(String name) { 204 for (int i = 0; i < boneList.length; i++) { 205 if (boneList[i].getName().equals(name)) { 206 return boneList[i]; 207 } 208 } 209 return null; 210 } 211 212 /** 213 * returns the bone index of the given bone 214 * @param bone 215 * @return 216 */ getBoneIndex(Bone bone)217 public int getBoneIndex(Bone bone) { 218 for (int i = 0; i < boneList.length; i++) { 219 if (boneList[i] == bone) { 220 return i; 221 } 222 } 223 224 return -1; 225 } 226 227 /** 228 * returns the bone index of the bone that has the given name 229 * @param name 230 * @return 231 */ getBoneIndex(String name)232 public int getBoneIndex(String name) { 233 for (int i = 0; i < boneList.length; i++) { 234 if (boneList[i].getName().equals(name)) { 235 return i; 236 } 237 } 238 239 return -1; 240 } 241 242 /** 243 * Compute the skining matrices for each bone of the skeleton that would be used to transform vertices of associated meshes 244 * @return 245 */ computeSkinningMatrices()246 public Matrix4f[] computeSkinningMatrices() { 247 TempVars vars = TempVars.get(); 248 for (int i = 0; i < boneList.length; i++) { 249 boneList[i].getOffsetTransform(skinningMatrixes[i], vars.quat1, vars.vect1, vars.vect2, vars.tempMat3); 250 } 251 vars.release(); 252 return skinningMatrixes; 253 } 254 255 /** 256 * returns the number of bones of this skeleton 257 * @return 258 */ getBoneCount()259 public int getBoneCount() { 260 return boneList.length; 261 } 262 263 @Override toString()264 public String toString() { 265 StringBuilder sb = new StringBuilder(); 266 sb.append("Skeleton - ").append(boneList.length).append(" bones, ").append(rootBones.length).append(" roots\n"); 267 for (Bone rootBone : rootBones) { 268 sb.append(rootBone.toString()); 269 } 270 return sb.toString(); 271 } 272 read(JmeImporter im)273 public void read(JmeImporter im) throws IOException { 274 InputCapsule input = im.getCapsule(this); 275 276 Savable[] boneRootsAsSav = input.readSavableArray("rootBones", null); 277 rootBones = new Bone[boneRootsAsSav.length]; 278 System.arraycopy(boneRootsAsSav, 0, rootBones, 0, boneRootsAsSav.length); 279 280 Savable[] boneListAsSavable = input.readSavableArray("boneList", null); 281 boneList = new Bone[boneListAsSavable.length]; 282 System.arraycopy(boneListAsSavable, 0, boneList, 0, boneListAsSavable.length); 283 284 createSkinningMatrices(); 285 286 for (Bone rootBone : rootBones) { 287 rootBone.update(); 288 rootBone.setBindingPose(); 289 } 290 } 291 write(JmeExporter ex)292 public void write(JmeExporter ex) throws IOException { 293 OutputCapsule output = ex.getCapsule(this); 294 output.write(rootBones, "rootBones", null); 295 output.write(boneList, "boneList", null); 296 } 297 } 298