Home | History | Annotate | Download | only in animation
      1 /*
      2  * To change this template, choose Tools | Templates
      3  * and open the template in the editor.
      4  */
      5 package com.jme3.animation;
      6 
      7 import com.jme3.export.*;
      8 import com.jme3.math.FastMath;
      9 import com.jme3.math.Matrix4f;
     10 import com.jme3.renderer.RenderManager;
     11 import com.jme3.renderer.ViewPort;
     12 import com.jme3.scene.*;
     13 import com.jme3.scene.VertexBuffer.Type;
     14 import com.jme3.scene.control.AbstractControl;
     15 import com.jme3.scene.control.Control;
     16 import com.jme3.util.TempVars;
     17 import java.io.IOException;
     18 import java.nio.ByteBuffer;
     19 import java.nio.FloatBuffer;
     20 import java.util.ArrayList;
     21 
     22 /**
     23  * The Skeleton control deforms a model according to a skeleton,
     24  * It handles the computation of the deformation matrices and performs
     25  * the transformations on the mesh
     26  *
     27  * @author Rmy Bouquet Based on AnimControl by Kirill Vainer
     28  */
     29 public class SkeletonControl extends AbstractControl implements Cloneable {
     30 
     31     /**
     32      * The skeleton of the model
     33      */
     34     private Skeleton skeleton;
     35     /**
     36      * List of targets which this controller effects.
     37      */
     38     private Mesh[] targets;
     39     /**
     40      * Used to track when a mesh was updated. Meshes are only updated
     41      * if they are visible in at least one camera.
     42      */
     43     private boolean wasMeshUpdated = false;
     44 
     45     /**
     46      * Serialization only. Do not use.
     47      */
     48     public SkeletonControl() {
     49     }
     50 
     51     /**
     52      * Creates a skeleton control.
     53      * The list of targets will be acquired automatically when
     54      * the control is attached to a node.
     55      *
     56      * @param skeleton the skeleton
     57      */
     58     public SkeletonControl(Skeleton skeleton) {
     59         this.skeleton = skeleton;
     60     }
     61 
     62     /**
     63      * Creates a skeleton control.
     64      *
     65      * @param targets the meshes controlled by the skeleton
     66      * @param skeleton the skeleton
     67      */
     68     @Deprecated
     69     SkeletonControl(Mesh[] targets, Skeleton skeleton) {
     70         this.skeleton = skeleton;
     71         this.targets = targets;
     72     }
     73 
     74     private boolean isMeshAnimated(Mesh mesh) {
     75         return mesh.getBuffer(Type.BindPosePosition) != null;
     76     }
     77 
     78     private Mesh[] findTargets(Node node) {
     79         Mesh sharedMesh = null;
     80         ArrayList<Mesh> animatedMeshes = new ArrayList<Mesh>();
     81 
     82         for (Spatial child : node.getChildren()) {
     83             if (!(child instanceof Geometry)) {
     84                 continue; // could be an attachment node, ignore.
     85             }
     86 
     87             Geometry geom = (Geometry) child;
     88 
     89             // is this geometry using a shared mesh?
     90             Mesh childSharedMesh = geom.getUserData(UserData.JME_SHAREDMESH);
     91 
     92             if (childSharedMesh != null) {
     93                 // Don't bother with non-animated shared meshes
     94                 if (isMeshAnimated(childSharedMesh)) {
     95                     // child is using shared mesh,
     96                     // so animate the shared mesh but ignore child
     97                     if (sharedMesh == null) {
     98                         sharedMesh = childSharedMesh;
     99                     } else if (sharedMesh != childSharedMesh) {
    100                         throw new IllegalStateException("Two conflicting shared meshes for " + node);
    101                     }
    102                 }
    103             } else {
    104                 Mesh mesh = geom.getMesh();
    105                 if (isMeshAnimated(mesh)) {
    106                     animatedMeshes.add(mesh);
    107                 }
    108             }
    109         }
    110 
    111         if (sharedMesh != null) {
    112             animatedMeshes.add(sharedMesh);
    113         }
    114 
    115         return animatedMeshes.toArray(new Mesh[animatedMeshes.size()]);
    116     }
    117 
    118     @Override
    119     public void setSpatial(Spatial spatial) {
    120         super.setSpatial(spatial);
    121         if (spatial != null) {
    122             Node node = (Node) spatial;
    123             targets = findTargets(node);
    124         } else {
    125             targets = null;
    126         }
    127     }
    128 
    129     @Override
    130     protected void controlRender(RenderManager rm, ViewPort vp) {
    131         if (!wasMeshUpdated) {
    132             resetToBind(); // reset morph meshes to bind pose
    133 
    134             Matrix4f[] offsetMatrices = skeleton.computeSkinningMatrices();
    135 
    136             // if hardware skinning is supported, the matrices and weight buffer
    137             // will be sent by the SkinningShaderLogic object assigned to the shader
    138             for (int i = 0; i < targets.length; i++) {
    139                 // NOTE: This assumes that code higher up
    140                 // Already ensured those targets are animated
    141                 // otherwise a crash will happen in skin update
    142                 //if (isMeshAnimated(targets[i])) {
    143                 softwareSkinUpdate(targets[i], offsetMatrices);
    144                 //}
    145             }
    146 
    147             wasMeshUpdated = true;
    148         }
    149     }
    150 
    151     @Override
    152     protected void controlUpdate(float tpf) {
    153         wasMeshUpdated = false;
    154     }
    155 
    156     void resetToBind() {
    157         for (Mesh mesh : targets) {
    158             if (isMeshAnimated(mesh)) {
    159                 VertexBuffer bi = mesh.getBuffer(Type.BoneIndex);
    160                 ByteBuffer bib = (ByteBuffer) bi.getData();
    161                 if (!bib.hasArray()) {
    162                     mesh.prepareForAnim(true); // prepare for software animation
    163                 }
    164                 VertexBuffer bindPos = mesh.getBuffer(Type.BindPosePosition);
    165                 VertexBuffer bindNorm = mesh.getBuffer(Type.BindPoseNormal);
    166                 VertexBuffer pos = mesh.getBuffer(Type.Position);
    167                 VertexBuffer norm = mesh.getBuffer(Type.Normal);
    168                 FloatBuffer pb = (FloatBuffer) pos.getData();
    169                 FloatBuffer nb = (FloatBuffer) norm.getData();
    170                 FloatBuffer bpb = (FloatBuffer) bindPos.getData();
    171                 FloatBuffer bnb = (FloatBuffer) bindNorm.getData();
    172                 pb.clear();
    173                 nb.clear();
    174                 bpb.clear();
    175                 bnb.clear();
    176 
    177                 //reseting bind tangents if there is a bind tangent buffer
    178                 VertexBuffer bindTangents = mesh.getBuffer(Type.BindPoseTangent);
    179                 if (bindTangents != null) {
    180                     VertexBuffer tangents = mesh.getBuffer(Type.Tangent);
    181                     FloatBuffer tb = (FloatBuffer) tangents.getData();
    182                     FloatBuffer btb = (FloatBuffer) bindTangents.getData();
    183                     tb.clear();
    184                     btb.clear();
    185                     tb.put(btb).clear();
    186                 }
    187 
    188 
    189                 pb.put(bpb).clear();
    190                 nb.put(bnb).clear();
    191             }
    192         }
    193     }
    194 
    195     public Control cloneForSpatial(Spatial spatial) {
    196         Node clonedNode = (Node) spatial;
    197         AnimControl ctrl = spatial.getControl(AnimControl.class);
    198         SkeletonControl clone = new SkeletonControl();
    199         clone.setSpatial(clonedNode);
    200 
    201         clone.skeleton = ctrl.getSkeleton();
    202         // Fix animated targets for the cloned node
    203         clone.targets = findTargets(clonedNode);
    204 
    205         // Fix attachments for the cloned node
    206         for (int i = 0; i < clonedNode.getQuantity(); i++) {
    207             // go through attachment nodes, apply them to correct bone
    208             Spatial child = clonedNode.getChild(i);
    209             if (child instanceof Node) {
    210                 Node clonedAttachNode = (Node) child;
    211                 Bone originalBone = (Bone) clonedAttachNode.getUserData("AttachedBone");
    212 
    213                 if (originalBone != null) {
    214                     Bone clonedBone = clone.skeleton.getBone(originalBone.getName());
    215 
    216                     clonedAttachNode.setUserData("AttachedBone", clonedBone);
    217                     clonedBone.setAttachmentsNode(clonedAttachNode);
    218                 }
    219             }
    220         }
    221 
    222         return clone;
    223     }
    224 
    225     /**
    226      *
    227      * @param boneName the name of the bone
    228      * @return the node attached to this bone
    229      */
    230     public Node getAttachmentsNode(String boneName) {
    231         Bone b = skeleton.getBone(boneName);
    232         if (b == null) {
    233             throw new IllegalArgumentException("Given bone name does not exist "
    234                     + "in the skeleton.");
    235         }
    236 
    237         Node n = b.getAttachmentsNode();
    238         Node model = (Node) spatial;
    239         model.attachChild(n);
    240         return n;
    241     }
    242 
    243     /**
    244      * returns the skeleton of this control
    245      * @return
    246      */
    247     public Skeleton getSkeleton() {
    248         return skeleton;
    249     }
    250 
    251     /**
    252      * sets the skeleton for this control
    253      * @param skeleton
    254      */
    255 //    public void setSkeleton(Skeleton skeleton) {
    256 //        this.skeleton = skeleton;
    257 //    }
    258     /**
    259      * returns the targets meshes of this control
    260      * @return
    261      */
    262     public Mesh[] getTargets() {
    263         return targets;
    264     }
    265 
    266     /**
    267      * sets the target  meshes of this control
    268      * @param targets
    269      */
    270 //    public void setTargets(Mesh[] targets) {
    271 //        this.targets = targets;
    272 //    }
    273     /**
    274      * Update the mesh according to the given transformation matrices
    275      * @param mesh then mesh
    276      * @param offsetMatrices the transformation matrices to apply
    277      */
    278     private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) {
    279 
    280         VertexBuffer tb = mesh.getBuffer(Type.Tangent);
    281         if (tb == null) {
    282             //if there are no tangents use the classic skinning
    283             applySkinning(mesh, offsetMatrices);
    284         } else {
    285             //if there are tangents use the skinning with tangents
    286             applySkinningTangents(mesh, offsetMatrices, tb);
    287         }
    288 
    289 
    290     }
    291 
    292     /**
    293      * Method to apply skinning transforms to a mesh's buffers
    294      * @param mesh the mesh
    295      * @param offsetMatrices the offset matices to apply
    296      */
    297     private void applySkinning(Mesh mesh, Matrix4f[] offsetMatrices) {
    298         int maxWeightsPerVert = mesh.getMaxNumWeights();
    299         if (maxWeightsPerVert <= 0) {
    300             throw new IllegalStateException("Max weights per vert is incorrectly set!");
    301         }
    302 
    303         int fourMinusMaxWeights = 4 - maxWeightsPerVert;
    304 
    305         // NOTE: This code assumes the vertex buffer is in bind pose
    306         // resetToBind() has been called this frame
    307         VertexBuffer vb = mesh.getBuffer(Type.Position);
    308         FloatBuffer fvb = (FloatBuffer) vb.getData();
    309         fvb.rewind();
    310 
    311         VertexBuffer nb = mesh.getBuffer(Type.Normal);
    312         FloatBuffer fnb = (FloatBuffer) nb.getData();
    313         fnb.rewind();
    314 
    315         // get boneIndexes and weights for mesh
    316         ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
    317         FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
    318 
    319         ib.rewind();
    320         wb.rewind();
    321 
    322         float[] weights = wb.array();
    323         byte[] indices = ib.array();
    324         int idxWeights = 0;
    325 
    326         TempVars vars = TempVars.get();
    327 
    328 
    329         float[] posBuf = vars.skinPositions;
    330         float[] normBuf = vars.skinNormals;
    331 
    332         int iterations = (int) FastMath.ceil(fvb.capacity() / ((float) posBuf.length));
    333         int bufLength = posBuf.length;
    334         for (int i = iterations - 1; i >= 0; i--) {
    335             // read next set of positions and normals from native buffer
    336             bufLength = Math.min(posBuf.length, fvb.remaining());
    337             fvb.get(posBuf, 0, bufLength);
    338             fnb.get(normBuf, 0, bufLength);
    339             int verts = bufLength / 3;
    340             int idxPositions = 0;
    341 
    342             // iterate vertices and apply skinning transform for each effecting bone
    343             for (int vert = verts - 1; vert >= 0; vert--) {
    344                 float nmx = normBuf[idxPositions];
    345                 float vtx = posBuf[idxPositions++];
    346                 float nmy = normBuf[idxPositions];
    347                 float vty = posBuf[idxPositions++];
    348                 float nmz = normBuf[idxPositions];
    349                 float vtz = posBuf[idxPositions++];
    350 
    351                 float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0;
    352 
    353                 for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
    354                     float weight = weights[idxWeights];
    355                     Matrix4f mat = offsetMatrices[indices[idxWeights++]];
    356 
    357                     rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
    358                     ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
    359                     rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight;
    360 
    361                     rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight;
    362                     rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight;
    363                     rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight;
    364                 }
    365 
    366                 idxWeights += fourMinusMaxWeights;
    367 
    368                 idxPositions -= 3;
    369                 normBuf[idxPositions] = rnx;
    370                 posBuf[idxPositions++] = rx;
    371                 normBuf[idxPositions] = rny;
    372                 posBuf[idxPositions++] = ry;
    373                 normBuf[idxPositions] = rnz;
    374                 posBuf[idxPositions++] = rz;
    375             }
    376 
    377             fvb.position(fvb.position() - bufLength);
    378             fvb.put(posBuf, 0, bufLength);
    379             fnb.position(fnb.position() - bufLength);
    380             fnb.put(normBuf, 0, bufLength);
    381         }
    382 
    383         vars.release();
    384 
    385         vb.updateData(fvb);
    386         nb.updateData(fnb);
    387 
    388     }
    389 
    390     /**
    391      * Specific method for skinning with tangents to avoid cluttering the classic skinning calculation with
    392      * null checks that would slow down the process even if tangents don't have to be computed.
    393      * Also the iteration has additional indexes since tangent has 4 components instead of 3 for pos and norm
    394      * @param maxWeightsPerVert maximum number of weights per vertex
    395      * @param mesh the mesh
    396      * @param offsetMatrices the offsetMaytrices to apply
    397      * @param tb the tangent vertexBuffer
    398      */
    399     private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexBuffer tb) {
    400         int maxWeightsPerVert = mesh.getMaxNumWeights();
    401 
    402         if (maxWeightsPerVert <= 0) {
    403             throw new IllegalStateException("Max weights per vert is incorrectly set!");
    404         }
    405 
    406         int fourMinusMaxWeights = 4 - maxWeightsPerVert;
    407 
    408         // NOTE: This code assumes the vertex buffer is in bind pose
    409         // resetToBind() has been called this frame
    410         VertexBuffer vb = mesh.getBuffer(Type.Position);
    411         FloatBuffer fvb = (FloatBuffer) vb.getData();
    412         fvb.rewind();
    413 
    414         VertexBuffer nb = mesh.getBuffer(Type.Normal);
    415 
    416         FloatBuffer fnb = (FloatBuffer) nb.getData();
    417         fnb.rewind();
    418 
    419 
    420         FloatBuffer ftb = (FloatBuffer) tb.getData();
    421         ftb.rewind();
    422 
    423 
    424         // get boneIndexes and weights for mesh
    425         ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
    426         FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
    427 
    428         ib.rewind();
    429         wb.rewind();
    430 
    431         float[] weights = wb.array();
    432         byte[] indices = ib.array();
    433         int idxWeights = 0;
    434 
    435         TempVars vars = TempVars.get();
    436 
    437 
    438         float[] posBuf = vars.skinPositions;
    439         float[] normBuf = vars.skinNormals;
    440         float[] tanBuf = vars.skinTangents;
    441 
    442         int iterations = (int) FastMath.ceil(fvb.capacity() / ((float) posBuf.length));
    443         int bufLength = 0;
    444         int tanLength = 0;
    445         for (int i = iterations - 1; i >= 0; i--) {
    446             // read next set of positions and normals from native buffer
    447             bufLength = Math.min(posBuf.length, fvb.remaining());
    448             tanLength = Math.min(tanBuf.length, ftb.remaining());
    449             fvb.get(posBuf, 0, bufLength);
    450             fnb.get(normBuf, 0, bufLength);
    451             ftb.get(tanBuf, 0, tanLength);
    452             int verts = bufLength / 3;
    453             int idxPositions = 0;
    454             //tangents has their own index because of the 4 components
    455             int idxTangents = 0;
    456 
    457             // iterate vertices and apply skinning transform for each effecting bone
    458             for (int vert = verts - 1; vert >= 0; vert--) {
    459                 float nmx = normBuf[idxPositions];
    460                 float vtx = posBuf[idxPositions++];
    461                 float nmy = normBuf[idxPositions];
    462                 float vty = posBuf[idxPositions++];
    463                 float nmz = normBuf[idxPositions];
    464                 float vtz = posBuf[idxPositions++];
    465 
    466                 float tnx = tanBuf[idxTangents++];
    467                 float tny = tanBuf[idxTangents++];
    468                 float tnz = tanBuf[idxTangents++];
    469 
    470                 //skipping the 4th component of the tangent since it doesn't have to be transformed
    471                 idxTangents++;
    472 
    473                 float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0, rtx = 0, rty = 0, rtz = 0;
    474 
    475                 for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
    476                     float weight = weights[idxWeights];
    477                     Matrix4f mat = offsetMatrices[indices[idxWeights++]];
    478 
    479                     rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
    480                     ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
    481                     rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight;
    482 
    483                     rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight;
    484                     rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight;
    485                     rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight;
    486 
    487                     rtx += (tnx * mat.m00 + tny * mat.m01 + tnz * mat.m02) * weight;
    488                     rty += (tnx * mat.m10 + tny * mat.m11 + tnz * mat.m12) * weight;
    489                     rtz += (tnx * mat.m20 + tny * mat.m21 + tnz * mat.m22) * weight;
    490                 }
    491 
    492                 idxWeights += fourMinusMaxWeights;
    493 
    494                 idxPositions -= 3;
    495 
    496                 normBuf[idxPositions] = rnx;
    497                 posBuf[idxPositions++] = rx;
    498                 normBuf[idxPositions] = rny;
    499                 posBuf[idxPositions++] = ry;
    500                 normBuf[idxPositions] = rnz;
    501                 posBuf[idxPositions++] = rz;
    502 
    503                 idxTangents -= 4;
    504 
    505                 tanBuf[idxTangents++] = rtx;
    506                 tanBuf[idxTangents++] = rty;
    507                 tanBuf[idxTangents++] = rtz;
    508 
    509                 //once again skipping the 4th component of the tangent
    510                 idxTangents++;
    511             }
    512 
    513             fvb.position(fvb.position() - bufLength);
    514             fvb.put(posBuf, 0, bufLength);
    515             fnb.position(fnb.position() - bufLength);
    516             fnb.put(normBuf, 0, bufLength);
    517             ftb.position(ftb.position() - tanLength);
    518             ftb.put(tanBuf, 0, tanLength);
    519         }
    520 
    521         vars.release();
    522 
    523         vb.updateData(fvb);
    524         nb.updateData(fnb);
    525         tb.updateData(ftb);
    526 
    527 
    528     }
    529 
    530     @Override
    531     public void write(JmeExporter ex) throws IOException {
    532         super.write(ex);
    533         OutputCapsule oc = ex.getCapsule(this);
    534         oc.write(targets, "targets", null);
    535         oc.write(skeleton, "skeleton", null);
    536     }
    537 
    538     @Override
    539     public void read(JmeImporter im) throws IOException {
    540         super.read(im);
    541         InputCapsule in = im.getCapsule(this);
    542         Savable[] sav = in.readSavableArray("targets", null);
    543         if (sav != null) {
    544             targets = new Mesh[sav.length];
    545             System.arraycopy(sav, 0, targets, 0, sav.length);
    546         }
    547         skeleton = (Skeleton) in.readSavable("skeleton", null);
    548     }
    549 }
    550