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      */
Bone(String name)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      */
Bone(Bone source)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      */
Bone()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      */
getName()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      */
getParent()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      */
getChildren()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      */
getLocalPosition()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      */
getLocalRotation()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      */
getLocalScale()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      */
getModelSpacePosition()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      */
getModelSpaceRotation()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      */
getModelSpaceScale()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      */
getWorldBindInversePosition()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      */
getWorldBindInverseRotation()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      */
getWorldBindInverseScale()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      */
getWorldBindPosition()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      */
getWorldBindRotation()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      */
getWorldBindScale()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      */
setUserControl(boolean enable)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      */
addChild(Bone bone)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      */
updateWorldVectors()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      */
update()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      */
setBindingPose()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      */
reset()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      */
getOffsetTransform(Matrix4f outTransform, Quaternion tmp1, Vector3f tmp2, Vector3f tmp3, Matrix3f tmp4)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      */
setUserTransforms(Vector3f translation, Quaternion rotation, Vector3f scale)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      */
setUserTransformsWorld(Vector3f translation, Quaternion rotation)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      */
getCombinedTransform(Vector3f position, Quaternion rotation)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      */
getAttachmentsNode()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      */
setAttachmentsNode(Node attachNode)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      */
setAnimTransforms(Vector3f translation, Quaternion rotation, Vector3f scale)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 
blendAnimTransforms(Vector3f translation, Quaternion rotation, Vector3f scale, float weight)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      */
setBindTransforms(Vector3f translation, Quaternion rotation, Vector3f scale)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 
toString(int depth)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
toString()588     public String toString() {
589         return this.toString(0);
590     }
591 
592     @Override
593     @SuppressWarnings("unchecked")
read(JmeImporter im)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
write(JmeExporter ex)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