OSDN Git Service

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