aboutsummaryrefslogtreecommitdiff
path: root/engine/src/core/com/jme3/animation/Skeleton.java
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/core/com/jme3/animation/Skeleton.java')
-rw-r--r--engine/src/core/com/jme3/animation/Skeleton.java297
1 files changed, 297 insertions, 0 deletions
diff --git a/engine/src/core/com/jme3/animation/Skeleton.java b/engine/src/core/com/jme3/animation/Skeleton.java
new file mode 100644
index 0000000..bc36542
--- /dev/null
+++ b/engine/src/core/com/jme3/animation/Skeleton.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.export.*;
+import com.jme3.math.Matrix4f;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <code>Skeleton</code> is a convenience class for managing a bone hierarchy.
+ * Skeleton updates the world transforms to reflect the current local
+ * animated matrixes.
+ *
+ * @author Kirill Vainer
+ */
+public final class Skeleton implements Savable {
+
+ private Bone[] rootBones;
+ private Bone[] boneList;
+
+ /**
+ * Contains the skinning matrices, multiplying it by a vertex effected by a bone
+ * will cause it to go to the animated position.
+ */
+ private transient Matrix4f[] skinningMatrixes;
+
+ /**
+ * Creates a skeleton from a bone list.
+ * The root bones are found automatically.
+ * <p>
+ * Note that using this constructor will cause the bones in the list
+ * to have their bind pose recomputed based on their local transforms.
+ *
+ * @param boneList The list of bones to manage by this Skeleton
+ */
+ public Skeleton(Bone[] boneList) {
+ this.boneList = boneList;
+
+ List<Bone> rootBoneList = new ArrayList<Bone>();
+ for (int i = boneList.length - 1; i >= 0; i--) {
+ Bone b = boneList[i];
+ if (b.getParent() == null) {
+ rootBoneList.add(b);
+ }
+ }
+ rootBones = rootBoneList.toArray(new Bone[rootBoneList.size()]);
+
+ createSkinningMatrices();
+
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ Bone rootBone = rootBones[i];
+ rootBone.update();
+ rootBone.setBindingPose();
+ }
+ }
+
+ /**
+ * Special-purpose copy constructor.
+ * <p>
+ * Shallow copies bind pose data from the source skeleton, does not
+ * copy any other data.
+ *
+ * @param source The source Skeleton to copy from
+ */
+ public Skeleton(Skeleton source) {
+ Bone[] sourceList = source.boneList;
+ boneList = new Bone[sourceList.length];
+ for (int i = 0; i < sourceList.length; i++) {
+ boneList[i] = new Bone(sourceList[i]);
+ }
+
+ rootBones = new Bone[source.rootBones.length];
+ for (int i = 0; i < rootBones.length; i++) {
+ rootBones[i] = recreateBoneStructure(source.rootBones[i]);
+ }
+ createSkinningMatrices();
+
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ rootBones[i].update();
+ }
+ }
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Skeleton() {
+ }
+
+ private void createSkinningMatrices() {
+ skinningMatrixes = new Matrix4f[boneList.length];
+ for (int i = 0; i < skinningMatrixes.length; i++) {
+ skinningMatrixes[i] = new Matrix4f();
+ }
+ }
+
+ private Bone recreateBoneStructure(Bone sourceRoot) {
+ Bone targetRoot = getBone(sourceRoot.getName());
+ List<Bone> children = sourceRoot.getChildren();
+ for (int i = 0; i < children.size(); i++) {
+ Bone sourceChild = children.get(i);
+ // find my version of the child
+ Bone targetChild = getBone(sourceChild.getName());
+ targetRoot.addChild(targetChild);
+ recreateBoneStructure(sourceChild);
+ }
+
+ return targetRoot;
+ }
+
+ /**
+ * Updates world transforms for all bones in this skeleton.
+ * Typically called after setting local animation transforms.
+ */
+ public void updateWorldVectors() {
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ rootBones[i].update();
+ }
+ }
+
+ /**
+ * Saves the current skeleton state as it's binding pose.
+ */
+ public void setBindingPose() {
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ rootBones[i].setBindingPose();
+ }
+ }
+
+ /**
+ * Reset the skeleton to bind pose.
+ */
+ public final void reset() {
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ rootBones[i].reset();
+ }
+ }
+
+ /**
+ * Reset the skeleton to bind pose and updates the bones
+ */
+ public final void resetAndUpdate() {
+ for (int i = rootBones.length - 1; i >= 0; i--) {
+ Bone rootBone = rootBones[i];
+ rootBone.reset();
+ rootBone.update();
+ }
+ }
+
+ /**
+ * returns the array of all root bones of this skeleton
+ * @return
+ */
+ public Bone[] getRoots() {
+ return rootBones;
+ }
+
+ /**
+ * return a bone for the given index
+ * @param index
+ * @return
+ */
+ public Bone getBone(int index) {
+ return boneList[index];
+ }
+
+ /**
+ * returns the bone with the given name
+ * @param name
+ * @return
+ */
+ public Bone getBone(String name) {
+ for (int i = 0; i < boneList.length; i++) {
+ if (boneList[i].getName().equals(name)) {
+ return boneList[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * returns the bone index of the given bone
+ * @param bone
+ * @return
+ */
+ public int getBoneIndex(Bone bone) {
+ for (int i = 0; i < boneList.length; i++) {
+ if (boneList[i] == bone) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * returns the bone index of the bone that has the given name
+ * @param name
+ * @return
+ */
+ public int getBoneIndex(String name) {
+ for (int i = 0; i < boneList.length; i++) {
+ if (boneList[i].getName().equals(name)) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Compute the skining matrices for each bone of the skeleton that would be used to transform vertices of associated meshes
+ * @return
+ */
+ public Matrix4f[] computeSkinningMatrices() {
+ TempVars vars = TempVars.get();
+ for (int i = 0; i < boneList.length; i++) {
+ boneList[i].getOffsetTransform(skinningMatrixes[i], vars.quat1, vars.vect1, vars.vect2, vars.tempMat3);
+ }
+ vars.release();
+ return skinningMatrixes;
+ }
+
+ /**
+ * returns the number of bones of this skeleton
+ * @return
+ */
+ public int getBoneCount() {
+ return boneList.length;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Skeleton - ").append(boneList.length).append(" bones, ").append(rootBones.length).append(" roots\n");
+ for (Bone rootBone : rootBones) {
+ sb.append(rootBone.toString());
+ }
+ return sb.toString();
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule input = im.getCapsule(this);
+
+ Savable[] boneRootsAsSav = input.readSavableArray("rootBones", null);
+ rootBones = new Bone[boneRootsAsSav.length];
+ System.arraycopy(boneRootsAsSav, 0, rootBones, 0, boneRootsAsSav.length);
+
+ Savable[] boneListAsSavable = input.readSavableArray("boneList", null);
+ boneList = new Bone[boneListAsSavable.length];
+ System.arraycopy(boneListAsSavable, 0, boneList, 0, boneListAsSavable.length);
+
+ createSkinningMatrices();
+
+ for (Bone rootBone : rootBones) {
+ rootBone.update();
+ rootBone.setBindingPose();
+ }
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule output = ex.getCapsule(this);
+ output.write(rootBones, "rootBones", null);
+ output.write(boneList, "boneList", null);
+ }
+}