• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Rémy 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      */
SkeletonControl()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      */
SkeletonControl(Skeleton skeleton)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
SkeletonControl(Mesh[] targets, Skeleton skeleton)69     SkeletonControl(Mesh[] targets, Skeleton skeleton) {
70         this.skeleton = skeleton;
71         this.targets = targets;
72     }
73 
isMeshAnimated(Mesh mesh)74     private boolean isMeshAnimated(Mesh mesh) {
75         return mesh.getBuffer(Type.BindPosePosition) != null;
76     }
77 
findTargets(Node node)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
setSpatial(Spatial spatial)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
controlRender(RenderManager rm, ViewPort vp)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
controlUpdate(float tpf)152     protected void controlUpdate(float tpf) {
153         wasMeshUpdated = false;
154     }
155 
resetToBind()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 
cloneForSpatial(Spatial spatial)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      */
getAttachmentsNode(String boneName)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      */
getSkeleton()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      */
getTargets()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      */
softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices)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      */
applySkinning(Mesh mesh, Matrix4f[] offsetMatrices)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      */
applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexBuffer tb)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
write(JmeExporter ex)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
read(JmeImporter im)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