Home | History | Annotate | Download | only in animation
      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      */
     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      */
     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      */
    118     public Skeleton() {
    119     }
    120 
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    185     public Bone[] getRoots() {
    186         return rootBones;
    187     }
    188 
    189     /**
    190      * return a bone for the given index
    191      * @param index
    192      * @return
    193      */
    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      */
    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      */
    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      */
    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      */
    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      */
    259     public int getBoneCount() {
    260         return boneList.length;
    261     }
    262 
    263     @Override
    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 
    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 
    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