Home | History | Annotate | Download | only in animation
      1 /*
      2  * Copyright (c) 2009-2012 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.Quaternion;
     36 import com.jme3.math.Vector3f;
     37 import com.jme3.util.TempVars;
     38 import java.io.IOException;
     39 import java.util.BitSet;
     40 
     41 /**
     42  * Contains a list of transforms and times for each keyframe.
     43  *
     44  * @author Kirill Vainer
     45  */
     46 public final class BoneTrack implements Track {
     47 
     48     /**
     49      * Bone index in the skeleton which this track effects.
     50      */
     51     private int targetBoneIndex;
     52 
     53     /**
     54      * Transforms and times for track.
     55      */
     56     private CompactVector3Array translations;
     57     private CompactQuaternionArray rotations;
     58     private CompactVector3Array scales;
     59     private float[] times;
     60 
     61     /**
     62      * Serialization-only. Do not use.
     63      */
     64     public BoneTrack() {
     65     }
     66 
     67     /**
     68      * Creates a bone track for the given bone index
     69      * @param targetBoneIndex the bone index
     70      * @param times a float array with the time of each frame
     71      * @param translations the translation of the bone for each frame
     72      * @param rotations the rotation of the bone for each frame
     73      */
     74     public BoneTrack(int targetBoneIndex, float[] times, Vector3f[] translations, Quaternion[] rotations) {
     75         this.targetBoneIndex = targetBoneIndex;
     76         this.setKeyframes(times, translations, rotations);
     77     }
     78 
     79     /**
     80      * Creates a bone track for the given bone index
     81      * @param targetBoneIndex the bone index
     82      * @param times a float array with the time of each frame
     83      * @param translations the translation of the bone for each frame
     84      * @param rotations the rotation of the bone for each frame
     85      * @param scales the scale of the bone for each frame
     86      */
     87     public BoneTrack(int targetBoneIndex, float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) {
     88     	this.targetBoneIndex = targetBoneIndex;
     89         this.setKeyframes(times, translations, rotations, scales);
     90     }
     91 
     92     /**
     93      * Creates a bone track for the given bone index
     94      * @param targetBoneIndex the bone's index
     95      */
     96     public BoneTrack(int targetBoneIndex) {
     97         this.targetBoneIndex = targetBoneIndex;
     98     }
     99 
    100     /**
    101      * @return the bone index of this bone track.
    102      */
    103     public int getTargetBoneIndex() {
    104         return targetBoneIndex;
    105     }
    106 
    107     /**
    108      * return the array of rotations of this track
    109      * @return
    110      */
    111     public Quaternion[] getRotations() {
    112         return rotations.toObjectArray();
    113     }
    114 
    115     /**
    116      * returns the array of scales for this track
    117      * @return
    118      */
    119     public Vector3f[] getScales() {
    120         return scales == null ? null : scales.toObjectArray();
    121     }
    122 
    123     /**
    124      * returns the arrays of time for this track
    125      * @return
    126      */
    127     public float[] getTimes() {
    128         return times;
    129     }
    130 
    131     /**
    132      * returns the array of translations of this track
    133      * @return
    134      */
    135     public Vector3f[] getTranslations() {
    136         return translations.toObjectArray();
    137     }
    138 
    139     /**
    140      * Set the translations and rotations for this bone track
    141      * @param times a float array with the time of each frame
    142      * @param translations the translation of the bone for each frame
    143      * @param rotations the rotation of the bone for each frame
    144      */
    145     public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations) {
    146         if (times.length == 0) {
    147             throw new RuntimeException("BoneTrack with no keyframes!");
    148         }
    149 
    150         assert times.length == translations.length && times.length == rotations.length;
    151 
    152         this.times = times;
    153         this.translations = new CompactVector3Array();
    154         this.translations.add(translations);
    155         this.translations.freeze();
    156         this.rotations = new CompactQuaternionArray();
    157         this.rotations.add(rotations);
    158         this.rotations.freeze();
    159     }
    160 
    161     /**
    162      * Set the translations, rotations and scales for this bone track
    163      * @param times a float array with the time of each frame
    164      * @param translations the translation of the bone for each frame
    165      * @param rotations the rotation of the bone for each frame
    166      * @param scales the scale of the bone for each frame
    167      */
    168     public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) {
    169         this.setKeyframes(times, translations, rotations);
    170         assert times.length == scales.length;
    171         if (scales != null) {
    172             this.scales = new CompactVector3Array();
    173             this.scales.add(scales);
    174             this.scales.freeze();
    175         }
    176     }
    177 
    178     /**
    179      *
    180      * Modify the bone which this track modifies in the skeleton to contain
    181      * the correct animation transforms for a given time.
    182      * The transforms can be interpolated in some method from the keyframes.
    183      *
    184      * @param time the current time of the animation
    185      * @param weight the weight of the animation
    186      * @param control
    187      * @param channel
    188      * @param vars
    189      */
    190     public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) {
    191         BitSet affectedBones = channel.getAffectedBones();
    192         if (affectedBones != null && !affectedBones.get(targetBoneIndex)) {
    193             return;
    194         }
    195 
    196         Bone target = control.getSkeleton().getBone(targetBoneIndex);
    197 
    198         Vector3f tempV = vars.vect1;
    199         Vector3f tempS = vars.vect2;
    200         Quaternion tempQ = vars.quat1;
    201         Vector3f tempV2 = vars.vect3;
    202         Vector3f tempS2 = vars.vect4;
    203         Quaternion tempQ2 = vars.quat2;
    204 
    205         int lastFrame = times.length - 1;
    206         if (time < 0 || lastFrame == 0) {
    207             rotations.get(0, tempQ);
    208             translations.get(0, tempV);
    209             if (scales != null) {
    210                 scales.get(0, tempS);
    211             }
    212         } else if (time >= times[lastFrame]) {
    213             rotations.get(lastFrame, tempQ);
    214             translations.get(lastFrame, tempV);
    215             if (scales != null) {
    216                 scales.get(lastFrame, tempS);
    217             }
    218         } else {
    219             int startFrame = 0;
    220             int endFrame = 1;
    221             // use lastFrame so we never overflow the array
    222             int i;
    223             for (i = 0; i < lastFrame && times[i] < time; i++) {
    224                 startFrame = i;
    225                 endFrame = i + 1;
    226             }
    227 
    228             float blend = (time - times[startFrame])
    229                     / (times[endFrame] - times[startFrame]);
    230 
    231             rotations.get(startFrame, tempQ);
    232             translations.get(startFrame, tempV);
    233             if (scales != null) {
    234                 scales.get(startFrame, tempS);
    235             }
    236             rotations.get(endFrame, tempQ2);
    237             translations.get(endFrame, tempV2);
    238             if (scales != null) {
    239                 scales.get(endFrame, tempS2);
    240             }
    241             tempQ.nlerp(tempQ2, blend);
    242             tempV.interpolate(tempV2, blend);
    243             tempS.interpolate(tempS2, blend);
    244         }
    245 
    246         if (weight != 1f) {
    247             target.blendAnimTransforms(tempV, tempQ, scales != null ? tempS : null, weight);
    248         } else {
    249             target.setAnimTransforms(tempV, tempQ, scales != null ? tempS : null);
    250         }
    251     }
    252 
    253     /**
    254      * @return the length of the track
    255      */
    256     public float getLength() {
    257         return times == null ? 0 : times[times.length - 1] - times[0];
    258     }
    259 
    260     /**
    261      * This method creates a clone of the current object.
    262      * @return a clone of the current object
    263      */
    264     @Override
    265     public BoneTrack clone() {
    266         int tablesLength = times.length;
    267 
    268         float[] times = this.times.clone();
    269         Vector3f[] sourceTranslations = this.getTranslations();
    270         Quaternion[] sourceRotations = this.getRotations();
    271         Vector3f[] sourceScales = this.getScales();
    272 
    273         Vector3f[] translations = new Vector3f[tablesLength];
    274         Quaternion[] rotations = new Quaternion[tablesLength];
    275         Vector3f[] scales = new Vector3f[tablesLength];
    276         for (int i = 0; i < tablesLength; ++i) {
    277             translations[i] = sourceTranslations[i].clone();
    278             rotations[i] = sourceRotations[i].clone();
    279             scales[i] = sourceScales != null ? sourceScales[i].clone() : new Vector3f(1.0f, 1.0f, 1.0f);
    280         }
    281 
    282         // Need to use the constructor here because of the final fields used in this class
    283         return new BoneTrack(targetBoneIndex, times, translations, rotations, scales);
    284     }
    285 
    286     @Override
    287     public void write(JmeExporter ex) throws IOException {
    288         OutputCapsule oc = ex.getCapsule(this);
    289         oc.write(targetBoneIndex, "boneIndex", 0);
    290         oc.write(translations, "translations", null);
    291         oc.write(rotations, "rotations", null);
    292         oc.write(times, "times", null);
    293         oc.write(scales, "scales", null);
    294     }
    295 
    296     @Override
    297     public void read(JmeImporter im) throws IOException {
    298         InputCapsule ic = im.getCapsule(this);
    299         targetBoneIndex = ic.readInt("boneIndex", 0);
    300 
    301         translations = (CompactVector3Array) ic.readSavable("translations", null);
    302         rotations = (CompactQuaternionArray) ic.readSavable("rotations", null);
    303         times = ic.readFloatArray("times", null);
    304         scales = (CompactVector3Array) ic.readSavable("scales", null);
    305 
    306         //Backward compatibility for old j3o files generated before revision 6807
    307         if (im.getFormatVersion() == 0){
    308             if (translations == null) {
    309                 Savable[] sav = ic.readSavableArray("translations", null);
    310                 if (sav != null) {
    311                     translations = new CompactVector3Array();
    312                     Vector3f[] transCopy = new Vector3f[sav.length];
    313                     System.arraycopy(sav, 0, transCopy, 0, sav.length);
    314                     translations.add(transCopy);
    315                     translations.freeze();
    316                 }
    317             }
    318             if (rotations == null) {
    319                 Savable[] sav = ic.readSavableArray("rotations", null);
    320                 if (sav != null) {
    321                     rotations = new CompactQuaternionArray();
    322                     Quaternion[] rotCopy = new Quaternion[sav.length];
    323                     System.arraycopy(sav, 0, rotCopy, 0, sav.length);
    324                     rotations.add(rotCopy);
    325                     rotations.freeze();
    326                 }
    327             }
    328         }
    329     }
    330 
    331     public void setTime(float time, float weight, AnimControl control, AnimChannel channel) {
    332         throw new UnsupportedOperationException("Not supported yet.");
    333     }
    334 }
    335