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.*;
     36 import com.jme3.scene.Node;
     37 import com.jme3.util.TempVars;
     38 import java.io.IOException;
     39 import java.util.ArrayList;
     40 
     41 /**
     42  * <code>Bone</code> describes a bone in the bone-weight skeletal animation
     43  * system. A bone contains a name and an index, as well as relevant
     44  * transformation data.
     45  *
     46  * @author Kirill Vainer
     47  */
     48 public final class Bone implements Savable {
     49 
     50     private String name;
     51     private Bone parent;
     52     private final ArrayList<Bone> children = new ArrayList<Bone>();
     53     /**
     54      * If enabled, user can control bone transform with setUserTransforms.
     55      * Animation transforms are not applied to this bone when enabled.
     56      */
     57     private boolean userControl = false;
     58     /**
     59      * The attachment node.
     60      */
     61     private Node attachNode;
     62     /**
     63      * Initial transform is the local bind transform of this bone.
     64      * PARENT SPACE -> BONE SPACE
     65      */
     66     private Vector3f initialPos;
     67     private Quaternion initialRot;
     68     private Vector3f initialScale;
     69     /**
     70      * The inverse world bind transform.
     71      * BONE SPACE -> MODEL SPACE
     72      */
     73     private Vector3f worldBindInversePos;
     74     private Quaternion worldBindInverseRot;
     75     private Vector3f worldBindInverseScale;
     76     /**
     77      * The local animated transform combined with the local bind transform and parent world transform
     78      */
     79     private Vector3f localPos = new Vector3f();
     80     private Quaternion localRot = new Quaternion();
     81     private Vector3f localScale = new Vector3f(1.0f, 1.0f, 1.0f);
     82     /**
     83      * MODEL SPACE -> BONE SPACE (in animated state)
     84      */
     85     private Vector3f worldPos = new Vector3f();
     86     private Quaternion worldRot = new Quaternion();
     87     private Vector3f worldScale = new Vector3f();
     88     //used for getCombinedTransform
     89     private Transform tmpTransform = new Transform();
     90 
     91     /**
     92      * Creates a new bone with the given name.
     93      *
     94      * @param name Name to give to this bone
     95      */
     96     public Bone(String name) {
     97         if (name == null)
     98             throw new IllegalArgumentException("Name cannot be null");
     99 
    100         this.name = name;
    101 
    102         initialPos = new Vector3f();
    103         initialRot = new Quaternion();
    104         initialScale = new Vector3f(1, 1, 1);
    105 
    106         worldBindInversePos = new Vector3f();
    107         worldBindInverseRot = new Quaternion();
    108         worldBindInverseScale = new Vector3f();
    109     }
    110 
    111     /**
    112      * Special-purpose copy constructor.
    113      * <p>
    114      * Only copies the name and bind pose from the original.
    115      * <p>
    116      * WARNING: Local bind pose and world inverse bind pose transforms shallow
    117      * copied. Modifying that data on the original bone will cause it to
    118      * be recomputed on any cloned bones.
    119      * <p>
    120      * The rest of the data is <em>NOT</em> copied, as it will be
    121      * generated automatically when the bone is animated.
    122      *
    123      * @param source The bone from which to copy the data.
    124      */
    125     Bone(Bone source) {
    126         this.name = source.name;
    127 
    128         userControl = source.userControl;
    129 
    130         initialPos = source.initialPos;
    131         initialRot = source.initialRot;
    132         initialScale = source.initialScale;
    133 
    134         worldBindInversePos = source.worldBindInversePos;
    135         worldBindInverseRot = source.worldBindInverseRot;
    136         worldBindInverseScale = source.worldBindInverseScale;
    137 
    138         // parent and children will be assigned manually..
    139     }
    140 
    141     /**
    142      * Serialization only. Do not use.
    143      */
    144     public Bone() {
    145     }
    146 
    147     /**
    148      * Returns the name of the bone, set in the constructor.
    149      *
    150      * @return The name of the bone, set in the constructor.
    151      */
    152     public String getName() {
    153         return name;
    154     }
    155 
    156     /**
    157      * Returns parent bone of this bone, or null if it is a root bone.
    158      * @return The parent bone of this bone, or null if it is a root bone.
    159      */
    160     public Bone getParent() {
    161         return parent;
    162     }
    163 
    164     /**
    165      * Returns all the children bones of this bone.
    166      *
    167      * @return All the children bones of this bone.
    168      */
    169     public ArrayList<Bone> getChildren() {
    170         return children;
    171     }
    172 
    173     /**
    174      * Returns the local position of the bone, relative to the parent bone.
    175      *
    176      * @return The local position of the bone, relative to the parent bone.
    177      */
    178     public Vector3f getLocalPosition() {
    179         return localPos;
    180     }
    181 
    182     /**
    183      * Returns the local rotation of the bone, relative to the parent bone.
    184      *
    185      * @return The local rotation of the bone, relative to the parent bone.
    186      */
    187     public Quaternion getLocalRotation() {
    188         return localRot;
    189     }
    190 
    191     /**
    192      * Returns the local scale of the bone, relative to the parent bone.
    193      *
    194      * @return The local scale of the bone, relative to the parent bone.
    195      */
    196     public Vector3f getLocalScale() {
    197         return localScale;
    198     }
    199 
    200     /**
    201      * Returns the position of the bone in model space.
    202      *
    203      * @return The position of the bone in model space.
    204      */
    205     public Vector3f getModelSpacePosition() {
    206         return worldPos;
    207     }
    208 
    209     /**
    210      * Returns the rotation of the bone in model space.
    211      *
    212      * @return The rotation of the bone in model space.
    213      */
    214     public Quaternion getModelSpaceRotation() {
    215         return worldRot;
    216     }
    217 
    218     /**
    219      * Returns the scale of the bone in model space.
    220      *
    221      * @return The scale of the bone in model space.
    222      */
    223     public Vector3f getModelSpaceScale() {
    224         return worldScale;
    225     }
    226 
    227     /**
    228      * Returns the inverse world bind pose position.
    229      * <p>
    230      * The bind pose transform of the bone is its "default"
    231      * transform with no animation applied.
    232      *
    233      * @return the inverse world bind pose position.
    234      */
    235     public Vector3f getWorldBindInversePosition() {
    236         return worldBindInversePos;
    237     }
    238 
    239     /**
    240      * Returns the inverse world bind pose rotation.
    241      * <p>
    242      * The bind pose transform of the bone is its "default"
    243      * transform with no animation applied.
    244      *
    245      * @return the inverse world bind pose rotation.
    246      */
    247     public Quaternion getWorldBindInverseRotation() {
    248         return worldBindInverseRot;
    249     }
    250 
    251     /**
    252      * Returns the inverse world bind pose scale.
    253      * <p>
    254      * The bind pose transform of the bone is its "default"
    255      * transform with no animation applied.
    256      *
    257      * @return the inverse world bind pose scale.
    258      */
    259     public Vector3f getWorldBindInverseScale() {
    260         return worldBindInverseScale;
    261     }
    262 
    263     /**
    264      * Returns the world bind pose position.
    265      * <p>
    266      * The bind pose transform of the bone is its "default"
    267      * transform with no animation applied.
    268      *
    269      * @return the world bind pose position.
    270      */
    271     public Vector3f getWorldBindPosition() {
    272         return initialPos;
    273     }
    274 
    275     /**
    276      * Returns the world bind pose rotation.
    277      * <p>
    278      * The bind pose transform of the bone is its "default"
    279      * transform with no animation applied.
    280      *
    281      * @return the world bind pose rotation.
    282      */
    283     public Quaternion getWorldBindRotation() {
    284         return initialRot;
    285     }
    286 
    287     /**
    288      * Returns the world bind pose scale.
    289      * <p>
    290      * The bind pose transform of the bone is its "default"
    291      * transform with no animation applied.
    292      *
    293      * @return the world bind pose scale.
    294      */
    295     public Vector3f getWorldBindScale() {
    296         return initialScale;
    297     }
    298 
    299     /**
    300      * If enabled, user can control bone transform with setUserTransforms.
    301      * Animation transforms are not applied to this bone when enabled.
    302      */
    303     public void setUserControl(boolean enable) {
    304         userControl = enable;
    305     }
    306 
    307     /**
    308      * Add a new child to this bone. Shouldn't be used by user code.
    309      * Can corrupt skeleton.
    310      *
    311      * @param bone The bone to add
    312      */
    313     public void addChild(Bone bone) {
    314         children.add(bone);
    315         bone.parent = this;
    316     }
    317 
    318     /**
    319      * Updates the world transforms for this bone, and, possibly the attach node
    320      * if not null.
    321      * <p>
    322      * The world transform of this bone is computed by combining the parent's
    323      * world transform with this bones' local transform.
    324      */
    325     public final void updateWorldVectors() {
    326         if (parent != null) {
    327             //rotation
    328             parent.worldRot.mult(localRot, worldRot);
    329 
    330             //scale
    331             //For scale parent scale is not taken into account!
    332             // worldScale.set(localScale);
    333             parent.worldScale.mult(localScale, worldScale);
    334 
    335             //translation
    336             //scale and rotation of parent affect bone position
    337             parent.worldRot.mult(localPos, worldPos);
    338             worldPos.multLocal(parent.worldScale);
    339             worldPos.addLocal(parent.worldPos);
    340         } else {
    341             worldRot.set(localRot);
    342             worldPos.set(localPos);
    343             worldScale.set(localScale);
    344         }
    345 
    346         if (attachNode != null) {
    347             attachNode.setLocalTranslation(worldPos);
    348             attachNode.setLocalRotation(worldRot);
    349             attachNode.setLocalScale(worldScale);
    350         }
    351     }
    352 
    353     /**
    354      * Updates world transforms for this bone and it's children.
    355      */
    356     final void update() {
    357         this.updateWorldVectors();
    358 
    359         for (int i = children.size() - 1; i >= 0; i--) {
    360             children.get(i).update();
    361         }
    362     }
    363 
    364     /**
    365      * Saves the current bone state as its binding pose, including its children.
    366      */
    367     void setBindingPose() {
    368         initialPos.set(localPos);
    369         initialRot.set(localRot);
    370         initialScale.set(localScale);
    371 
    372         if (worldBindInversePos == null) {
    373             worldBindInversePos = new Vector3f();
    374             worldBindInverseRot = new Quaternion();
    375             worldBindInverseScale = new Vector3f();
    376         }
    377 
    378         // Save inverse derived position/scale/orientation, used for calculate offset transform later
    379         worldBindInversePos.set(worldPos);
    380         worldBindInversePos.negateLocal();
    381 
    382         worldBindInverseRot.set(worldRot);
    383         worldBindInverseRot.inverseLocal();
    384 
    385         worldBindInverseScale.set(Vector3f.UNIT_XYZ);
    386         worldBindInverseScale.divideLocal(worldScale);
    387 
    388         for (Bone b : children) {
    389             b.setBindingPose();
    390         }
    391     }
    392 
    393     /**
    394      * Reset the bone and it's children to bind pose.
    395      */
    396     final void reset() {
    397         if (!userControl) {
    398             localPos.set(initialPos);
    399             localRot.set(initialRot);
    400             localScale.set(initialScale);
    401         }
    402 
    403         for (int i = children.size() - 1; i >= 0; i--) {
    404             children.get(i).reset();
    405         }
    406     }
    407 
    408      /**
    409      * Stores the skinning transform in the specified Matrix4f.
    410      * The skinning transform applies the animation of the bone to a vertex.
    411      *
    412      * This assumes that the world transforms for the entire bone hierarchy
    413      * have already been computed, otherwise this method will return undefined
    414      * results.
    415      *
    416      * @param outTransform
    417      */
    418     void getOffsetTransform(Matrix4f outTransform, Quaternion tmp1, Vector3f tmp2, Vector3f tmp3, Matrix3f tmp4) {
    419         // Computing scale
    420         Vector3f scale = worldScale.mult(worldBindInverseScale, tmp3);
    421 
    422         // Computing rotation
    423         Quaternion rotate = worldRot.mult(worldBindInverseRot, tmp1);
    424 
    425         // Computing translation
    426         // Translation depend on rotation and scale
    427         Vector3f translate = worldPos.add(rotate.mult(scale.mult(worldBindInversePos, tmp2), tmp2), tmp2);
    428 
    429         // Populating the matrix
    430         outTransform.loadIdentity();
    431         outTransform.setTransform(translate, scale, rotate.toRotationMatrix(tmp4));
    432     }
    433 
    434     /**
    435      * Sets user transform.
    436      */
    437     public void setUserTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) {
    438         if (!userControl) {
    439             throw new IllegalStateException("User control must be on bone to allow user transforms");
    440         }
    441 
    442         localPos.set(initialPos);
    443         localRot.set(initialRot);
    444         localScale.set(initialScale);
    445 
    446         localPos.addLocal(translation);
    447         localRot = localRot.mult(rotation);
    448         localScale.multLocal(scale);
    449     }
    450 
    451     /**
    452      * Must update all bones in skeleton for this to work.
    453      * @param translation
    454      * @param rotation
    455      */
    456     public void setUserTransformsWorld(Vector3f translation, Quaternion rotation) {
    457         if (!userControl) {
    458             throw new IllegalStateException("User control must be on bone to allow user transforms");
    459         }
    460 
    461         // TODO: add scale here ???
    462         worldPos.set(translation);
    463         worldRot.set(rotation);
    464 
    465         //if there is an attached Node we need to set it's local transforms too.
    466         if(attachNode != null){
    467             attachNode.setLocalTranslation(translation);
    468             attachNode.setLocalRotation(rotation);
    469         }
    470     }
    471 
    472     /**
    473      * Returns the local transform of this bone combined with the given position and rotation
    474      * @param position a position
    475      * @param rotation a rotation
    476      */
    477     public Transform getCombinedTransform(Vector3f position, Quaternion rotation) {
    478         rotation.mult(localPos, tmpTransform.getTranslation()).addLocal(position);
    479         tmpTransform.setRotation(rotation).getRotation().multLocal(localRot);
    480         return tmpTransform;
    481     }
    482 
    483     /**
    484      * Returns the attachment node.
    485      * Attach models and effects to this node to make
    486      * them follow this bone's motions.
    487      */
    488     Node getAttachmentsNode() {
    489         if (attachNode == null) {
    490             attachNode = new Node(name + "_attachnode");
    491             attachNode.setUserData("AttachedBone", this);
    492         }
    493         return attachNode;
    494     }
    495 
    496     /**
    497      * Used internally after model cloning.
    498      * @param attachNode
    499      */
    500     void setAttachmentsNode(Node attachNode) {
    501         this.attachNode = attachNode;
    502     }
    503 
    504     /**
    505      * Sets the local animation transform of this bone.
    506      * Bone is assumed to be in bind pose when this is called.
    507      */
    508     void setAnimTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) {
    509         if (userControl) {
    510             return;
    511         }
    512 
    513 //        localPos.addLocal(translation);
    514 //        localRot.multLocal(rotation);
    515         //localRot = localRot.mult(rotation);
    516 
    517         localPos.set(initialPos).addLocal(translation);
    518         localRot.set(initialRot).multLocal(rotation);
    519 
    520         if (scale != null) {
    521             localScale.set(initialScale).multLocal(scale);
    522         }
    523     }
    524 
    525     void blendAnimTransforms(Vector3f translation, Quaternion rotation, Vector3f scale, float weight) {
    526         if (userControl) {
    527             return;
    528         }
    529 
    530         TempVars vars = TempVars.get();
    531 //        assert vars.lock();
    532 
    533         Vector3f tmpV = vars.vect1;
    534         Vector3f tmpV2 = vars.vect2;
    535         Quaternion tmpQ = vars.quat1;
    536 
    537         //location
    538         tmpV.set(initialPos).addLocal(translation);
    539         localPos.interpolate(tmpV, weight);
    540 
    541         //rotation
    542         tmpQ.set(initialRot).multLocal(rotation);
    543         localRot.nlerp(tmpQ, weight);
    544 
    545         //scale
    546         if (scale != null) {
    547             tmpV2.set(initialScale).multLocal(scale);
    548             localScale.interpolate(tmpV2, weight);
    549         }
    550 
    551 
    552         vars.release();
    553     }
    554 
    555     /**
    556      * Sets local bind transform for bone.
    557      * Call setBindingPose() after all of the skeleton bones' bind transforms are set to save them.
    558      */
    559     public void setBindTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) {
    560         initialPos.set(translation);
    561         initialRot.set(rotation);
    562         //ogre.xml can have null scale values breaking this if the check is removed
    563         if (scale != null) {
    564             initialScale.set(scale);
    565         }
    566 
    567         localPos.set(translation);
    568         localRot.set(rotation);
    569         if (scale != null) {
    570             localScale.set(scale);
    571         }
    572     }
    573 
    574     private String toString(int depth) {
    575         StringBuilder sb = new StringBuilder();
    576         for (int i = 0; i < depth; i++) {
    577             sb.append('-');
    578         }
    579 
    580         sb.append(name).append(" bone\n");
    581         for (Bone child : children) {
    582             sb.append(child.toString(depth + 1));
    583         }
    584         return sb.toString();
    585     }
    586 
    587     @Override
    588     public String toString() {
    589         return this.toString(0);
    590     }
    591 
    592     @Override
    593     @SuppressWarnings("unchecked")
    594     public void read(JmeImporter im) throws IOException {
    595         InputCapsule input = im.getCapsule(this);
    596 
    597         name = input.readString("name", null);
    598         initialPos = (Vector3f) input.readSavable("initialPos", null);
    599         initialRot = (Quaternion) input.readSavable("initialRot", null);
    600         initialScale = (Vector3f) input.readSavable("initialScale", new Vector3f(1.0f, 1.0f, 1.0f));
    601         attachNode = (Node) input.readSavable("attachNode", null);
    602 
    603         localPos.set(initialPos);
    604         localRot.set(initialRot);
    605 
    606         ArrayList<Bone> childList = input.readSavableArrayList("children", null);
    607         for (int i = childList.size() - 1; i >= 0; i--) {
    608             this.addChild(childList.get(i));
    609         }
    610 
    611         // NOTE: Parent skeleton will call update() then setBindingPose()
    612         // after Skeleton has been de-serialized.
    613         // Therefore, worldBindInversePos and worldBindInverseRot
    614         // will be reconstructed based on that information.
    615     }
    616 
    617     @Override
    618     public void write(JmeExporter ex) throws IOException {
    619         OutputCapsule output = ex.getCapsule(this);
    620 
    621         output.write(name, "name", null);
    622         output.write(attachNode, "attachNode", null);
    623         output.write(initialPos, "initialPos", null);
    624         output.write(initialRot, "initialRot", null);
    625         output.write(initialScale, "initialScale", new Vector3f(1.0f, 1.0f, 1.0f));
    626         output.writeSavableArrayList(children, "children", null);
    627     }
    628 }
    629